diff --git a/palm/Makefile b/palm/Makefile new file mode 100644 index 000000000..12dbc91b0 --- /dev/null +++ b/palm/Makefile @@ -0,0 +1,226 @@ +# -*- mode: Makefile; -*- +# Copyright 2002 by Eric House (fixin@peak.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. + +PLATFORM=palm +LANG=en_US +TARGET=$(PLATFORM)/xwords4.prc +ROOTNAME = Crosswords +BITMAPS = ./bmps +NAME = "Crosswords" +TYPE = appl +ICONTEXT = "Crosswords" +APPID = Xwr4 +MLPREFIX = /usr +DICT = $(shell echo $$BASENG_PATH) + +#OWNERNAME = "Eric House" +NO_REG_REQUIRED = -DNO_REG_REQUIRED + +PALM_TOOLS_PREFIX = $(shell echo $$PALM_TOOLS_PREFIX) + +ifeq (x$(PALM_TOOLS_PREFIX)x, xx) + PALM_TOOLS_PREFIX = m68k-palmos- +endif +#PALM_TOOLS_PREFIX=m68k-palmos-coff- + +CC = $(PALM_TOOLS_PREFIX)gcc +AR = $(PALM_TOOLS_PREFIX)ar +MULTILINK = $(PALM_TOOLS_PREFIX)multilink +PAR = par +PILRC = pilrc + +MULTILINK_OPTIONS = -basename $(ROOTNAME) -segmentsize 27k -g \ + -deadstrip -verbose -gdb-script app.gdb + +ifneq (x$(OWNERNAME)x,xx) +HASHDEF = -DOWNER_HASH=$$(./namehash $(OWNERNAME)) +endif + +MYDEFINES = -DXW_FEATURE_UTILS -DPOINTER_SUPPORT -DKEY_SUPPORT \ + -DOVERRIDE_EDGE_FOR_INDEX -DDIRECT_PALMOS_CALLS -DCOLOR_SUPPORT \ + -DSHOW_PROGRESS \ + $(HASHDEF) $(NO_REG_REQUIRED) + +# turn on letting users pick tiles "face-up" +# MYDEFINES += -DFEATURE_TRAY_EDIT + +MYDEFINES += -DBEYOND_IR + +ifdef XWFEATURE_STANDALONE_ONLY +MYDEFINES += -DXWFEATURE_STANDALONE_ONLY +else +MYDEFINES += -DIR_SUPPORT -DIR_EXCHMGR +endif +# -DEIGHT_TILES +# -DSUPPORT_SONY_JOGDIAL + +# HS_DUO_SUPPORT = 1 + +DEFINES += -DPLATFORM_PALM -D__BIG_ENDIAN $(MYDEFINES) + +BITMAP_RSRCS = \ + $(BITMAPS)/rightarrow.pbitm \ + $(BITMAPS)/downarrow.pbitm \ + $(BITMAPS)/flipbutton.pbitm \ + $(BITMAPS)/valuebutton.pbitm \ + $(BITMAPS)/lightbulb.pbitm \ + $(BITMAPS)/traybuttons.pbitm \ + $(BITMAPS)/showtray.pbitm \ + $(BITMAPS)/xwords4.pbitm \ + $(BITMAPS)/xwcoloricon.ppm \ + $(BITMAPS)/xwords4small.pbitm \ + +INCLUDES += -I/usr/local/share/palmdev/sdk-4.0/Extensions/ExpansionMgr + +ifneq (x$(HS_DUO_SUPPORT)x, xx) + INCLUDES += -I/usr/local/share/palmdev/duoIncs + INCLUDES += -I/usr/local/share/palmdev/duoIncs/68K + INCLUDES += -I/usr/local/share/palmdev/duoIncs/68K/System + INCLUDES += -I/usr/local/share/palmdev/duoIncs/Common/System + + MYDEFINES += -DHS_DUO_SUPPORT + + FNAVS = ./fnav03e9.bin +endif + + +CSFLAGS = -O2 -g -S -Wall -DAPPID=\'$(APPID)\' $(DEFINES) $(INCLUDES) +#CFLAGS = -O2 -g -Wall -DAPPID=\'$(APPID)\' $(DEFINES) $(INCLUDES) -palmos3.5 +CFLAGS = -O2 -g -Wall -DAPPID=\'$(APPID)\' $(DEFINES) $(INCLUDES) + + +# In the non-debug mode (for which DONT_OMIT is undefined) build without +# the frame pointer. +ifneq ($(DONT_OMIT), true) + CSFLAGS += -fomit-frame-pointer + CFLAGS += -fomit-frame-pointer +endif + +include ../common/config.mk + + +OBJS = $(PLATFORM)/palmmain.o \ + $(PLATFORM)/palmsavg.o \ + $(PLATFORM)/gameutil.o \ + $(PLATFORM)/newgame.o \ + $(PLATFORM)/palmdict.o \ + $(PLATFORM)/palmdraw.o \ + $(PLATFORM)/palmutil.o \ + $(PLATFORM)/dictui.o \ + $(PLATFORM)/dictlist.o \ + $(PLATFORM)/palmir.o \ + $(PLATFORM)/prefsdlg.o \ + $(PLATFORM)/connsdlg.o \ + $(COMMONOBJ) + +include ../common/rules.mk + +$(TARGET): $(PLATFORM)/objs.prc $(PLATFORM)/res.prc + $(PAR) -c -a 'resource|backup' $@ $(NAME) $(TYPE) $(APPID) $^ + if [ x$(shell echo $$XW_UPLOAD_DIR) != x ]; then \ + zip -j xw4.zip $@; \ + mv xw4.zip $(shell echo $$XW_UPLOAD_DIR); \ + cp $@ $(shell echo $$XW_UPLOAD_DIR); \ + fi + +solo: + $(MAKE) XWFEATURE_STANDALONE_ONLY=1 + +debug: + $(MAKE) MYDEFINES="$(MYDEFINES) -DDEBUG" DONT_OMIT=true + +memdebug: + $(MAKE) MYDEFINES="$(MYDEFINES) -DDEBUG -DMEM_DEBUG" DONT_OMIT=true + +gremlins: + $(MAKE) MYDEFINES="$(MYDEFINES) -DDEBUG -DMEM_DEBUG -DFOR_GREMLINS" DONT_OMIT=true + +# +REL=405 +REL_PATH=public_html/xwords/4.0.5 +ship-all: + make clean; make; \ + make; (cd xwconfig && make); \ + for l in fr_FR en_US es_ES es_CT sv_SE de_DE ; do \ + make clean; \ + make LANG=$$l; \ + zip -j xw$(REL)_$$l.zip xwconfig/xwconfig.prc $(TARGET); \ + done + +.S.o: + $(CC) $(TARGETFLAGS) -c $< + +.c.s: + $(CC) $(CSFLAGS) $< + +$(BITMAPS)/%.pbitm: $(BITMAPS)/%.bmp + bmtoa $< > $@ + +$(PLATFORM)/objs.prc: LocalizedStrIncludes.h $(OBJS) gdbload + @rm -f *.grc *.bin + $(MULTILINK) $(MULTILINK_OPTIONS) $(OBJS) + $(PAR) -c -a resource $(PLATFORM)/objs.prc Code rsrc rsrc *.grc + @rm -f *.grc *.bin + +$(PLATFORM)/res.prc: xwords4.rcp $(HEADERS) $(CODESEG) StrL03e8.bin $(FNAVS) + $(PILRC) $< >/dev/null + $(PAR) -c -a 'resource' $@ Rsrc rsrc rsrc *.bin + rm -f $< *.bin + +xwords4.rcp: l10n/xwords4_$(LANG).rcp.pre xwords4defines.h $(BITMAP_RSRCS) + gcc -x c -E -P $(INCLUDES) $(MYDEFINES) \ + -DICONTEXT=\"$(ICONTEXT)\" $< > $@ + +LocalizedStrIncludes.h StrL03e8.bin: ./l10n/StrRes_$(LANG).pre ./l10n/mkstrsres.c + gcc $(CFLAGS) $(FORMATDEFINES) \ + -DLANGSTRFILE=\"$<\" ./l10n/mkstrsres.c \ + -o mkstrsres + ./mkstrsres StrL03e8.bin LocalizedStrIncludes.h + rm -f mkstrsres + +namehash: namehash.c ownerhash.h +fnavgen: fnavgen.c + gcc $< -o $@ + +$(FNAVS): ./fnavgen + ./$< + +# GDB seems confused by relative paths these days. So generate the +# file rather than trying to keep in in cvs. +gdbload: + echo "source app.gdb" > $@ + echo "load-segments" >> $@ + echo "dir $(shell pwd)" >> $@ + echo "dir $(shell pwd)/../common" >> $@ + +clean: + cd ../common && $(MAKE) PLATFORM=$(PLATFORM) $@ + rm -rf $(PLATFORM)/*.[oa] xwords4 *.bin *.stamp *.[pg]rc \ + xwords4.rcp *.btxt $(PLATFORM)/* $(CODESEG) \ + LocalizedStrIncludes.h gdbload + +#cmod03E8.bin: palmdraw.c Makefile +# $(CC) -O2 -nostartfiles $(INCLUDES) $(MYDEFINES) -o tmp $< +# $(OBJRES) tmp +# mv code0001.tmp.grc $@ ; rm *.tmp.grc + +help: + @echo make memdebug OR + @echo make LANG=en_US OR + @echo make ship-all OR + @echo make gremlins OR + @echo make clean diff --git a/palm/callback.h b/palm/callback.h new file mode 100644 index 000000000..abb63805a --- /dev/null +++ b/palm/callback.h @@ -0,0 +1,21 @@ +/* copied from _Palm Programming_ p. 79*/ + +#ifndef __CALLBACK__ +#define __CALLBACK__ + +#ifdef MW_COMPILER +/* these are no-ops for MW as I understand it */ +# define CALLBACK_PROLOGUE() +# define CALLBACK_EPILOGUE() +#else + +register void *reg_a4 asm("%a4"); + +#define CALLBACK_PROLOGUE() \ + { void* __save_a4 = reg_a4; asm("move.l %%a5,%%a4; sub.l #edata,%%a4" : :); + +#define CALLBACK_EPILOGUE() reg_a4 = __save_a4;} + +#endif /* MW_COMPILER */ + +#endif diff --git a/palm/connsdlg.c b/palm/connsdlg.c new file mode 100644 index 000000000..66e93e620 --- /dev/null +++ b/palm/connsdlg.c @@ -0,0 +1,320 @@ +/* -*-mode: C; fill-column: 77; c-basic-offset: 4; -*- */ +/* + * Copyright 2003 by Eric House (fixin@peak.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. + */ + +#ifdef BEYOND_IR + +#include + +#include "callback.h" + +#include "connsdlg.h" +#include "palmmain.h" +#include "palmutil.h" +#include "palmir.h" + +/* When user pops up via Host gadget, we want to get the port to listen on. + * When pops up via the Guest gadget, we want to get the port and IP address + * of the host AS WELL AS the local port that we'll tell it we're listening + * on. We need local state for both, since user can switch between them and + * expect state to live as long as the parent dialog isn't exited. + */ + +typedef struct ConnsDlgState { + ListPtr connTypesList; + XP_U16 serverRole; + XP_Bool isNewGame; + ConnDlgAddrs* addrState; + XP_UCHAR localIPStr[16]; +} ConnsDlgState; + +static void +fieldFromStr( XP_U16 id, XP_UCHAR* buf, XP_Bool editable ) +{ + FieldPtr field = getActiveObjectPtr( id ); + UInt16 len = FldGetTextLength( field ); + FldSetSelection( field, 0, len ); + FldInsert( field, buf, XP_STRLEN(buf) ); + setFieldEditable( field, editable ); +} /* fieldFromStr */ + +static void +strFromField( XP_U16 id, XP_UCHAR* buf, XP_U16 max ) +{ + FieldPtr field = getActiveObjectPtr( id ); + XP_UCHAR* str = FldGetTextPtr( field ); + XP_U16 len = FldGetTextLength( field ); + if ( len > max-1 ) { + len = max - 1; + } + XP_MEMCPY( buf, str, len ); + buf[len] = '\0'; +} /* strFromField */ + +#if 0 +static void +stateFromGlobals( PalmAppGlobals* globals, ConnsDlgState* state ) +{ + comms_getAddr( globals->game.comms, &state->targetAddr, + &state->myPort ); + + state->isNewGame = globals->isNewGame; +} /* stateFromGlobals */ + +static void +globalsFromState( PalmAppGlobals* globals, ConnsDlgState* state ) +{ + comms_setAddr( globals->game.comms, &state->targetAddr, + state->myPort ); +} /* globalsFromState */ +#endif + +static void +ctlsFromState( PalmAppGlobals* globals, FormPtr form, ConnsDlgState* state ) +{ + XP_Bool isNewGame = state->isNewGame; + XP_UCHAR buf[16]; + ConnDlgAddrs* addrState = state->addrState; + + NetLibAddrINToA( globals->nlStuff.netLibRef, + addrState->remoteIP, buf ); + fieldFromStr( XW_CONNS_TARGET_FIELD_ID, buf, isNewGame ); + + StrPrintF( buf, "%d", addrState->remotePort ); + fieldFromStr( XW_CONNS_TPORT_FIELD_ID, buf, isNewGame ); + + StrPrintF( buf, "%d", addrState->localPort ); + fieldFromStr( XW_CONNS_MYPORT_FIELD_ID, buf, isNewGame ); + + fieldFromStr( XW_CONNS_HOSTIP_FIELD_ID, state->localIPStr, XP_FALSE ); +} /* ctlsFromState */ + +static XP_Bool +stateFromCtls( PalmAppGlobals* globals, ConnsDlgState* state ) +{ + XP_Bool ok = XP_TRUE; + XP_UCHAR buf[16]; + XP_U32 tmpAddr; + ConnDlgAddrs* addrState = state->addrState; + XP_Bool rejectBadIP = addrState->conType == COMMS_CONN_IP; + + strFromField( XW_CONNS_TARGET_FIELD_ID, buf, sizeof(buf) ); + tmpAddr = NetLibAddrAToIN( globals->nlStuff.netLibRef, buf ); + if ( tmpAddr != -1L ) { + addrState->remoteIP = tmpAddr; + } else if ( rejectBadIP ) { + NetLibAddrINToA( globals->nlStuff.netLibRef, addrState->remoteIP, + buf ); + fieldFromStr( XW_CONNS_TARGET_FIELD_ID, buf, state->isNewGame ); + ok = XP_FALSE; + } + + strFromField( XW_CONNS_TPORT_FIELD_ID, buf, sizeof(buf) ); + addrState->remotePort = StrAToI( buf ); + + strFromField( XW_CONNS_MYPORT_FIELD_ID, buf, sizeof(buf) ); + addrState->localPort = StrAToI( buf ); + + return ok; +} /* stateFromCtls */ + +/* Adjust the set of visible form controls based on state. There are two + * variables here: whether we're showing for a Host or a Guest, and whether + * the connection method is IR or IP. Currently IR means no controls needed, + * either way. IP means on set if launched from host button, another if + * launched from Guest. + */ +static void +updateFormCtls( FormPtr form, ConnsDlgState* state ) +{ + const XP_U16 ipCtlsBoth[] = { + XW_CONNS_MYPORT_LABEL_ID, + XW_CONNS_MYPORT_FIELD_ID, + XW_CONNS_HOSTIP_LABEL_ID, + XW_CONNS_HOSTIP_FIELD_ID, + 0 + }; + const XP_U16 ipCtlsGuest[] = { + XW_CONNS_TARGET_LABEL_ID, + XW_CONNS_TARGET_FIELD_ID, + XW_CONNS_TPORT_LABEL_ID, + XW_CONNS_TPORT_FIELD_ID, + 0 + }; + + if ( state->addrState->conType == COMMS_CONN_IR ) { + disOrEnableSet( form, ipCtlsBoth, XP_FALSE ); + disOrEnableSet( form, ipCtlsGuest, XP_FALSE ); + } else { + disOrEnableSet( form, ipCtlsBoth, XP_TRUE ); + disOrEnableSet( form, ipCtlsGuest, + state->serverRole == SERVER_ISCLIENT ); + setFieldEditable( getActiveObjectPtr(XW_CONNS_HOSTIP_FIELD_ID), + XP_FALSE ); + } + +} /* updateFormCtls */ + +static void +cleanupExit( PalmAppGlobals* globals ) +{ + XP_FREE( globals->mpool, globals->connState ); + globals->connState = NULL; + FrmReturnToForm( 0 ); +} /* cleanupExit */ + +static XP_U32 +figureLocalIP( PalmAppGlobals* globals, XP_UCHAR* buf ) +{ + Err err; + XP_U32 ipAddr = 0L; + XP_U16 netLibRef = globals->nlStuff.netLibRef; + XP_U16 index; + + for ( index = 0; ; ++index ) { + UInt32 creator; + UInt16 instance; + err = NetLibIFGet( netLibRef, index, + &creator, &instance ); + + /* Docs say to iterate until get netErrInvalidInterface, but I'm + never getting that, getting netErrInterfaceNotFound instead */ + if ( (err == netErrInvalidInterface) || + (err == netErrInterfaceNotFound) ) { + break; /* we're done */ + } else if ( err == errNone ) { + XP_U8 up; + UInt16 siz = sizeof(up); + err = NetLibIFSettingGet( netLibRef, creator, instance, + netIFSettingUp, &up, &siz ); + if ( (err == errNone) && (up != 0) ) { + + siz = sizeof(ipAddr); + /* use this interface?? */ + err = NetLibIFSettingGet( netLibRef, creator, instance, + netIFSettingReqIPAddr, + &ipAddr, &siz ); + XP_ASSERT( siz == 4 ); + } + } + } + + if ( !!buf ) { + NetLibAddrINToA( globals->nlStuff.netLibRef, ipAddr, buf ); + XP_LOGF( "got local addr: %s", buf ); + } + return ipAddr; +} /* figureLocalIP */ + +Boolean +ConnsFormHandleEvent( EventPtr event ) +{ + Boolean result; + PalmAppGlobals* globals; + ConnsDlgState* state; + FormPtr form; + XP_S16 chosen; + + CALLBACK_PROLOGUE(); + + globals = getFormRefcon(); + state = globals->connState; + if ( !state ) { + state = globals->connState = XP_MALLOC( globals->mpool, + sizeof(*state) ); + XP_MEMSET( state, 0, sizeof(*state) ); + } + + form = FrmGetActiveForm(); + + switch ( event->eType ) { + case frmOpenEvent: + + if ( !openNetLibIfNot( globals ) ) { + beep(); + cleanupExit( globals ); + result = true; + break; + } + + state->serverRole = + (Connectedness)globals->dlgParams[CONNS_PARAM_ROLE_INDEX]; + state->addrState = + (ConnDlgAddrs*)globals->dlgParams[CONNS_PARAM_ADDR_INDEX]; + state->isNewGame = globals->isNewGame; + (void)figureLocalIP( globals, state->localIPStr ); + + /* setup connection popup */ + state->connTypesList = getActiveObjectPtr( XW_CONNS_TYPE_LIST_ID ); + XP_ASSERT( state->addrState->conType == COMMS_CONN_IR + || state->addrState->conType == COMMS_CONN_IP ); + setSelectorFromList( XW_CONNS_TYPE_TRIGGER_ID, state->connTypesList, + state->addrState->conType == COMMS_CONN_IR? 0:1 ); + + ctlsFromState( globals, form, state ); + + updateFormCtls( form, state ); + + case frmUpdateEvent: + FrmDrawForm( form ); + result = true; + break; + + case ctlSelectEvent: + result = true; + switch ( event->data.ctlSelect.controlID ) { + + case XW_CONNS_TYPE_TRIGGER_ID: + if ( state->isNewGame ) { + chosen = LstPopupList( state->connTypesList ); + if ( chosen >= 0 ) { + setSelectorFromList( XW_CONNS_TYPE_TRIGGER_ID, + state->connTypesList, chosen ); + state->addrState->conType = + chosen == 0? COMMS_CONN_IR : COMMS_CONN_IP; + updateFormCtls( form, state ); + } + } + break; + + case XW_CONNS_OK_BUTTON_ID: + if ( !state->isNewGame ) { + /* do nothing; same as cancel */ + } else if ( !stateFromCtls( globals, state ) ) { + beep(); + break; + } else { + EventType eventToPost; + eventToPost.eType = connsSettingChgEvent; + EvtAddEventToQueue( &eventToPost ); + } + + case XW_CONNS_CANCEL_BUTTON_ID: + cleanupExit( globals ); + break; + } + break; + default: + result = false; + } + + CALLBACK_EPILOGUE(); + return result; +} /* ConnsFormHandleEvent */ + +#endif /* BEYOND_IR */ diff --git a/palm/connsdlg.h b/palm/connsdlg.h new file mode 100644 index 000000000..c379eef80 --- /dev/null +++ b/palm/connsdlg.h @@ -0,0 +1,37 @@ +/* -*-mode: C; fill-column: 77; c-basic-offset: 4; -*- */ +/* + * Copyright 2003 by Eric House (fixin@peak.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. + */ + +#ifndef _CONNSDLG_H_ +#define _CONNSDLG_H_ + +#include + + +Boolean ConnsFormHandleEvent( EventPtr event ); + +#define CONNS_PARAM_ROLE_INDEX 0 +#define CONNS_PARAM_ADDR_INDEX 1 + +#define PopupConnsForm( g, h, addrP ) { \ + (g)->dlgParams[CONNS_PARAM_ROLE_INDEX] = (XP_U32)(h); \ + (g)->dlgParams[CONNS_PARAM_ADDR_INDEX] = (XP_U32)(addrP); \ + FrmPopupForm( XW_CONNS_FORM ); \ +} + +#endif diff --git a/palm/dictlist.c b/palm/dictlist.c new file mode 100644 index 000000000..6eb7793a2 --- /dev/null +++ b/palm/dictlist.c @@ -0,0 +1,346 @@ +/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */ +/**************************************************************************** + * * + * Copyright 1999 - 2003 by Eric House (fixin@peak.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 "callback.h" +#include "dictlist.h" +#include "palmmain.h" +#include "palmutil.h" +#include "palmdict.h" +#include "strutils.h" +#include "xwords4defines.h" + +#define TYPE_DAWG 'DAWG' +#define TYPE_XWR3 'Xwr3' + +////////////////////////////////////////////////////////////////////////////// +// typedef and #defines +////////////////////////////////////////////////////////////////////////////// + +struct PalmDictList { + XP_U16 nDicts; + DictListEntry dictArray[1]; +}; + +////////////////////////////////////////////////////////////////////////////// +// Prototypes +////////////////////////////////////////////////////////////////////////////// +XP_Bool +getNthDict( const PalmDictList* dl, short n, DictListEntry** dle ) +{ + XP_Bool exists = !!dl && (dl->nDicts > n); + if ( exists ) { + *dle = (DictListEntry*)&dl->dictArray[n]; + } + return exists; +} /* getNthDict */ + +XP_Bool +getDictWithName( const PalmDictList* dl, XP_UCHAR* name, + DictListEntry** dlep ) +{ + XP_Bool result = XP_FALSE; + + if ( !!dl ) { + XP_U16 i; + XP_UCHAR* extName; + XP_UCHAR oldChName = '\0'; /* shut compiler up */ + DictListEntry* dle = (DictListEntry*)dl->dictArray; + + extName = (XP_UCHAR*)StrStr((const char*)name, + (const char*)".pdb" ); + + if ( !!extName ) { + oldChName = *extName; + *extName = '\0'; + } + + for ( i = 0; !result && i < dl->nDicts; ++i ) { + XP_UCHAR* extCand; + XP_UCHAR oldChCand = '\0'; + + extCand = (XP_UCHAR*)StrStr((const char*)dle->baseName, ".pdb" ); + if ( !!extCand ) { + oldChCand = *extCand; + *extCand = '\0'; + } + + if ( 0 == XP_STRCMP( (const char*)name, + (const char*)dle->baseName ) ) { + + *dlep = dle; + result = XP_TRUE; + } + + if ( !!extCand ) { + *extCand = oldChCand; + } + + ++dle; + } + + if ( !!extName ) { + *extName = oldChName; + } + + } + return result; +} /* getDictWithName */ + +void +cacheDictForName( PalmDictList* dl, XP_UCHAR* dictName, + DictionaryCtxt* dict ) +{ + DictListEntry* dle; + (void)getDictWithName(dl, dictName, &dle ); + XP_ASSERT( getDictWithName(dl, dictName, &dle ) ); + XP_ASSERT( !dle->dict ); + + dle->dict = dict; +} /* cacheDictForName */ + +void +removeFromDictCache( PalmDictList* dl, XP_UCHAR* dictName, + DictionaryCtxt* dict ) +{ + DictListEntry* dle; + (void)getDictWithName( dl, dictName, &dle ); + XP_ASSERT( getDictWithName( dl, dictName, &dle ) ); + XP_ASSERT( !!dle->dict ); + XP_ASSERT( dle->dict == dict ); + + dle->dict = NULL; +} /* removeFromDictCache */ + +static void +addEntry( MPFORMAL PalmDictList** dlp, DictListEntry* dle ) +{ + PalmDictList* dl = *dlp; + DictListEntry* ignore; + + if ( !dl ) { + dl = (PalmDictList*)XP_MALLOC( mpool, sizeof(*dl) ); + XP_MEMSET( dl, 0, sizeof(*dl) ); + } + + if ( !getDictWithName( dl, dle->baseName, &ignore ) ) { + XP_U16 size = sizeof(*dl); + size += dl->nDicts * sizeof( dl->dictArray[0] ); + + dl = (PalmDictList*)XP_REALLOC( mpool, (XP_U8*)dl, size ); + + dle->dict = NULL; + XP_MEMCPY( &dl->dictArray[dl->nDicts++], dle, + sizeof( dl->dictArray[0] ) ); + } + + *dlp = dl; +} /* addEntry */ + +static void +searchDir( MPFORMAL PalmDictList** dlp, UInt16 volNum, unsigned char separator, + unsigned char* path, XP_U16 pathSize ) +{ + Err err; + FileRef dirRef; + XP_U16 pathLen = XP_STRLEN( (const char*)path ); + + err = VFSFileOpen( volNum, (const char*)path, vfsModeRead, &dirRef ); + if ( err == errNone ) { + UInt32 dEnum = vfsIteratorStart; + FileInfoType fit; + + fit.nameP = (char*)path + pathLen; + + while ( dEnum != vfsIteratorStop ) { + XP_UCHAR* ext; + fit.nameBufLen = pathSize - pathLen; + err = VFSDirEntryEnumerate( dirRef, &dEnum, &fit ); + + if ( err != errNone ) { + break; + } + + if ( (fit.attributes & vfsFileAttrDirectory) != 0 ) { + XP_U16 len = XP_STRLEN((const char*)path); + path[len] = separator; + path[len+1] = '\0'; + searchDir( MPPARM(mpool) dlp, volNum, separator, + path, pathSize ); + } else if ( (ext = (XP_UCHAR*)StrStr( (const char*)path, ".pdb" )) + != NULL ) { + + /* find out if it's a crosswords dict. */ + FileRef fileRef; + UInt32 type, creator; + + err = VFSFileOpen( volNum, (const char*)path, vfsModeRead, + &fileRef ); + if ( err == errNone ) { + err = VFSFileDBInfo( fileRef, NULL, /* name */ + NULL, /* attributes */ + NULL, /* versionP */ + NULL, /* crDateP */ + NULL, NULL, /*UInt32 *modDateP, UInt32 *bckUpDateP,*/ + NULL, NULL, /*UInt32 *modNumP, MemHandle *appInfoHP,*/ + NULL, /*MemHandle *sortInfoHP, */ + &type, &creator, + NULL ); /* nRecords */ + VFSFileClose( fileRef ); + + if ( (err == errNone) && (type == TYPE_DAWG) && + (creator == TYPE_XWR3) ) { + DictListEntry dl; + + dl.path = copyString( MPPARM(mpool) path ); + dl.location = DL_VFS; + dl.u.vfsData.volNum = volNum; + dl.baseName = dl.path + pathLen; + + addEntry( MPPARM(mpool) dlp, &dl ); + } + } + } + } + + path[pathLen] = '\0'; + VFSFileClose( dirRef ); + } + +} /* searchDir */ + +static void +tryVFSSearch( MPFORMAL PalmDictList** dlp ) +{ + Err err; + UInt16 volNum; + UInt32 vEnum; + + vEnum = vfsIteratorStart; + while ( vEnum != vfsIteratorStop ) { + unsigned char pathStr[265]; + UInt16 bufLen; + + err = VFSVolumeEnumerate( &volNum, &vEnum ); + if ( err != errNone ) { + break; + } + + bufLen = sizeof(pathStr); + err = VFSGetDefaultDirectory( volNum, ".pdb", (char*)pathStr, + &bufLen ); + + if ( err == errNone ) { + pathStr[1] = '\0'; + searchDir( MPPARM(mpool) dlp, volNum, pathStr[0], + pathStr, sizeof(pathStr) ); + } + } + +} /* tryVFSSearch */ + +/* if we've allocated extra space in the array as an optimization now's when + * we pull back */ +static void +cleanList( PalmDictList** dl ) +{ +} /* cleanList */ + +PalmDictList* +DictListMake( MPFORMAL_NOCOMMA ) +{ + Err err; + DmSearchStateType stateType; + UInt32 vers; + PalmDictList* dl = NULL; + UInt16 cardNo; + LocalID dbID; + Boolean newSearch = true; + XP_Bool found = false; + + /* first the DM case */ + while ( !found ) { + err = DmGetNextDatabaseByTypeCreator( newSearch, &stateType, TYPE_DAWG, + TYPE_XWR3/* APPID */, + false,// onlyLatestVers, + &cardNo, &dbID ); + if ( err != 0 ) { + break; + } else { + XP_UCHAR nameBuf[33]; + XP_U16 nameLen; + DictListEntry dle; + + err = DmDatabaseInfo( cardNo, dbID, (char*)nameBuf, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL ); + nameLen = XP_STRLEN( (const char*)nameBuf ) + 1; + + dle.location = DL_STORAGE; + dle.u.dmData.cardNo = cardNo; + dle.u.dmData.dbID = dbID; + dle.path = dle.baseName = XP_MALLOC( mpool, nameLen ); + XP_MEMCPY( dle.path, nameBuf, nameLen ); + + addEntry( MPPARM(mpool) &dl, &dle ); + } + + newSearch = false; + } + + /* then the VFS case */ + err = FtrGet( sysFileCVFSMgr, vfsFtrIDVersion, &vers ); + if ( err == errNone ) { + tryVFSSearch( MPPARM(mpool) &dl ); + } + + cleanList( &dl ); + + return dl; +} /* DictListMake */ + +void +DictListFree( MPFORMAL PalmDictList* dl ) +{ + if ( !!dl ) { + DictListEntry* dle = dl->dictArray; + XP_U16 i; + + for ( i = 0; i < dl->nDicts; ++i, ++dle ) { + XP_FREE( mpool, dle->path ); + } + + XP_FREE( mpool, dl ); + } +} /* dictListFree */ + +XP_U16 +DictListCount( PalmDictList* dl ) +{ + if ( !dl ) { + return 0; + } else { + return dl->nDicts; + } +} /* dictListCount */ diff --git a/palm/dictlist.h b/palm/dictlist.h new file mode 100644 index 000000000..65120573d --- /dev/null +++ b/palm/dictlist.h @@ -0,0 +1,56 @@ +/* -*-mode: C; fill-column: 77; c-basic-offset: 4; -*- */ +/* + * Copyright 1999 - 2001 by Eric House (fixin@peak.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. + */ +#ifndef _DICTLIST_H_ +#define _DICTLIST_H_ + +#include "palmmain.h" +#include "palmdict.h" + +enum { DL_STORAGE, DL_VFS }; + +typedef struct DictListEntry { + XP_UCHAR* path; + XP_UCHAR* baseName; /* points into, or ==, path */ + DictionaryCtxt* dict; /* cache so can refcount */ + XP_UCHAR location; /* Storage RAM or VFS */ + union { + struct { + UInt16 cardNo; + LocalID dbID; + } dmData; + struct { + UInt16 volNum; + } vfsData; + } u; +} DictListEntry; + +PalmDictList* DictListMake( MPFORMAL_NOCOMMA ); +void DictListFree( MPFORMAL PalmDictList* dl ); +XP_U16 DictListCount( PalmDictList* dl ); + +XP_Bool getDictWithName( const PalmDictList* dl, unsigned char* name, + DictListEntry** dle ); +void cacheDictForName( PalmDictList* dl, XP_UCHAR* dictName, + DictionaryCtxt* ctxt ); +void removeFromDictCache( PalmDictList* dl, XP_UCHAR* dictName, + DictionaryCtxt* dict ); + +XP_Bool getNthDict( const PalmDictList* dl, short n, DictListEntry** dle ); + +#endif diff --git a/palm/dictui.c b/palm/dictui.c new file mode 100644 index 000000000..72df51763 --- /dev/null +++ b/palm/dictui.c @@ -0,0 +1,286 @@ +/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */ +/**************************************************************************** + * * + * Copyright 1999 - 2003 by Eric House (fixin@peak.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 "callback.h" +#include "dictui.h" +#include "palmmain.h" +#include "palmutil.h" +#include "palmdict.h" +#include "dictlist.h" +#include "strutils.h" +#include "xwords4defines.h" + +#define TYPE_DAWG 'DAWG' +#define TYPE_XWR3 'Xwr3' + +////////////////////////////////////////////////////////////////////////////// +// Prototypes +////////////////////////////////////////////////////////////////////////////// +static XP_U16 populateDictionaryList( MPFORMAL ListData* sLd, + XP_UCHAR* curDictName, + ListPtr list, Int16 triggerID, + PalmDictList* dl ); +static Boolean beamDict( const PalmDictList* dl, XP_UCHAR* dictName ); + +/***************************************************************************** + * Handler for dictionary info form. + ****************************************************************************/ +#define USE_POPULATE 1 +Boolean +dictFormHandleEvent( EventPtr event ) +{ + FormPtr form; + Boolean result; + Int16 chosen; + DictionaryCtxt* dict; + PalmAppGlobals* globals; + XP_UCHAR* dictName; + + CALLBACK_PROLOGUE(); + + result = false; + globals = getFormRefcon(); + + switch ( event->eType ) { + case frmOpenEvent: { + XP_UCHAR* curName; + XP_U16 width; + RectangleType rect; + + form = FrmGetActiveForm(); + + /* we're either a beam dlg or a dict picker; disable a button here. */ + disOrEnable( form, + globals->dictuiForBeaming ? + XW_DICTINFO_DONE_BUTTON_ID:XW_DICTINFO_BEAM_BUTTON_ID, + false ); + + /* dictionary list setup */ + globals->dictState.dictList = + getActiveObjectPtr( XW_DICTINFO_LIST_ID ); + + dict = !!globals->game.model? + model_getDictionary(globals->game.model) : NULL; + if ( dict ) { + curName = dict_getName( dict ); + } else { + curName = NULL; + } + width = populateDictionaryList( MPPARM(globals->mpool) + &globals->dictState.sLd, + curName, globals->dictState.dictList, + XW_DICTINFO_TRIGGER_ID, + globals->dictList ); + getObjectBounds( XW_DICTINFO_LIST_ID, &rect ); + rect.extent.x = width; + setObjectBounds( XW_DICTINFO_LIST_ID, &rect ); + + FrmDrawForm( form ); + break; + } + + case ctlSelectEvent: + switch ( event->data.ctlEnter.controlID ) { + + case XW_DICTINFO_TRIGGER_ID: + // don't let change dict except first time + if ( globals->dictuiForBeaming || globals->isNewGame ) { + chosen = LstPopupList( globals->dictState.dictList ); + if ( chosen >= 0 ) { + setSelectorFromList( XW_DICTINFO_TRIGGER_ID, + globals->dictState.dictList, + chosen ); + } + } + result = true; + break; + /* case XW_PHONIES_TRIGGER_ID: */ + /* chosen = LstPopupList( sPhoniesList ); */ + /* if ( chosen >= 0 ) { */ + /* setTriggerFromList( XW_PHONIES_TRIGGER_ID, sPhoniesList, */ + /* chosen ); */ + /* } */ + /* result = true; */ + /* break; */ + + case XW_DICTINFO_DONE_BUTTON_ID: + case XW_DICTINFO_BEAM_BUTTON_ID: + /* discard the const */ + dictName = (XP_UCHAR*)CtlGetLabel( + getActiveObjectPtr( XW_DICTINFO_TRIGGER_ID) ); + + if ( globals->dictuiForBeaming ) { + if ( !beamDict( globals->dictList, dictName ) ) { + break; /* don't cancel dialog yet */ + } + } else { + EventType eventToPost; + + XP_ASSERT( dictName != NULL ); + + eventToPost.eType = dictSelectedEvent; + ((DictSelectedData*)&eventToPost.data.generic)->dictName + = copyString( MEMPOOL dictName ); + EvtAddEventToQueue( &eventToPost ); + } + + case XW_DICTINFO_CANCEL_BUTTON_ID: + result = true; + freeListData( MPPARM(globals->mpool) &globals->dictState.sLd ); + FrmReturnToForm( 0 ); + break; + + } // switch ( event->data.ctlEnter.controlID ) + break; + + default: + break; + } // switch + + CALLBACK_EPILOGUE(); + return result; +} /* dictFormHandleEvent */ + +/***************************************************************************** + * + ****************************************************************************/ +static XP_U16 +populateDictionaryList( MPFORMAL ListData* sLd, XP_UCHAR* curDictName, + ListPtr list, Int16 triggerID, PalmDictList* dl ) +{ + XP_U16 i; + XP_U16 maxWidth = 0; + XP_U16 nDicts; + + initListData( MPPARM(mpool) sLd, 16 ); /* PENDING: MAX_DICTS or count */ + nDicts = DictListCount( dl ); + + for ( i = 0; i < nDicts; ++i ) { + DictListEntry* dle; + XP_UCHAR* name; + XP_U16 width; + + getNthDict( dl, i, &dle ); + name = dle->baseName; + + addListTextItem( MPPARM(mpool) sLd, name ); + width = FntCharsWidth( (const char*)name, XP_STRLEN((const char*)name) ); + if ( width > maxWidth ) { + maxWidth = width; + } + } + + sortList( sLd ); + setListSelection( sLd, (char*)curDictName ); + setListChoices( sLd, list, NULL ); + + setSelectorFromList( triggerID, list, LstGetSelection(list) ); + + return maxWidth + 3; /* 3: for white space */ +} /* populateDictionaryList */ + +/*********************************************************************** + * The rest of this file mostly stolen from palmos.com. I've only modified + * beamDict + ************************************************************************/ +static Err +WriteDBData(const void* dataP, UInt32* sizeP, void* userDataP) +{ + Err err; + + /* Try to send as many bytes as were requested by the caller */ + *sizeP = ExgSend((ExgSocketPtr)userDataP, (void*)dataP, *sizeP, &err); + return err; +} /* WriteDBData */ + +Err +sendDatabase( UInt16 cardNo, LocalID dbID, XP_UCHAR* nameP, + XP_UCHAR* descriptionP ) +{ + ExgSocketType exgSocket; + Err err; + + /* Create exgSocket structure */ + XP_MEMSET( &exgSocket, 0, sizeof(exgSocket) ); + exgSocket.description = (char*)descriptionP; + exgSocket.name = (char*)nameP; + + /* Start an exchange put operation */ + err = ExgPut(&exgSocket); + if ( !err ) { + err = ExgDBWrite( WriteDBData, &exgSocket, NULL, dbID, cardNo ); + /* Disconnect Exg and pass error */ + err = ExgDisconnect(&exgSocket, err); + } + return err; +} /* sendDatabase */ + +static Boolean +beamDict( const PalmDictList* dl, XP_UCHAR* dictName ) +{ + Err err; + UInt16 cardNo; + LocalID dbID; + Boolean found; + XP_Bool shouldDispose = XP_FALSE; + DictListEntry* dle; + + found = getDictWithName( dl, dictName, &dle ); + + /* Find our app using its internal name */ + XP_ASSERT( found ); + + if ( found ) { + if ( dle->location == DL_VFS ) { + err = VFSImportDatabaseFromFile( dle->u.vfsData.volNum, + (const char*)dle->path, + &cardNo, &dbID ); + if ( err == dmErrAlreadyExists ) { + } else if ( err == errNone ) { + shouldDispose = XP_TRUE; + } else { + found = XP_FALSE; + } + } else { + cardNo = dle->u.dmData.cardNo; + dbID = dle->u.dmData.dbID; + } + } + + if ( found ) { /* send it giving external name and description */ + XP_UCHAR prcName[40]; + XP_SNPRINTF( prcName, sizeof(prcName), (XP_UCHAR*)"%s.pdb", dictName ); + err = sendDatabase( cardNo, dbID, prcName, dictName ); + found = err == 0; + + if ( shouldDispose ) { + DmDeleteDatabase( cardNo, dbID ); + } + } + + return found; +} /* beamDict */ + diff --git a/palm/dictui.h b/palm/dictui.h new file mode 100644 index 000000000..f5817d16b --- /dev/null +++ b/palm/dictui.h @@ -0,0 +1,34 @@ +/* -*-mode: C; fill-column: 77; c-basic-offset: 4; -*- */ +/* + * Copyright 1999 - 2001 by Eric House (fixin@peak.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. + */ +#ifndef _DICTUI_H_ +#define _DICTUI_H_ + +#include "palmmain.h" +#include "palmdict.h" + +typedef struct DictSelectedData { + MemPtr dictName; +} DictSelectedData; + +Boolean dictFormHandleEvent( EventPtr event ); + +/* export for beamBoard */ +Err sendDatabase( UInt16 cardNo, LocalID dbID, XP_UCHAR* nameP, + XP_UCHAR* descriptionP ); +#endif diff --git a/palm/fnavgen.c b/palm/fnavgen.c new file mode 100644 index 000000000..899291027 --- /dev/null +++ b/palm/fnavgen.c @@ -0,0 +1,186 @@ +/* -*- mode: c; -*- */ + +#include +#include + +#include "xwords4defines.h" + +typedef struct fnavElem { + unsigned short objectID; + unsigned short objectFlags; + unsigned short objectAbove; + unsigned short objectBelow; +} fnavElem; + +typedef struct fnavHeader { + unsigned short formID; /* not part of resource!!!! */ + + unsigned short version; /* always 1 */ + unsigned short objcount; + unsigned short headerSizeInBytes; /* always 20 */ + unsigned short listSizeInBytes; /* always 8 */ + unsigned short navflags; + unsigned short initialHint; + unsigned short jumpToHint; + unsigned short bottomLeftHint; +} fnavHeader; + + +fnavHeader gamesHeader = { + XW_NEWGAMES_FORM, + 0, /* fill this in */ + 0, /* fill this in */ + 0, /* fill this in */ + 0, /* fill this in */ + 0x0001, /* force obj focus mode */ + 0, + 0, + 0, +}; + +fnavElem gamesElems[] = { + { XW_ROBOT_1_CHECKBOX_ID, 0, 0, XW_ROBOT_2_CHECKBOX_ID }, + { XW_ROBOT_2_CHECKBOX_ID, 0, XW_ROBOT_1_CHECKBOX_ID, XW_ROBOT_3_CHECKBOX_ID }, + { XW_ROBOT_3_CHECKBOX_ID, 0, XW_ROBOT_2_CHECKBOX_ID, XW_ROBOT_4_CHECKBOX_ID }, + { XW_ROBOT_4_CHECKBOX_ID, 0, XW_ROBOT_3_CHECKBOX_ID, XW_SOLO_GADGET_ID }, + + { XW_SOLO_GADGET_ID, 0, XW_ROBOT_4_CHECKBOX_ID, XW_SERVER_GADGET_ID }, + { XW_SERVER_GADGET_ID, 0, XW_SOLO_GADGET_ID, XW_CLIENT_GADGET_ID }, + { XW_CLIENT_GADGET_ID, 0, XW_SERVER_GADGET_ID, 0 } +}; + +unsigned short gamesElemsShort[] = { + +#ifndef XWFEATURE_STANDALONE_ONLY + XW_SOLO_GADGET_ID, + XW_SERVER_GADGET_ID, + XW_CLIENT_GADGET_ID, +#endif + + XW_NPLAYERS_SELECTOR_ID, + XW_PREFS_BUTTON_ID, + + XW_REMOTE_1_CHECKBOX_ID, + XW_PLAYERNAME_1_FIELD_ID, + XW_ROBOT_1_CHECKBOX_ID, + XW_PLAYERPASSWD_1_TRIGGER_ID, + + XW_REMOTE_2_CHECKBOX_ID, + XW_PLAYERNAME_2_FIELD_ID, + XW_ROBOT_2_CHECKBOX_ID, + XW_PLAYERPASSWD_2_TRIGGER_ID, + + XW_REMOTE_3_CHECKBOX_ID, + XW_PLAYERNAME_3_FIELD_ID, + XW_ROBOT_3_CHECKBOX_ID, + XW_PLAYERPASSWD_3_TRIGGER_ID, + + XW_REMOTE_4_CHECKBOX_ID, + XW_PLAYERNAME_4_FIELD_ID, + XW_ROBOT_4_CHECKBOX_ID, + XW_PLAYERPASSWD_4_TRIGGER_ID, + + XW_DICT_SELECTOR_ID, + XW_CANCEL_BUTTON_ID, + XW_OK_BUTTON_ID + +}; + +static void +usage( char* name ) +{ + fprintf( stderr, "usage: %s outfile\n", name ); + exit( 1 ); +} /* */ + +static void +write_network_short( FILE* fil, unsigned short s ) +{ + unsigned short tmp = htons( s ); + fwrite( &tmp, sizeof(tmp), 1, fil ); +} /* write_network_short */ + +write_fnav( fnavHeader* header, unsigned short* idArray, + fnavElem* elems, int count ) +{ + char nameBuf[32]; + FILE* fil; + int i; + + assert( !idArray || !elems ); + + sprintf( nameBuf, "fnav%.4x.bin", header->formID ); + fil = fopen( nameBuf, "w" ); + fprintf( stderr, "created file %s\n", nameBuf ); + + write_network_short( fil, 1 ); + write_network_short( fil, count ); + write_network_short( fil, 20 ); + write_network_short( fil, 8 ); + write_network_short( fil, header->navflags ); + write_network_short( fil, header->initialHint ); + write_network_short( fil, header->jumpToHint ); + write_network_short( fil, header->bottomLeftHint ); + + /* Two words of padding. Docs disagree, but Blazer's resources have + 'em */ + write_network_short( fil, 0 ); + write_network_short( fil, 0 ); + + if ( !!elems ) { + + for ( i = 0; i < count; ++i ) { + write_network_short( fil, elems->objectID ); + write_network_short( fil, elems->objectFlags ); + write_network_short( fil, elems->objectAbove ); + write_network_short( fil, elems->objectBelow ); + + ++elems; + } + + } else { + unsigned short prevID = 0; + unsigned short id = *idArray++; + + while ( count-- ) { + unsigned short nextID; + + if ( count == 0 ) { + nextID = 0; + } else { + nextID = *idArray++; + } + + write_network_short( fil, id ); + write_network_short( fil, 0 ); + write_network_short( fil, prevID ); + write_network_short( fil, nextID ); + + if ( !nextID ) { + break; + } + + prevID = id; + id = nextID; + } + } + + fclose(fil); +} /* write_fnav */ + +int +main( int argc, char** argv ) +{ + int i; + char* outFName; + + if ( argc != 1 ) { + usage( argv[0] ); + } + + write_fnav( &gamesHeader, gamesElemsShort, NULL, + sizeof(gamesElemsShort)/sizeof(gamesElemsShort[0]) ); +/* sizeof(gamesElems)/sizeof(gamesElems[0]) ); */ + + +} /* main */ diff --git a/palm/gameutil.c b/palm/gameutil.c new file mode 100644 index 000000000..57d9619ff --- /dev/null +++ b/palm/gameutil.c @@ -0,0 +1,208 @@ +/* -*-mode: C; fill-column: 77; c-basic-offset: 4; -*- */ +/* + * Copyright 1999 - 2001 by Eric House (fixin@peak.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 "comtypes.h" +#include "comms.h" +#include "strutils.h" +#include "gameutil.h" +#include "xwords4defines.h" +#include "xwstream.h" +#include "palmmain.h" + +XP_U16 +countGameRecords( PalmAppGlobals* globals ) +{ + LocalID id; + DmOpenRef dbP; + UInt16 numRecs; + + id = DMFINDDATABASE( globals, CARD_0, XW_GAMES_DBNAME ); + dbP = DMOPENDATABASE( globals, CARD_0, id, dmModeWrite ); + numRecs = DmNumRecords( dbP ); + DMCLOSEDATABASE( dbP ); + return numRecs; +} + +#if defined OWNER_HASH || defined NO_REG_REQUIRED +void +deleteGameRecord( PalmAppGlobals* globals, XP_S16 index ) +{ + LocalID id; + DmOpenRef dbP; + + id = DMFINDDATABASE( globals, CARD_0, XW_GAMES_DBNAME ); + dbP = DMOPENDATABASE( globals, CARD_0, id, dmModeWrite ); + DmRemoveRecord( dbP, index ); + DMCLOSEDATABASE( dbP ); +} /* deleteGameRecord */ + +XP_S16 +duplicateGameRecord( PalmAppGlobals* globals, XP_S16 fromIndex ) +{ + LocalID id; + DmOpenRef dbP; + MemHandle newRecord, curRecord; + XP_U16 size; + XP_S16 newIndex = fromIndex + 1; + + id = DMFINDDATABASE( globals, CARD_0, XW_GAMES_DBNAME ); + dbP = DMOPENDATABASE( globals, CARD_0, id, dmModeWrite ); + XP_ASSERT( fromIndex < countGameRecords(globals) ); + curRecord = DmQueryRecord( dbP, fromIndex ); + size = MemHandleSize( curRecord ); + newRecord = DmNewRecord( dbP, (XP_U16*)&newIndex, size ); + + DmWrite( MemHandleLock(newRecord), 0, MemHandleLock(curRecord), size ); + + MemHandleUnlock( curRecord ); + MemHandleUnlock( newRecord ); + + DmReleaseRecord( dbP, newIndex, true ); + + DMCLOSEDATABASE( dbP ); + + return newIndex; +} /* duplicateGameRecord */ +#endif + +void +streamToGameRecord( PalmAppGlobals* globals, XWStreamCtxt* stream, + XP_S16 index ) +{ + LocalID id; + DmOpenRef dbP; + MemHandle handle; + MemPtr tmpPtr, ptr; + Err err; + + XP_U32 size = stream_getSize( stream ); + + id = DMFINDDATABASE( globals, CARD_0, XW_GAMES_DBNAME ); + dbP = DMOPENDATABASE( globals, CARD_0, id, dmModeWrite ); + XP_ASSERT( !!dbP ); + + if ( index == DmNumRecords(dbP) ) { + handle = DmNewRecord( dbP, (XP_U16*)&index, size ); + } else { + XP_ASSERT( index < countGameRecords(globals) ); + handle = DmGetRecord( dbP, index ); + MemHandleResize( handle, size ); + } + + tmpPtr = MemPtrNew(size); + stream_getBytes( stream, tmpPtr, size ); + ptr = MemHandleLock( handle ); + err = DmWrite( ptr, 0, tmpPtr, size ); + XP_ASSERT( err == 0 ); + MemPtrFree( tmpPtr ); + + MemHandleUnlock(handle); + err = DmReleaseRecord( dbP, index, true ); + XP_ASSERT( err == 0 ); + DMCLOSEDATABASE( dbP ); +} /* streamToGameRecord */ + +void +writeNameToGameRecord( PalmAppGlobals* globals, XP_S16 index, + char* newName, XP_U16 len ) +{ + LocalID id; + DmOpenRef dbP; + MemHandle handle; + char name[MAX_GAMENAME_LENGTH]; + + XP_ASSERT( len == XP_STRLEN(newName) ); + XP_ASSERT( len > 0 ); + XP_MEMSET( name, 0, sizeof(name) ); + if ( len >= MAX_GAMENAME_LENGTH ) { + len = MAX_GAMENAME_LENGTH - 1; + } + XP_MEMCPY( name, newName, len+1 ); + + id = DMFINDDATABASE( globals, CARD_0, XW_GAMES_DBNAME ); + dbP = DMOPENDATABASE( globals, CARD_0, id, dmModeWrite ); + + if ( index == DmNumRecords(dbP) ) { + handle = DmNewRecord(dbP, (XP_U16*)&index, MAX_GAMENAME_LENGTH); + } else { + XP_ASSERT( index < DmNumRecords(dbP) ); + handle = DmGetRecord( dbP, index ); + } + XP_ASSERT( !!handle ); + DmWrite( MemHandleLock(handle), 0, name, MAX_GAMENAME_LENGTH ); + MemHandleUnlock( handle ); + (void)DmReleaseRecord( dbP, index, true ); + DMCLOSEDATABASE( dbP ); +} /* writeNameToGameRecord */ + +void +nameFromRecord( PalmAppGlobals* globals, XP_S16 index, char* buf ) +{ + LocalID id; + DmOpenRef dbP; + MemHandle handle; + + buf[0] = '\0'; /* init to empty string */ + + if ( index < countGameRecords(globals) ) { + + id = DMFINDDATABASE( globals, CARD_0, XW_GAMES_DBNAME ); + if ( id != 0 ) { + dbP = DMOPENDATABASE( globals, CARD_0, id, dmModeWrite ); + if ( dbP != 0 ) { + handle = DmQueryRecord( dbP, index ); + + XP_MEMCPY( buf, MemHandleLock(handle), MAX_GAMENAME_LENGTH ); + buf[MAX_GAMENAME_LENGTH-1] = '\0'; + + MemHandleUnlock( handle ); + DMCLOSEDATABASE( dbP ); + } + } + } +} /* nameFromRecord */ + +/***************************************************************************** + * Later this will provide a default name based on a timestamp. + *****************************************************************************/ +#ifndef TIME_FORMAT +#define TIME_FORMAT tfColonAMPM +#endif +#ifndef DATE_FORMAT +#define DATE_FORMAT dfMDYLongWithComma +#endif +void +makeDefaultGameName( char* buf ) +{ + char timeBuf[timeStringLength+1]; /* add 1 to be safe */ + char dateBuf[longDateStrLength+1]; + DateTimeType timeType; + + TimSecondsToDateTime( TimGetSeconds(), &timeType ); + TimeToAscii( timeType.hour, timeType.minute, TIME_FORMAT, timeBuf ); + + DateToAscii( timeType.month, timeType.day, timeType.year, + DATE_FORMAT, dateBuf ); + + StrPrintF( buf, "%s, %s", dateBuf, timeBuf ); +} /* makeDefaultGameName */ + diff --git a/palm/gameutil.h b/palm/gameutil.h new file mode 100644 index 000000000..d0ff01bf0 --- /dev/null +++ b/palm/gameutil.h @@ -0,0 +1,35 @@ +/* -*-mode: C; fill-column: 77; c-basic-offset: 4; -*- */ +/* + * Copyright 1999 - 2001 by Eric House (fixin@peak.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. + */ + +#ifndef _GAMEUTIL_H_ +#define _GAMEUTIL_H_ + +#include "comtypes.h" +#include "memstream.h" +#include "palmmain.h" + +XP_U16 countGameRecords( PalmAppGlobals* globals ); +void deleteGameRecord( PalmAppGlobals* globals, XP_S16 index ); +XP_S16 duplicateGameRecord( PalmAppGlobals* globals, XP_S16 fromIndex ); +void nameFromRecord( PalmAppGlobals* globals, XP_S16 index, char* buf ); +void streamToGameRecord( PalmAppGlobals* globals, XWStreamCtxt* stream, + XP_S16 index ); +void makeDefaultGameName( char* buf ); + +#endif diff --git a/palm/newgame.c b/palm/newgame.c new file mode 100644 index 000000000..d54a0a3ae --- /dev/null +++ b/palm/newgame.c @@ -0,0 +1,952 @@ +/* -*-mode: C; fill-column: 77; c-basic-offset: 4; -*- */ +/* + * Copyright 1999 - 2001 by Eric House (fixin@peak.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 /* for nextFieldChr */ +#include /* for GrfSetState */ +#include +#ifdef HS_DUO_SUPPORT +# include +#endif + +#include "callback.h" +#include "comtypes.h" +#include "palmmain.h" +#include "comms.h" +#include "strutils.h" +#include "newgame.h" +#include "xwords4defines.h" +#include "dictui.h" +#include "palmdict.h" +#include "palmutil.h" +#include "palmir.h" +#include "prefsdlg.h" +#include "connsdlg.h" + +static void handlePasswordTrigger( PalmAppGlobals* globals, + UInt16 controlID ); +static void adjustVisibility( PalmAppGlobals* globals, XP_Bool canDraw ); +static void setNPlayersAndAdjust( PalmAppGlobals* globals, Int16 chosen ); +static void updatePlayerInfo( PalmAppGlobals* globals ); +static XP_Bool tryFieldNavigationKey( XP_U16 key ); +static void loadNewGameState( PalmAppGlobals* globals ); +static void unloadNewGameState( PalmAppGlobals* globals ); +static void setNameThatFits( PalmNewGameState* state ); +#ifdef HS_DUO_SUPPORT +static XP_Bool tryDuoRockerKey( PalmAppGlobals* globals,XP_U16 key ); +static XP_Bool considerGadgetFocus( PalmNewGameState* state, EventType* event ); +#else +# define tryDuoRockerKey(g,key) XP_FALSE +#endif + +#ifndef XWFEATURE_STANDALONE_ONLY +static Boolean checkHiliteGadget(PalmAppGlobals* globals, EventType* event, + PalmNewGameState* state ); +static void drawConnectGadgets( PalmAppGlobals* globals ); +static void changeGadgetHilite( PalmAppGlobals* globals, UInt16 hiliteID ); + +#else +# define checkHiliteGadget(globals, event, state) XP_FALSE +# define drawConnectGadgets( globals ) +#endif + +#define IS_SERVER_GADGET(id) \ + ((id) >= XW_SOLO_GADGET_ID && (id) <= XW_CLIENT_GADGET_ID) + + +/***************************************************************************** + * + ****************************************************************************/ +Boolean +newGameHandleEvent( EventPtr event ) +{ + Boolean result = false; + EventType eventToPost; /* used only with OK button */ + PalmAppGlobals* globals; + FormPtr form; + LocalPlayer* lp; + CurGameInfo* gi; + PalmNewGameState* state; + Int16 chosen; + XP_U16 i; + XP_U16 controlID; + XP_U16 index; + Boolean on; + Boolean canEdit; + + CALLBACK_PROLOGUE(); + + globals = getFormRefcon(); + gi = &globals->gameInfo; + state = &globals->newGameState; + + switch ( event->eType ) { + + case frmOpenEvent: + + GlobalPrefsToLocal( globals ); + + loadNewGameState( globals ); + + form = FrmGetActiveForm(); +#ifndef XWFEATURE_STANDALONE_ONLY + sizeGadgetsForStrings( form, + getActiveObjectPtr( XW_SERVERTYPES_LIST_ID ), + XW_SOLO_GADGET_ID ); +#endif + state->playerNumList = + getActiveObjectPtr( XW_NPLAYERS_LIST_ID ); + XP_ASSERT( state->playerNumList != NULL ); + + setSelectorFromList( XW_NPLAYERS_SELECTOR_ID, + state->playerNumList, + gi->nPlayers - 1 ); + + XP_ASSERT( !!state->dictName ); + setNameThatFits( state ); + + XP_ASSERT( !!globals->game.server ); + + canEdit = state->curServerHilite == SERVER_STANDALONE + || globals->isNewGame; + + /* load the fields from what we already have */ + for ( lp = gi->players, i = 0; i < MAX_NUM_PLAYERS; ++lp, ++i ) { + XP_U16 offset = i * NUM_PLAYER_COLS; + ControlPtr check; + XP_UCHAR* name; + FieldPtr nameField; + +#ifndef XWFEATURE_STANDALONE_ONLY + Boolean isLocal = lp->isLocal; + check = getActiveObjectPtr(XW_REMOTE_1_CHECKBOX_ID + offset); + CtlSetValue( check, !isLocal ); +#endif + check = getActiveObjectPtr(XW_ROBOT_1_CHECKBOX_ID+offset); + CtlSetValue( check, lp->isRobot ); + + nameField = getActiveObjectPtr(XW_PLAYERNAME_1_FIELD_ID+offset); + name = lp->name; + if ( !!name && !!*name ) { + FldInsert( nameField, (const char*)name, + XP_STRLEN((const char*)name) ); + } + setFieldEditable( nameField, canEdit ); + + /* set up the password */ + if ( !!lp->password ) { + CtlSetLabel( getActiveObjectPtr( + XW_PLAYERPASSWD_1_TRIGGER_ID+offset ), + "*" ); + } + } + /* form = FrmGetActiveForm(); */ + FrmSetFocus(form, FrmGetObjectIndex(form, XW_PLAYERNAME_1_FIELD_ID)); + + case frmUpdateEvent: + adjustVisibility( globals, XP_FALSE ); + + GrfSetState( false, false, false ); + FrmDrawForm( FrmGetActiveForm() ); + + drawConnectGadgets( globals ); + + result = true; + break; + +#ifdef BEYOND_IR + case connsSettingChgEvent: + XP_ASSERT( globals->isNewGame ); + state->connsSettingChanged = XP_TRUE; + break; +#endif + +#ifdef HS_DUO_SUPPORT + case frmObjectFocusTakeEvent: + case frmObjectFocusLostEvent: + result = considerGadgetFocus( state, event ); + break; +#endif + + case penDownEvent: + result = checkHiliteGadget( globals, event, state ); + break; + + case keyDownEvent: + result = tryFieldNavigationKey( event->data.keyDown.chr ) + || tryDuoRockerKey( globals, event->data.keyDown.chr ); + break; + + case prefsChangedEvent: + state->forwardChange = true; + break; + + case ctlEnterEvent: + switch ( event->data.ctlEnter.controlID ) { +#ifndef XWFEATURE_STANDALONE_ONLY + case XW_REMOTE_1_CHECKBOX_ID: + case XW_REMOTE_2_CHECKBOX_ID: + case XW_REMOTE_3_CHECKBOX_ID: + case XW_REMOTE_4_CHECKBOX_ID: +#endif + case XW_DICT_SELECTOR_ID: + case XW_NPLAYERS_SELECTOR_ID: + if ( !globals->isNewGame ) { + result = true; + beep(); + } + } + break; + + case ctlSelectEvent: + result = true; + controlID = event->data.ctlSelect.controlID; + on = event->data.ctlSelect.on; + switch ( controlID ) { + + case XW_ROBOT_1_CHECKBOX_ID: + case XW_ROBOT_2_CHECKBOX_ID: + case XW_ROBOT_3_CHECKBOX_ID: + case XW_ROBOT_4_CHECKBOX_ID: + index = (controlID - XW_ROBOT_1_CHECKBOX_ID) / NUM_PLAYER_COLS; + state->isRobot[index] = on; + adjustVisibility( globals, XP_TRUE ); + break; +#ifndef XWFEATURE_STANDALONE_ONLY + case XW_REMOTE_1_CHECKBOX_ID: + case XW_REMOTE_2_CHECKBOX_ID: + case XW_REMOTE_3_CHECKBOX_ID: + case XW_REMOTE_4_CHECKBOX_ID: + XP_ASSERT( state->curServerHilite == SERVER_ISSERVER ); + index = (controlID - XW_REMOTE_1_CHECKBOX_ID) / NUM_PLAYER_COLS; + state->isLocal[index] = !on; + state->curNPlayersLocal += on? -1:1; + adjustVisibility( globals, XP_TRUE ); + break; +#endif + + case XW_NPLAYERS_SELECTOR_ID: + XP_ASSERT( globals->isNewGame ); + chosen = LstPopupList( state->playerNumList ); + if ( chosen >= 0 ) { + setSelectorFromList( XW_NPLAYERS_SELECTOR_ID, + state->playerNumList, + chosen ); + ++chosen; /* chosen is 0-based */ + if (state->curServerHilite==SERVER_ISCLIENT) { + state->curNPlayersLocal = chosen; + XP_ASSERT( state->curNPlayersLocal <= MAX_NUM_PLAYERS ); + } else { + state->curNPlayersTotal = chosen; + } + setNPlayersAndAdjust( globals, chosen ); + } + break; + + case XW_DICT_SELECTOR_ID: + XP_ASSERT( globals->isNewGame ); + globals->dictuiForBeaming = false; + FrmPopupForm( XW_DICTINFO_FORM ); + + /* popup dict selection dialog -- or maybe just a list if there + are no preferences to set. The results should all be + cancellable, so don't delete the existing dictionary (if + any) until OK is chosen */ + break; + + case XW_OK_BUTTON_ID: + + /* if we put up the prefs form from within this one and the user + clicked ok, we need to make sure the main form gets the + notification so it can make use of any changes. */ + if ( globals->isNewGame ) { + + updatePlayerInfo( globals ); + + eventToPost.eType = newGameOkEvent; + EvtAddEventToQueue( &eventToPost ); + globals->postponeDraw = true; + + } else if ( state->curServerHilite + == SERVER_STANDALONE ) { + updatePlayerInfo( globals ); + } + + if ( state->forwardChange ) { + eventToPost.eType = prefsChangedEvent; + EvtAddEventToQueue( &eventToPost ); + state->forwardChange = false; + } + + unloadNewGameState( globals ); + + FrmReturnToForm( 0 ); + break; + + case XW_CANCEL_BUTTON_ID: + unloadNewGameState( globals ); + eventToPost.eType = newGameCancelEvent; + EvtAddEventToQueue( &eventToPost ); + FrmReturnToForm( 0 ); + break; + + case XW_PREFS_BUTTON_ID: + /* bring up with the this-game tab selected */ + XP_ASSERT( !!globals->prefsDlgState ); + globals->prefsDlgState->stateTypeIsGlobal = false; + FrmPopupForm( XW_PREFS_FORM ); + break; + + case XW_PLAYERPASSWD_1_TRIGGER_ID: + case XW_PLAYERPASSWD_2_TRIGGER_ID: + case XW_PLAYERPASSWD_3_TRIGGER_ID: + case XW_PLAYERPASSWD_4_TRIGGER_ID: + handlePasswordTrigger( globals, controlID ); + break; + + default: /* one of the password selectors? */ + result = false; + } + break; + + case dictSelectedEvent: + /* posted by the form we raise when user clicks Dictionary selector + above. */ + if ( state->dictName != NULL ) { + XP_FREE( globals->mpool, state->dictName ); + } + state->dictName = + ((DictSelectedData*)&event->data.generic)->dictName; + setNameThatFits( state ); + break; + + default: + break; + } + + CALLBACK_EPILOGUE(); + return result; +} /* newGameHandleEvent */ + +static void +setNameThatFits( PalmNewGameState* state ) +{ + RectangleType rect; + XP_U16 width; + XP_U16 len = XP_STRLEN( (const char*)state->dictName ); + + XP_MEMCPY( state->shortDictName, state->dictName, len + 1 ); + + /* The width available is the cancel button's left minus ours */ + getObjectBounds( XW_CANCEL_BUTTON_ID, &rect ); + width = rect.topLeft.x; + getObjectBounds( XW_DICT_SELECTOR_ID, &rect ); + width -= (rect.topLeft.x + 6); + + for ( ; FntCharsWidth( (const char*)state->dictName, len ) > width; --len ) { + /* do nothing */ + } + + state->shortDictName[len] = '\0'; + CtlSetLabel( getActiveObjectPtr( XW_DICT_SELECTOR_ID ), + (const char*)state->shortDictName ); +} /* setNameThatFits */ + +static XP_U16 +countLocalIn( PalmNewGameState* state, XP_U16 nPlayers ) +{ + XP_U16 nLocal = 0; + XP_U16 i; + + for ( i = 0; i < nPlayers; ++i ) { + if ( state->isLocal[i] ) { + ++nLocal; + } + } + + return nLocal; +} /* countLocalIn */ + +/* If we're in GUEST mode, only local players are visible, and so this means + * an increase in the number of local players. If we're in a different mode + * then it means a simple increase in all players. Only the first case is + * difficult, because if the number's getting larger we need to confirm that + * there's another local player to show, and if there's not we need to + * convert the first non-local player. + */ +static void +setNPlayersAndAdjust( PalmAppGlobals* globals, Int16 chosen ) +{ +#ifndef XWFEATURE_STANDALONE_ONLY + PalmNewGameState* state = &globals->newGameState; + + if ( state->curServerHilite == SERVER_ISCLIENT ) { + XP_U16 i; + XP_S16 nRemote = 0; + XP_S16 nLocal = 0; + + /* find the first non-local player */ + for ( i = 0; i < MAX_NUM_PLAYERS; ++i ) { + if ( state->isLocal[i] ) { + ++nLocal; + } else { + ++nRemote; + } + } + + /* Make as many local as necessary */ + for ( i = 0; nLocal < chosen && i < MAX_NUM_PLAYERS; ++i ) { + if ( !state->isLocal[i] ) { + state->isLocal[i] = true; + setBooleanCtrl( XW_REMOTE_1_CHECKBOX_ID + + (i * NUM_PLAYER_COLS), false ); + ++nLocal; + --nRemote; + } + } + + state->curNPlayersLocal = chosen; + XP_ASSERT( state->curNPlayersLocal <= MAX_NUM_PLAYERS ); + state->curNPlayersTotal = chosen + nRemote; + XP_ASSERT( state->curNPlayersTotal <= MAX_NUM_PLAYERS ); + } else { + state->curNPlayersTotal = chosen; + state->curNPlayersLocal = countLocalIn( state, chosen ); + XP_ASSERT( state->curNPlayersLocal <= MAX_NUM_PLAYERS ); + } +#endif + + adjustVisibility( globals, XP_TRUE ); +} /* setNPlayersAndAdjust */ + +static Boolean +tryFieldNavigationKey( XP_U16 key ) +{ + FormPtr form; + Int16 curFocus, nextFocus, change; + UInt16 nObjects; + + if ( key == prevFieldChr ) { + change = -1; + } else if ( key == nextFieldChr ) { + change = 1; + } else { + return false; + } + + form = FrmGetActiveForm(); + curFocus = nextFocus = FrmGetFocus( form ); + nObjects = FrmGetNumberOfObjects(form); + + /* find the next (in either direction) usable field */ + for ( ; ; ) { + nextFocus += change; + + if ( nextFocus == nObjects ) { + nextFocus = 0; + } else if ( nextFocus < 0 ) { + nextFocus = nObjects-1; + } + + if ( nextFocus == curFocus ) { + break; + } else if ( FrmGetObjectType(form, nextFocus) != frmFieldObj ) { + continue; + } else { + FieldPtr field = FrmGetObjectPtr( form, nextFocus ); + FieldAttrType attrs; + FldGetAttributes( field, &attrs ); + if ( attrs.usable ) { + break; + } + } + } + + FrmSetFocus( form, nextFocus ); + return true; +} /* tryFieldNavigationKey */ + +#ifdef HS_DUO_SUPPORT +#ifdef DEBUG +static XP_UCHAR* +keyToStr( XP_U16 key ) +{ +#define keyCase(k) case (k): return #k + switch( key ) { + keyCase(vchrRockerUp); + keyCase(vchrRockerDown); + keyCase(vchrRockerLeft); + keyCase(vchrRockerRight); + keyCase(vchrRockerCenter); + default: + return "huh?"; + } +#undef keyCase +} +#endif + +static XP_Bool +tryDuoRockerKey( PalmAppGlobals* globals, XP_U16 key ) +{ + XP_Bool result = XP_FALSE; + XP_U16 focusID; + FormPtr form; + + switch( key ) { + case vchrRockerUp: + case vchrRockerDown: + case vchrRockerLeft: + case vchrRockerRight: + XP_LOGF( "got rocker key: %s", keyToStr(key) ); + result = XP_FALSE; + break; + case vchrRockerCenter: + /* if one of the gadgets is focussed, "tap" it. */ + XP_LOGF( "got rocker key: %s", keyToStr(key) ); + form = FrmGetActiveForm(); + focusID = FrmGetObjectId( form, FrmGetFocus( form ) ); + if ( IS_SERVER_GADGET( focusID ) ) { + changeGadgetHilite( globals, focusID ); + result = XP_TRUE; + } else { + XP_LOGF( "%d not server gadget", focusID ); + } + break; + default: + break; + } + return result; +} /* tryDuoRockerKey */ +#endif + +static void +adjustVisibility( PalmAppGlobals* globals, XP_Bool canDraw ) +{ + FormPtr form = FrmGetActiveForm(); + short i; + PalmNewGameState* state = &globals->newGameState; + XP_Bool isNewGame = globals->isNewGame; + + Connectedness curServerHilite = state->curServerHilite; + Boolean canShowRemote = curServerHilite != SERVER_STANDALONE; + Boolean isClient = curServerHilite == SERVER_ISCLIENT; + XP_U16 nShown = 0; + Boolean canEdit = (curServerHilite == SERVER_STANDALONE) || isNewGame; + XP_U16 nToShow = (isClient && isNewGame)? + state->curNPlayersLocal:state->curNPlayersTotal; + + /* It's illegal for there to be 0 players selected. So if that ever + happens, make the first player local. And beep? */ + if ( nToShow == 0 ) { + XP_ASSERT( isClient ); /* the only way this can happen is if someone + sets type to SERVER_ISCLIENT when there + are no local players. */ + state->isLocal[0] = true; + nToShow = state->curNPlayersLocal = 1; + setBooleanCtrl( XW_REMOTE_1_CHECKBOX_ID, false ); + } + + if ( canShowRemote && isClient ) { + canShowRemote = !isNewGame; + } + +#ifndef XWFEATURE_STANDALONE_ONLY + disOrEnable( form, XW_LOCAL_LABEL_ID, canShowRemote ); + if ( canShowRemote ) { + disOrEnable( form, XW_TOTALP_LABEL_ID, XP_TRUE ); + } else { + disOrEnable( form, XW_LOCALP_LABEL_ID, XP_TRUE ); + } +#endif + + for ( i = 0; i < MAX_NUM_PLAYERS; ++i ) { + short offset = NUM_PLAYER_COLS * i; + Boolean lineVisible = nShown < nToShow; + Boolean isLocal; +#ifndef XWFEATURE_STANDALONE_ONLY + Boolean showRemote; + Boolean remoteChecked = !state->isLocal[i]; + + if ( isClient && lineVisible && remoteChecked && isNewGame ) { + lineVisible = false; + } +#endif + /* Since the user of a device can change at will whether a local + player is a robot, we don't show that attribute except when it's + for a local player; don't keep track of what's going on on the + other device. */ + isLocal = lineVisible; +#ifndef XWFEATURE_STANDALONE_ONLY + showRemote = lineVisible && canShowRemote; + isLocal = isLocal && + (curServerHilite == SERVER_STANDALONE + || (showRemote && !remoteChecked) + || (isClient && isNewGame) ); + + /* show local/remote checkbox if not standalone */ + disOrEnable( form, XW_REMOTE_1_CHECKBOX_ID + offset, + lineVisible && showRemote ); +#endif + /* show name no matter what (if line's showing) */ + disOrEnable( form, XW_PLAYERNAME_1_FIELD_ID + offset, + lineVisible && (isLocal || !isNewGame) ); + + if ( lineVisible ) { + FieldPtr nameField = + getActiveObjectPtr(XW_PLAYERNAME_1_FIELD_ID + offset); + setFieldEditable( nameField, canEdit ); + if ( canDraw ) { + FldDrawField( nameField ); + } + } + + /* show robot checkbox if player is local */ + disOrEnable( form, XW_ROBOT_1_CHECKBOX_ID + offset, isLocal ); + + /* and show password if not a robot (and if local) */ + disOrEnable( form, XW_PLAYERPASSWD_1_TRIGGER_ID + offset, + isLocal && !state->isRobot[i] ); + + if ( lineVisible ) { + ++nShown; + } + XP_ASSERT( nShown <= MAX_NUM_PLAYERS ); + } + +#ifndef XWFEATURE_STANDALONE_ONLY + XP_ASSERT( nShown > 0 ); + setSelectorFromList( XW_NPLAYERS_SELECTOR_ID, + state->playerNumList, nShown - 1 ); +#endif +} /* adjustVisibility */ + +/* + * Copy the local state into global state. + */ +static void +updatePlayerInfo( PalmAppGlobals* globals ) +{ + UInt16 i; + CurGameInfo* gi; + LocalPlayer* lp; + PalmNewGameState* state = &globals->newGameState; + Connectedness curServerHilite = globals->newGameState.curServerHilite; + + gi = &globals->gameInfo; + + gi->nPlayers = curServerHilite == SERVER_ISCLIENT? + state->curNPlayersLocal: state->curNPlayersTotal; + XP_ASSERT( gi->nPlayers <= MAX_NUM_PLAYERS ); + + gi->boardSize = globals->prefsDlgState->curBdSize; + gi->serverRole = curServerHilite; + + replaceStringIfDifferent( MPPARM(globals->mpool) &gi->dictName, + globals->newGameState.dictName ); + + for ( i = 0, lp = gi->players; i < MAX_NUM_PLAYERS; ++i, ++lp ) { + XP_UCHAR* name = NULL; + XP_UCHAR* passwd = NULL; + short offset = NUM_PLAYER_COLS * i; + XP_Bool isLocal = state->isLocal[i]; + + if ( isLocal ) { + MemPtr p = getActiveObjectPtr( offset + + XW_PLAYERNAME_1_FIELD_ID ); + name = (XP_UCHAR*)FldGetTextPtr( p ); + + if ( name == NULL ) { + name = (XP_UCHAR*)""; + } + passwd = globals->newGameState.passwds[i]; + } + + lp->isRobot = state->isRobot[i]; + lp->isLocal = isLocal; + + replaceStringIfDifferent(MPPARM(globals->mpool) &lp->name, name); + replaceStringIfDifferent(MPPARM(globals->mpool) &lp->password, passwd); + } + +#ifdef BEYOND_IR + if ( state->connsSettingChanged ) { + CommsAddrRec addr; + XP_U16 localPort; + + comms_getAddr( globals->game.comms, &addr, &localPort ); + + addr.conType = state->connAddrs.conType; + if ( addr.conType == COMMS_CONN_IP ) { + addr.u.ip.port = state->connAddrs.remotePort; + addr.u.ip.ipAddr = state->connAddrs.remoteIP; + localPort = state->connAddrs.localPort; + } + comms_setAddr( globals->game.comms, &addr, localPort ); + } +#endif + +} /* updatePlayerInfo */ + +void +drawOneGadget( UInt16 id, char* text, Boolean hilite ) +{ + RectangleType divRect; + XP_U16 len = StrLen(text); + XP_U16 width = FntCharsWidth( text, len ); + XP_U16 left; + + getObjectBounds( id, &divRect ); + WinDrawRectangleFrame( rectangleFrame, &divRect ); + WinEraseRectangle( &divRect, 0 ); + left = divRect.topLeft.x; + left += (divRect.extent.x - width) / 2; + WinDrawChars( text, len, left, divRect.topLeft.y ); + if ( hilite ) { + WinInvertRectangle( &divRect, 0 ); + } +} /* drawOneGadget */ + +/* Frame 'em, draw their text, and highlight the one that's selected + */ +#ifndef XWFEATURE_STANDALONE_ONLY + +#ifdef HS_DUO_SUPPORT +static void +drawFocusRingOnGadget() +{ + FormPtr form = FrmGetActiveForm(); + XP_U16 focusID = FrmGetObjectId( form, FrmGetFocus(form) ); + if ( IS_SERVER_GADGET(focusID) ) { + Err err; + RectangleType rect; + + getObjectBounds( focusID, &rect ); + + err = HsNavDrawFocusRing( form, focusID, 0, &rect, + hsNavFocusRingStyleObjectTypeDefault, + false ); + XP_ASSERT( err == errNone ); + } +} /* drawFocusRingOnGadget */ +#endif + +static void +drawConnectGadgets( PalmAppGlobals* globals ) +{ + UInt16 i; + ListPtr list = getActiveObjectPtr( XW_SERVERTYPES_LIST_ID ); + XP_ASSERT( !!list ); + + for ( i = 0; i < 3; ++i ) { + char* text = LstGetSelectionText( list, i ); + Boolean hilite = i == globals->newGameState.curServerHilite; + drawOneGadget( i + XW_SOLO_GADGET_ID, text, hilite ); + } + +#ifdef HS_DUO_SUPPORT + drawFocusRingOnGadget(); +#endif + +} /* drawConnectGadgets */ + +#ifdef HS_DUO_SUPPORT +static XP_Bool +considerGadgetFocus( PalmNewGameState* state, EventType* event ) +{ + XP_Bool result = XP_FALSE; + XP_Bool isTake; + XP_U16 eType = event->eType; + FormPtr form = FrmGetActiveForm(); + XP_U16 objectID; + + XP_ASSERT( eType == frmObjectFocusTakeEvent + || eType == frmObjectFocusLostEvent ); + XP_ASSERT( event->data.frmObjectFocusTake.formID == FrmGetActiveFormID() ); + + + isTake = eType == frmObjectFocusTakeEvent; + if ( isTake ) { + objectID = event->data.frmObjectFocusTake.objectID; + } else { + objectID = event->data.frmObjectFocusLost.objectID; + } + + /* docs say to return HANDLED for both take and lost */ + result = IS_SERVER_GADGET( objectID ); + + if ( result ) { + Err err; + if ( isTake ) { + + FrmSetFocus(form, FrmGetObjectIndex(form, objectID)); + drawFocusRingOnGadget(); + result = XP_TRUE; +/* } else { */ +/* err = HsNavRemoveFocusRing( form ); */ + } + } + + return result; +} /* considerGadgetFocus */ +#endif + +static void +changeGadgetHilite( PalmAppGlobals* globals, UInt16 hiliteID ) +{ + PalmNewGameState* state = &globals->newGameState; + XP_Bool isNewGame = globals->isNewGame; + + hiliteID -= XW_SOLO_GADGET_ID; + + if ( hiliteID != state->curServerHilite ) { + /* if it's not a new game, don't recognize the change */ + if ( isNewGame ) { + state->curServerHilite = hiliteID; + drawConnectGadgets( globals ); + adjustVisibility( globals, XP_TRUE ); + } else { + beep(); + } + } + +#ifdef BEYOND_IR + /* Even if it didn't change, pop the connections form */ + if ( hiliteID != SERVER_STANDALONE ) { + if ( isNewGame || hiliteID==globals->newGameState.curServerHilite ) { + PopupConnsForm( globals, hiliteID, &state->connAddrs ); + } + } +#endif +} /* changeGadgetHilite */ + +static Boolean +checkHiliteGadget( PalmAppGlobals* globals, EventType* event, + PalmNewGameState* state ) +{ + Boolean result = false; + UInt16 selGadget; + + XP_ASSERT( &globals->newGameState == state ); + + result = penInGadget( event, &selGadget ); + if ( result ) { + changeGadgetHilite( globals, selGadget ); + } + + return result; +} /* checkHiliteGadget */ +#endif + +/* If there's currently no password set, just let 'em set one. If there is + * one set, they need to know the old before setting the new. + */ +static void +handlePasswordTrigger( PalmAppGlobals* globals, UInt16 controlID ) +{ + UInt16 playerNum; + PalmNewGameState* state = &globals->newGameState; + XP_UCHAR** password; + XP_UCHAR* name; + FieldPtr nameField; + XP_U16 len; + XP_UCHAR buf[32]; + char* label; + + playerNum = (controlID - XW_PLAYERPASSWD_1_TRIGGER_ID) / NUM_PLAYER_COLS; + XP_ASSERT( playerNum < MAX_NUM_PLAYERS ); + + password = &state->passwds[playerNum]; + nameField = getActiveObjectPtr( XW_PLAYERNAME_1_FIELD_ID + + (NUM_PLAYER_COLS * playerNum) ); + name = (XP_UCHAR*)FldGetTextPtr( nameField ); + + len = sizeof(buf); + if ( askPassword( globals, name, true, buf, &len ) ) { + + if ( len == 0 ) { + buf[0] = '\0'; + label = " "; + } else { + label = "*"; + } + replaceStringIfDifferent(MPPARM(globals->mpool) password, + (unsigned char*)buf); + + /* control owns the string passed in */ + CtlSetLabel( getActiveObjectPtr( controlID ), label ); + } +} /* handlePasswordTrigger */ + +static void +unloadNewGameState( PalmAppGlobals* globals ) +{ + XP_U16 i; + XP_UCHAR** passwd; + PalmNewGameState* state = &globals->newGameState; + + for ( passwd = state->passwds, i = 0; + i < MAX_NUM_PLAYERS; ++i, ++passwd ) { + if ( !!*passwd ) { + XP_FREE( globals->mpool, *passwd ); + *passwd = NULL; + } + } + /* XP_WARNF( "freeing string 0x%lx", state->dictName ); */ + XP_FREE( globals->mpool, state->dictName ); + state->dictName = NULL; +} /* unloadNewGameState */ + +static void +loadNewGameState( PalmAppGlobals* globals ) +{ + CurGameInfo* gi = &globals->gameInfo; + PalmNewGameState* state = &globals->newGameState; + XP_U16 i; + LocalPlayer* lp; + + XP_MEMSET( state, 0, sizeof(*state) ); + + state->dictName = copyString( MPPARM(globals->mpool) gi->dictName ); + state->curServerHilite = gi->serverRole; + + for ( i = 0, lp=gi->players; i < MAX_NUM_PLAYERS; ++i, ++lp ) { + state->isLocal[i] = lp->isLocal; + state->isRobot[i] = lp->isRobot; + state->passwds[i] = copyString( MPPARM(globals->mpool) + lp->password ); + } + + state->curNPlayersTotal = gi->nPlayers; + state->curNPlayersLocal = countLocalIn( state, gi->nPlayers ); + XP_ASSERT( state->curNPlayersLocal <= MAX_NUM_PLAYERS ); + +#ifdef BEYOND_IR + { + CommsAddrRec addr; + comms_getAddr( globals->game.comms, &addr, + &state->connAddrs.localPort ); + state->connAddrs.remotePort = addr.u.ip.port; + state->connAddrs.remoteIP = addr.u.ip.ipAddr; + state->connAddrs.conType = addr.conType; + } +#endif + +} /* loadNewGameState */ diff --git a/palm/newgame.h b/palm/newgame.h new file mode 100644 index 000000000..fa7c795e2 --- /dev/null +++ b/palm/newgame.h @@ -0,0 +1,32 @@ +/* -*-mode: C; fill-column: 77; c-basic-offset: 4; -*- */ +/* + * Copyright 2001-2002 by Eric House (fixin@peak.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. + */ + +#ifndef NEWGAME_H +#define NEWGAME_H + +#include + +#include "palmmain.h" + + +Boolean newGameHandleEvent( EventPtr event ); + +void drawOneGadget( UInt16 id, char* text, Boolean hilite ); + +#endif