first checkin

This commit is contained in:
ehouse 2003-11-01 05:35:29 +00:00
parent c505d9a0f7
commit 5776da0b93
80 changed files with 22330 additions and 0 deletions

5
xwords4/common/Makefile Normal file
View file

@ -0,0 +1,5 @@
# -*- mode: Makefile; -*-
clean:
rm -rf $(PLATFORM)/*.o

2625
xwords4/common/board.c Normal file

File diff suppressed because it is too large Load diff

163
xwords4/common/board.h Normal file
View file

@ -0,0 +1,163 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 1997 - 2000 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 __BOARD_H__
#define __BOARD_H__
#include "comtypes.h"
#include "model.h"
#include "server.h"
#include "draw.h"
#include "xwstream.h"
/* typedef struct BoardVTable { */
/* } BoardVTable; */
#ifdef CPLUS
extern "C" {
#endif
typedef enum {
TRAY_HIDDEN, /* doesn't happen unless tray overlaps board */
TRAY_REVERSED,
TRAY_REVEALED
} XW_TrayVisState;
typedef enum {
OBJ_NONE,
OBJ_BOARD,
OBJ_SCORE,
OBJ_TRAY
} BoardObjectType;
typedef enum {
/* keep these three together: for the cursor */
XP_KEY_NONE,
XP_CURSOR_KEY_DOWN,
XP_CURSOR_KEY_RIGHT,
XP_CURSOR_KEY_UP,
XP_CURSOR_KEY_LEFT,
XP_CURSOR_KEY_DEL,
XP_FOCUSCHANGE_KEY,
XP_RETURN_KEY,
XP_KEY_LAST
} XP_Key;
#define BONUS_HINT_INTERVAL 15 /* stolen from xwords.c */
/* typedef struct BoardCtxt BoardCtxt; */
BoardCtxt* board_make( MPFORMAL ModelCtxt* model, ServerCtxt* server,
DrawCtx* draw, XW_UtilCtxt* util );
BoardCtxt* board_makeFromStream( MPFORMAL XWStreamCtxt* stream,
ModelCtxt* model, ServerCtxt* server,
DrawCtx* draw, XW_UtilCtxt* util,
XP_U16 nPlayers );
void board_destroy( BoardCtxt* board );
void board_writeToStream( BoardCtxt* board, XWStreamCtxt* stream );
void board_setPos( BoardCtxt* board, XP_U16 left, XP_U16 top,
XP_Bool leftHanded );
void board_reset( BoardCtxt* board );
/* Vertical scroll support; offset is in rows, not pixels */
XP_Bool board_setYOffset( BoardCtxt* board, XP_U16 newOffset,
XP_Bool invalRevealed );
XP_U16 board_getYOffset( BoardCtxt* board );
void board_setScoreboardLoc( BoardCtxt* board,
XP_U16 scoreLeft, XP_U16 scoreTop,
XP_U16 scoreWidth, XP_U16 scoreHeight,
XP_Bool divideHorizontally );
void board_setTimerLoc( BoardCtxt* board,
XP_U16 timerLeft, XP_U16 timerTop,
XP_U16 timerWidth, XP_U16 timerHeight );
void board_invalAll( BoardCtxt* board );
void board_invalRect( BoardCtxt* board, XP_Rect* rect );
XP_Bool board_draw( BoardCtxt* board );
XP_Bool board_flip( BoardCtxt* board );
XP_Bool board_toggle_showValues( BoardCtxt* board );
XP_Bool board_getShowColors( BoardCtxt* board );
XP_Bool board_setShowColors( BoardCtxt* board, XP_Bool showColors );
XP_Bool board_replaceTiles( BoardCtxt* board );
XP_Bool board_requestHint( BoardCtxt* board, XP_Bool* workRemainsP );
void board_setScale( BoardCtxt* board, XP_U16 hScale, XP_U16 vScale );
void board_getScale( BoardCtxt* board, XP_U16* hScale, XP_U16* vScale );
XP_Bool board_prefsChanged( BoardCtxt* board, CommonPrefs* cp );
BoardObjectType board_getFocusOwner( BoardCtxt* board );
void board_hiliteCellAt( BoardCtxt* board, XP_U16 col, XP_U16 row );
void board_resetEngine( BoardCtxt* board );
void board_timerFired( BoardCtxt* board, XWTimerReason why );
XP_Bool board_commitTurn( BoardCtxt* board );
void board_pushTimerSave( BoardCtxt* board );
void board_popTimerSave( BoardCtxt* board );
#ifdef POINTER_SUPPORT
XP_Bool board_handlePenDown( BoardCtxt* board, XP_U16 x, XP_U16 y,
XP_Time when );
XP_Bool board_handlePenMove( BoardCtxt* board, XP_U16 x, XP_U16 y );
XP_Bool board_handlePenUp( BoardCtxt* board, XP_U16 x, XP_U16 y, XP_Time when );
#endif
XP_Bool board_handleKey( BoardCtxt* board, XP_Key key );
#ifdef KEYBOARD_NAV
/* void board_focusChange( BoardCtxt* board ); */
XP_Bool board_toggle_arrowDir( BoardCtxt* board );
#endif
/******************** Tray methods ********************/
#define NO_TILES ((TileBit)0)
void board_setTrayLoc( BoardCtxt* board, XP_U16 trayLeft, XP_U16 trayTop,
XP_U8 trayScaleH, XP_U8 trayScaleV,
XP_U8 dividerWidth );
XP_Bool board_hideTray( BoardCtxt* board );
XP_Bool board_showTray( BoardCtxt* board );
XW_TrayVisState board_getTrayVisState( BoardCtxt* board );
void board_invalTrayTiles( BoardCtxt* board, TileBit what );
XP_Bool board_juggleTray( BoardCtxt* board );
XP_Bool board_beginTrade( BoardCtxt* board );
#if defined FOR_GREMLINS || defined KEYBOARD_NAV
XP_Bool board_moveDivider( BoardCtxt* board, XP_Bool right );
#endif
#ifdef CPLUS
}
#endif
#endif

191
xwords4/common/boardp.h Normal file
View file

@ -0,0 +1,191 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 1997 - 2000 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 _BOARDP_H_
#define _BOARDP_H_
#include "comtypes.h"
#include "model.h"
#include "board.h"
#include "mempool.h" /* debug only */
#ifdef CPLUS
extern "C" {
#endif
typedef struct TileDragState {
XP_Bool dragInProgress;
XP_Bool wasHilited;
TileBit selectionAtStart;
XP_Bool movePending;
TileBit prevIndex;
} TileDragState;
typedef struct DividerDragState {
XP_Bool dragInProgress;
} DividerDragState;
typedef struct BoardArrow { /* gets flipped along with board */
XP_U8 col;
XP_U8 row;
XP_Bool vert;
XP_Bool visible;
} BoardArrow;
#ifdef KEYBOARD_NAV
typedef struct BdCursorLoc {
XP_U8 col;
XP_U8 row;
} BdCursorLoc;
#endif
/* We only need two of these, one for the value hint and the other for the
trading window. There's never more than of the former since it lives only
as long as the pen is down. There are, in theory, as many trading windows
as there are (local) players, but they can all use the same window. */
typedef struct MiniWindowStuff {
void* closure;
unsigned char* text;
XP_Rect rect;
} MiniWindowStuff;
enum { MINIWINDOW_VALHINT, MINIWINDOW_TRADING };
typedef XP_U16 MiniWindowType; /* one of the two above */
struct BoardCtxt {
/* BoardVTable* vtable; */
ModelCtxt* model;
ServerCtxt* server;
DrawCtx* draw;
XW_UtilCtxt* util;
struct CurGameInfo* gi;
XP_U16 boardHScale;
XP_U16 boardVScale;
XP_U16 yOffset;
XP_U16 preHideYOffset;
XP_U16 prevYScrollOffset; /* represents where the last draw took place;
used to see if bit scrolling can be used */
XP_U16 penDownX;
XP_U16 penDownY;
XP_U32 timerStoppedTime;
XP_U16 timerSaveCount;
#ifdef DEBUG
XP_S16 timerStoppedTurn;
#endif
XP_U16 redrawFlags[MAX_ROWS];
XP_Rect boardBounds;
XP_Time penDownTime;
BoardObjectType penDownObject;
XP_Bool needsDrawing;
XP_Bool isFlipped;
XP_Bool showGrid;
XP_Bool gameOver;
XP_Bool leftHanded;
XP_Bool badWordRejected;
XP_Bool timerPending;
XP_Bool disableArrow;
XP_Bool tradeInProgress[MAX_NUM_PLAYERS];
XP_Bool eraseTray;
XP_Bool boardObscuresTray;
XP_Bool scoreSplitHor; /* how to divide the scoreboard? */
XP_U16 star_row;
/* Unless KEYBOARD_NAV is defined, this does not change */
BoardObjectType focussed;
BoardArrow boardArrow[MAX_NUM_PLAYERS];
#ifdef KEYBOARD_NAV
BdCursorLoc bdCursor[MAX_NUM_PLAYERS];
#endif
XP_U8 dividerLoc[MAX_NUM_PLAYERS]; /* 0 means left of 0th tile, etc. */
XP_U8 scoreDims[MAX_NUM_PLAYERS];
/* scoreboard state */
XP_Rect scoreBdBounds;
XP_Rect timerBounds;
XP_U8 selPlayer; /* which player is selected (!= turn) */
XP_U8 remDim; /* width (or ht) of the "rem:" string in scoreboard */
/* tray state */
XP_U8 trayScaleH;
XP_U8 trayScaleV;
XP_Rect trayBounds;
XP_U8 dividerWidth; /* 0 would mean invisible */
XP_Bool dividerInvalid;
XP_Bool scoreBoardInvalid;
TileDragState tileDragState;
DividerDragState divDragState;
MiniWindowStuff miniWindowStuff[2];
XP_Bool tradingMiniWindowInvalid;
TileBit trayInvalBits;
TileBit traySelBits[MAX_NUM_PLAYERS];
#ifdef KEYBOARD_NAV
XP_U8 trayCursorLoc[MAX_NUM_PLAYERS];
#endif
XW_TrayVisState trayVisState;
XP_Bool penTimerFired;
XP_Bool showCellValues;
XP_Bool showColors;
MPSLOT
};
#define valHintMiniWindowActive( board ) \
((XP_Bool)((board)->miniWindowStuff[MINIWINDOW_VALHINT].text != NULL))
#define MY_TURN(b) ((b)->selPlayer == server_getCurrentTurn( (b)->server ))
#define TRADE_IN_PROGRESS(b) ((b)->tradeInProgress[(b)->selPlayer]==XP_TRUE)
/* tray-related functions */
XP_Bool handlePenDownInTray( BoardCtxt* board, XP_U16 x, XP_U16 y );
XP_Bool handlePenUpTray( BoardCtxt* board, XP_U16 x, XP_U16 y, XP_Time when );
void drawTray( BoardCtxt* board, XP_Bool focussed );
TileBit continueTileDrag( BoardCtxt* board, XP_U16 x, XP_U16 y );
XP_Bool endTileDrag( BoardCtxt* board, XP_U16 x, XP_U16 y );
XP_Bool continueDividerDrag( BoardCtxt* board, XP_U16 x, XP_U16 y );
XP_Bool endDividerDrag( BoardCtxt* board, XP_U16 x, XP_U16 y );
XP_Bool moveTileToArrowLoc( BoardCtxt* board, XP_U8 index );
XP_U16 indexForBits( XP_U8 bits );
XP_Bool rectContainsPt( XP_Rect* rect1, XP_U16 x, XP_U16 y );
XP_Bool checkRevealTray( BoardCtxt* board );
#ifdef KEYBOARD_NAV
XP_Bool tray_moveCursor( BoardCtxt* board, XP_Key cursorKey );
XP_Bool tray_keyAction( BoardCtxt* board );
#endif
#ifdef CPLUS
}
#endif
#endif

54
xwords4/common/commmgr.h Normal file
View file

@ -0,0 +1,54 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 2000 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.
*/
/* The Communications manager brokers messages between controllers It
* maps players to devices.
*
* Messages for players go through it. If the player is remote, then the
* message is queued. If local, it's a simple function call executed
* immediately. When the caller is finished, it calls comms_okToSend()
* or somesuch, and all the queued messages are combined into a single
* message for each device represented, and sent.
*
* The problem of duplicate messages: Say there are two players A and B on
* remote device D. A has just made a move {{7,6,'A'},{7,7,'T'}}. The
* server wants to tell each player about A's move. It will want to send the
* same message to every player but A, yet there's no point in sending to B's
* device since the information is already there.
*
* There are three possiblities: put message codes into classes -- e.g.
*/
#ifndef _COMMMGR_H_
#define _COMMMGR_H_
#include "comtypes.h" /* that's *common* types */
#include "xwstream.h"
#include "server.h"
/* typedef struct CommMgrCtxt CommMgrCtxt; */
CommMgrCtxt* commmgr_make( XWStreamCtxt* serverStream );
void commmgr_setServer( CommMgrCtxt* commMgr, ServerCtxt* server );
XWStreamCtxt* commmgr_getServerStream( CommMgrCtxt* commmgr );
void commmgr_receiveMessage( CommMgrCtxt* commmgr, XWStreamCtxt* incomming );
#endif

719
xwords4/common/comms.c Normal file
View file

@ -0,0 +1,719 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 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.
*/
#ifdef USE_STDIO
# include <stdio.h>
#endif
#include "comms.h"
#include "util.h"
#include "xwstream.h"
#include "memstream.h"
#define cEND 0x65454e44
#ifndef XWFEATURE_STANDALONE_ONLY
typedef struct MsgQueueElem {
struct MsgQueueElem* next;
XP_U8* msg;
XP_U16 len;
XP_U16 channelNo;
MsgID msgID;
} MsgQueueElem;
typedef struct AddressRecord {
struct AddressRecord* next;
CommsAddrRec addr;
#ifdef DEBUG
XP_U16 lastACK;
XP_U16 nUniqueBytes;
#endif
MsgID nextMsgID; /* on a per-channel basis */
MsgID lastMsgReceived; /* on a per-channel basis */
XP_PlayerAddr channelNo;
} AddressRecord;
struct CommsCtxt {
XW_UtilCtxt* util;
XP_U32 connID; /* 0 means ignore; otherwise must match */
XP_U16 nextChannelNo;
AddressRecord* recs; /* return addresses */
TransportSend sendproc;
void* sendClosure;
MsgQueueElem* msgQueueHead;
MsgQueueElem* msgQueueTail;
XP_U16 queueLen;
/* added to stream format. Must deal with format change in saved
games. PENDING */
XP_U16 listenPort;
CommsAddrRec addr;
XP_Bool isServer;
#ifdef DEBUG
XP_U16 nUniqueBytes;
#endif
MPSLOT
};
/****************************************************************************
* prototypes
****************************************************************************/
static AddressRecord* rememberChannelAddress( CommsCtxt* comms,
XP_PlayerAddr channelNo,
CommsAddrRec* addr );
static XP_Bool channelToAddress( CommsCtxt* comms, XP_PlayerAddr channelNo,
CommsAddrRec** addr );
static AddressRecord* getRecordFor( CommsCtxt* comms,
XP_PlayerAddr channelNo );
static XP_S16 sendMsg( CommsCtxt* comms, MsgQueueElem* elem );
static void addToQueue( CommsCtxt* comms, MsgQueueElem* newMsgElem );
static XP_U16 countAddrRecs( CommsCtxt* comms );
/****************************************************************************
* implementation
****************************************************************************/
CommsCtxt*
comms_make( MPFORMAL XW_UtilCtxt* util, XP_Bool isServer,
TransportSend sendproc, void* closure )
{
CommsCtxt* result = (CommsCtxt*)XP_MALLOC( mpool, sizeof(*result) );
XP_MEMSET( result, 0, sizeof(*result) );
MPASSIGN(result->mpool, mpool);
result->isServer = isServer;
result->sendproc = sendproc;
result->sendClosure = closure;
result->util = util;
#ifdef BEYOND_IR
/* default values; default is still IR where there's a choice */
result->addr.conType = COMMS_CONN_IR;
result->addr.u.ip.ipAddr = 0L; /* force 'em to set it */
result->addr.u.ip.port = 6000;
result->listenPort = 6001;
#endif
return result;
} /* comms_make */
static void
cleanupInternal( CommsCtxt* comms )
{
MsgQueueElem* msg;
MsgQueueElem* next;
for ( msg = comms->msgQueueHead; !!msg; msg = next ) {
next = msg->next;
XP_FREE( comms->mpool, msg->msg );
XP_FREE( comms->mpool, msg );
}
comms->queueLen = 0;
comms->msgQueueHead = comms->msgQueueTail = (MsgQueueElem*)NULL;
} /* cleanupInternal */
static void
cleanupAddrRecs( CommsCtxt* comms )
{
AddressRecord* recs;
AddressRecord* next;
for ( recs = comms->recs; !!recs; recs = next ) {
next = recs->next;
XP_FREE( comms->mpool, recs );
}
comms->recs = (AddressRecord*)NULL;
} /* cleanupAddrRecs */
void
comms_reset( CommsCtxt* comms, XP_Bool isServer )
{
cleanupInternal( comms );
comms->isServer = isServer;
cleanupAddrRecs( comms );
comms->nextChannelNo = 0;
comms->connID = CONN_ID_NONE;
} /* comms_reset */
void
comms_destroy( CommsCtxt* comms )
{
cleanupInternal( comms );
cleanupAddrRecs( comms );
XP_FREE( comms->mpool, comms );
} /* comms_destroy */
void
comms_setConnID( CommsCtxt* comms, XP_U32 connID )
{
comms->connID = connID;
XP_STATUSF( "set connID to %ld", connID );
} /* comms_setConnID */
CommsCtxt*
comms_makeFromStream( MPFORMAL XWStreamCtxt* stream, XW_UtilCtxt* util,
TransportSend sendproc, void* closure )
{
CommsCtxt* comms;
XP_Bool isServer;
XP_U16 nAddrRecs;
AddressRecord** prevsAddrNext;
MsgQueueElem** prevsQueueNext;
short i;
isServer = stream_getU8( stream );
comms = comms_make( MPPARM(mpool) util, isServer, sendproc, closure );
comms->connID = stream_getU32( stream );
comms->nextChannelNo = stream_getU16( stream );
#ifdef DEBUG
comms->nUniqueBytes = stream_getU16( stream );
#endif
comms->queueLen = stream_getU8( stream );
nAddrRecs = stream_getU8( stream );
prevsAddrNext = &comms->recs;
for ( i = 0; i < nAddrRecs; ++i ) {
AddressRecord* rec = (AddressRecord*)XP_MALLOC( mpool, sizeof(*rec));
XP_MEMSET( rec, 0, sizeof(*rec) );
stream_getBytes( stream, &rec->addr, sizeof(rec->addr) );
rec->nextMsgID = stream_getU16( stream );
rec->lastMsgReceived = stream_getU16( stream );
rec->channelNo = stream_getU16( stream );
#ifdef DEBUG
rec->lastACK = stream_getU16( stream );
rec->nUniqueBytes = stream_getU16( stream );
#endif
rec->next = (AddressRecord*)NULL;
*prevsAddrNext = rec;
prevsAddrNext = &rec->next;
}
prevsQueueNext = &comms->msgQueueHead;
for ( i = 0; i < comms->queueLen; ++i ) {
MsgQueueElem* msg = (MsgQueueElem*)XP_MALLOC( mpool, sizeof(*msg) );
msg->channelNo = stream_getU16( stream );
msg->msgID = stream_getU32( stream );
msg->len = stream_getU16( stream );
msg->msg = (XP_U8*)XP_MALLOC( mpool, msg->len );
stream_getBytes( stream, msg->msg, msg->len );
msg->next = (MsgQueueElem*)NULL;
*prevsQueueNext = comms->msgQueueTail = msg;
comms->msgQueueTail = msg;
prevsQueueNext = &msg->next;
}
#ifdef BEYOND_IR
comms->addr.conType = stream_getBits( stream, 3 );
comms->addr.u.ip.ipAddr = stream_getU32( stream );
comms->addr.u.ip.port = stream_getU16( stream );
comms->listenPort = stream_getU16( stream );
/* tell client about the port */
util_listenPortChange( util, comms->listenPort );
#endif
XP_ASSERT( stream_getU32( stream ) == cEND );
return comms;
} /* comms_makeFromStream */
void
comms_writeToStream( CommsCtxt* comms, XWStreamCtxt* stream )
{
XP_U16 nAddrRecs;
AddressRecord* rec;
MsgQueueElem* msg;
stream_putU8( stream, (XP_U8)comms->isServer );
stream_putU32( stream, comms->connID );
stream_putU16( stream, comms->nextChannelNo );
#ifdef DEBUG
stream_putU16( stream, comms->nUniqueBytes );
#endif
XP_ASSERT( comms->queueLen <= 255 );
stream_putU8( stream, (XP_U8)comms->queueLen );
nAddrRecs = countAddrRecs(comms);
stream_putU8( stream, (XP_U8)nAddrRecs );
for ( rec = comms->recs; !!rec; rec = rec->next ) {
stream_putBytes( stream, &rec->addr, sizeof(rec->addr) );
stream_putU16( stream, (XP_U16)rec->nextMsgID );
stream_putU16( stream, (XP_U16)rec->lastMsgReceived );
stream_putU16( stream, rec->channelNo );
#ifdef DEBUG
stream_putU16( stream, rec->lastACK );
stream_putU16( stream, rec->nUniqueBytes );
#endif
}
for ( msg = comms->msgQueueHead; !!msg; msg = msg->next ) {
stream_putU16( stream, msg->channelNo );
stream_putU32( stream, msg->msgID );
stream_putU16( stream, msg->len );
stream_putBytes( stream, msg->msg, msg->len );
}
#ifdef BEYOND_IR
stream_putBits( stream, 3, comms->addr.conType );
stream_putU32( stream, comms->addr.u.ip.ipAddr );
stream_putU16( stream, comms->addr.u.ip.port );
stream_putU16( stream, comms->listenPort );
#endif
#ifdef DEBUG
stream_putU32( stream, cEND );
#endif
} /* comms_writeToStream */
void
comms_getAddr( CommsCtxt* comms, CommsAddrRec* addr, XP_U16* listenPort )
{
XP_MEMCPY( addr, &comms->addr, sizeof(*addr) );
*listenPort = comms->listenPort;
} /* comms_getAddr */
void
comms_setAddr( CommsCtxt* comms, CommsAddrRec* addr, XP_U16 listenPort )
{
XP_MEMCPY( &comms->addr, addr, sizeof(comms->addr) );
comms->listenPort = listenPort;
#ifdef BEYOND_IR
util_listenPortChange( comms->util, listenPort );
#endif
} /* comms_setAddr */
CommsConnType
comms_getConType( CommsCtxt* comms )
{
return comms->addr.conType;
} /* comms_getConType */
/* Send a message using the sequentially next MsgID. Save the message so
* resend can work. */
XP_S16
comms_send( CommsCtxt* comms, CommsConnType conType, XWStreamCtxt* stream )
{
XP_U16 streamSize = stream_getSize( stream );
XP_U16 headerLen;
XP_PlayerAddr channelNo = stream_getAddress( stream );
AddressRecord* rec = getRecordFor( comms, channelNo );
MsgID msgID = (!!rec)? ++rec->nextMsgID : 0;
MsgID lastMsgRcd = (!!rec)? rec->lastMsgReceived : 0;
MsgQueueElem* newMsgElem;
XWStreamCtxt* msgStream;
XP_DEBUGF( "assigning msgID of %ld on chnl %d", msgID, channelNo );
#ifdef DEBUG
if ( !!rec ) {
rec->nUniqueBytes += streamSize;
} else {
comms->nUniqueBytes += streamSize;
}
#endif
newMsgElem = (MsgQueueElem*)XP_MALLOC( comms->mpool,
sizeof( *newMsgElem ) );
newMsgElem->channelNo = channelNo;
newMsgElem->msgID = msgID;
msgStream = mem_stream_make( MPPARM(comms->mpool)
util_getVTManager(comms->util),
NULL, 0,
(MemStreamCloseCallback)NULL );
stream_open( msgStream );
stream_putU32( msgStream, comms->connID );
#ifdef BEYOND_IR
if ( conType == COMMS_CONN_IP ) {
stream_putU16( msgStream, comms->listenPort );
XP_LOGF( "wrote return port to stream: %d", comms->listenPort );
}
#endif
stream_putU16( msgStream, channelNo );
stream_putU32( msgStream, msgID );
stream_putU32( msgStream, lastMsgRcd );
headerLen = stream_getSize( msgStream );
newMsgElem->len = streamSize + headerLen;
newMsgElem->msg = (XP_U8*)XP_MALLOC( comms->mpool, newMsgElem->len );
stream_getBytes( msgStream, newMsgElem->msg, headerLen );
stream_destroy( msgStream );
stream_getBytes( stream, newMsgElem->msg + headerLen, streamSize );
addToQueue( comms, newMsgElem );
return sendMsg( comms, newMsgElem );
} /* comms_send */
/* Add new message to the end of the list. The list needs to be kept in order
* by ascending msgIDs within each channel since if there's a resend that's
* the order in which they need to be sent.
*/
static void
addToQueue( CommsCtxt* comms, MsgQueueElem* newMsgElem )
{
newMsgElem->next = (MsgQueueElem*)NULL;
if ( !comms->msgQueueHead ) {
comms->msgQueueHead = comms->msgQueueTail = newMsgElem;
XP_ASSERT( comms->queueLen == 0 );
comms->queueLen = 1;
} else {
XP_ASSERT( !!comms->msgQueueTail );
comms->msgQueueTail->next = newMsgElem;
comms->msgQueueTail = newMsgElem;
XP_ASSERT( comms->queueLen > 0 );
++comms->queueLen;
}
XP_STATUSF( "addToQueue: queueLen now %d", comms->queueLen );
} /* addToQueue */
#ifdef DEBUG
static void
printQueue( CommsCtxt* comms )
{
MsgQueueElem* elem;
short i;
for ( elem = comms->msgQueueHead, i = 0; i < comms->queueLen;
elem = elem->next, ++i ) {
XP_STATUSF( "\t%d: channel: %d; msgID: %ld",
i+1, elem->channelNo, elem->msgID );
}
}
#else
#define printQueue(foo)
#endif
/* We've received on some channel a message with a certain ID. This means
* that all messages sent on that channel with lower IDs have been received
* and can be removed from our queue.
*/
static void
removeFromQueue( CommsCtxt* comms, XP_PlayerAddr channelNo, MsgID msgID )
{
MsgQueueElem* elem;
MsgQueueElem* prev;
XP_STATUSF( "looking to remove msgs prior or equal to %ld for channel %d "
"(queue len now %d)",
msgID, channelNo, comms->queueLen );
for ( prev = (MsgQueueElem*)NULL, elem = comms->msgQueueHead;
!!elem; prev = elem, elem = elem->next ) {
/* remove the 0-channel message if we've established a channel number.
Only clients should have any 0-channel messages in the queue, and
receiving something from the server is an implicit ACK */
if ( elem->channelNo == 0 && channelNo != 0 ) {
XP_ASSERT( !comms->isServer ); /* I've seen this fail once */
XP_ASSERT( elem->msgID == 0 ); /* will the check below pass? */
} else if ( elem->channelNo != channelNo ) {
continue;
}
if ( elem->msgID <= msgID ) {
if ( !prev ) { /* it's the first element */
comms->msgQueueHead = elem->next;
prev = comms->msgQueueHead; /* so elem=prev below will work */
} else {
prev->next = elem->next;
}
if ( comms->msgQueueTail == elem ) {
comms->msgQueueTail = prev;
}
XP_FREE( comms->mpool, elem->msg );
XP_FREE( comms->mpool, elem );
elem = prev;
--comms->queueLen;
if ( !elem ) {
break;
}
}
}
XP_STATUSF( "removeFromQueue: queueLen now %d", comms->queueLen );
XP_ASSERT( comms->queueLen > 0 || comms->msgQueueHead == NULL );
printQueue( comms );
} /* removeFromQueue */
static XP_S16
sendMsg( CommsCtxt* comms, MsgQueueElem* elem )
{
XP_S16 result;
XP_PlayerAddr channelNo;
CommsAddrRec* addr;
channelNo = elem->channelNo;
if ( !channelToAddress( comms, channelNo, &addr ) ) {
addr = NULL;
}
XP_ASSERT( !!comms->sendproc );
result = (*comms->sendproc)( elem->msg, elem->len, addr,
comms->sendClosure );
return result;
} /* sendMsg */
XP_S16
comms_resendAll( CommsCtxt* comms )
{
MsgQueueElem* msg;
XP_S16 result = 0;
for ( msg = comms->msgQueueHead; !!msg; msg = msg->next ) {
XP_S16 oneResult = sendMsg( comms, msg );
if ( result == 0 && oneResult != 0 ) {
result = oneResult;
}
XP_STATUSF( "resend: msgid=%ld; rslt=%d", msg->msgID, oneResult );
}
return result;
} /* comms_resend */
/* read a raw buffer into a stream, stripping off the headers and keeping
* any necessary stats.
*
* Keep track of return addresses by channel. If the message's channel number
* is 0, assign a new channel number and associate an address with it.
* Otherwise update the address, which may have changed since we last heard
* from this channel.
*
* There may be messages that are only about the comms 'protocol', that
* contain nothing to be passed to the server. In that case, return false
* indicating the caller that all processing is finished.
*
* conType tells both how to interpret the addr and whether to expect any
* special fields in the message itself. In the IP case, for example, the
* port component of a return address is in the message but the IP address
* component will be passed in.
*/
XP_Bool
comms_checkIncommingStream( CommsCtxt* comms, XWStreamCtxt* stream,
CommsAddrRec* addr )
{
XP_U16 channelNo;
XP_U32 connID;
MsgID msgID;
MsgID lastMsgRcd;
XP_Bool validMessage = XP_TRUE;
AddressRecord* recs = (AddressRecord*)NULL;
connID = stream_getU32( stream );
XP_STATUSF( "read connID of %ld", connID );
#ifdef BEYOND_IR
if ( addr->conType == COMMS_CONN_IP ) {
addr->u.ip.port = stream_getU16( stream );
XP_LOGF( "read return port from stream: %d", addr->u.ip.port );
}
#endif
if ( comms->connID == connID || comms->connID == CONN_ID_NONE ) {
channelNo = stream_getU16( stream );
msgID = stream_getU32( stream );
lastMsgRcd = stream_getU32( stream );
XP_DEBUGF( "rcd: msg %ld on chnl %d", msgID, channelNo );
removeFromQueue( comms, channelNo, lastMsgRcd );
if ( channelNo == 0 ) {
XP_ASSERT( comms->isServer );
XP_ASSERT( msgID == 0 );
channelNo = ++comms->nextChannelNo;
XP_STATUSF( "assigning channelNo=%d", channelNo );
} else {
recs = getRecordFor( comms, channelNo );
/* messageID for an incomming message should be one greater than
* the id most recently used for that channel. */
if ( !!recs && (msgID != recs->lastMsgReceived + 1) ) {
XP_DEBUGF( "unex msgID %ld (expt %ld)",
msgID, recs->lastMsgReceived+1 );
validMessage = XP_FALSE;
}
#ifdef DEBUG
if ( !!recs ) {
XP_ASSERT( lastMsgRcd < 0x0000FFFF );
recs->lastACK = (XP_U16)lastMsgRcd;
}
#endif
}
if ( validMessage ) {
recs = rememberChannelAddress( comms, channelNo, addr );
stream_setAddress( stream, channelNo );
if ( !!recs ) {
recs->lastMsgReceived = msgID;
}
XP_STATUSF( "set channel %d's lastMsgReceived to %ld",
channelNo, msgID );
}
} else {
XP_STATUSF( "refusing non-matching connID; got %ld, wanted %ld",
connID, comms->connID );
}
return validMessage;
} /* comms_checkIncommingStream */
#ifdef DEBUG
void
comms_getStats( CommsCtxt* comms, XWStreamCtxt* stream )
{
XP_UCHAR buf[100];
AddressRecord* rec;
XP_SNPRINTF( (XP_UCHAR*)buf, sizeof(buf), (XP_UCHAR*)"msg queue len: %d\n",
comms->queueLen );
stream_putBytes( stream, buf, (XP_U16)XP_STRLEN( buf ) );
XP_SNPRINTF( (XP_UCHAR*)buf, sizeof(buf),
(XP_UCHAR*)"channel-less bytes sent: %d\n",
comms->nUniqueBytes );
stream_putBytes( stream, buf, (XP_U16)XP_STRLEN( buf ) );
for ( rec = comms->recs; !!rec; rec = rec->next ) {
XP_SNPRINTF( (XP_UCHAR*)buf, sizeof(buf),
(XP_UCHAR*)" Stats for channel: %d\n",
rec->channelNo );
stream_putBytes( stream, buf, (XP_U16)XP_STRLEN( buf ) );
XP_SNPRINTF( (XP_UCHAR*)buf, sizeof(buf),
(XP_UCHAR*)"Last msg sent: %ld\n",
rec->nextMsgID );
stream_putBytes( stream, buf, (XP_U16)XP_STRLEN( buf ) );
XP_SNPRINTF( (XP_UCHAR*)buf, sizeof(buf),
(XP_UCHAR*)"Unique bytes sent: %d\n",
rec->nUniqueBytes );
stream_putBytes( stream, buf, (XP_U16)XP_STRLEN( buf ) );
XP_SNPRINTF( (XP_UCHAR*)buf, sizeof(buf),
(XP_UCHAR*)"Last message acknowledged: %d\n",
rec->lastACK);
stream_putBytes( stream, buf, (XP_U16)XP_STRLEN( buf ) );
}
} /* comms_getStats */
#endif
static AddressRecord*
rememberChannelAddress( CommsCtxt* comms, XP_PlayerAddr channelNo,
CommsAddrRec* addr )
{
AddressRecord* recs = NULL;
if ( addr != NULL ) {
recs = getRecordFor( comms, channelNo );
if ( !!recs ) {
/* Looks as if this will overwrite the address each time a new
message comes in. I *guess* that's right... */
XP_MEMCPY( &recs->addr, addr, sizeof(recs->addr) );
} else {
/* not found; add a new entry */
recs = (AddressRecord*)XP_MALLOC( comms->mpool, sizeof(*recs) );
recs->nextMsgID = 0;
recs->channelNo = channelNo;
XP_MEMCPY( &recs->addr, addr, sizeof(recs->addr) );
#ifdef DEBUG
recs->nUniqueBytes = 0;
#endif
recs->next = comms->recs;
comms->recs = recs;
}
}
return recs;
} /* rememberChannelAddress */
static XP_Bool
channelToAddress( CommsCtxt* comms, XP_PlayerAddr channelNo,
CommsAddrRec** addr )
{
AddressRecord* recs = getRecordFor( comms, channelNo );
if ( !!recs ) {
*addr = &recs->addr;
return XP_TRUE;
} else {
return XP_FALSE;
}
} /* channelToAddress */
static AddressRecord*
getRecordFor( CommsCtxt* comms, XP_PlayerAddr channelNo )
{
AddressRecord* recs;
if ( channelNo == CHANNEL_NONE ) {
return (AddressRecord*)NULL;
}
for ( recs = comms->recs; !!recs; recs = recs->next ) {
if ( recs->channelNo == channelNo ) {
return recs;
}
}
return (AddressRecord*)NULL;
} /* getRecordFor */
static XP_U16
countAddrRecs( CommsCtxt* comms )
{
short count = 0;
AddressRecord* recs;
for ( recs = comms->recs; !!recs; recs = recs->next ) {
++count;
}
return count;
} /* countAddrRecs */
#endif /* #ifndef XWFEATURE_STANDALONE_ONLY */

93
xwords4/common/comms.h Normal file
View file

@ -0,0 +1,93 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 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 _COMMS_H_
#define _COMMS_H_
#include "comtypes.h"
#include "mempool.h"
#define CHANNEL_NONE ((XP_PlayerAddr)0)
#define CONN_ID_NONE 0L
typedef XP_U32 MsgID;
typedef enum {
COMMS_CONN_UNUSED, /* I want errors on uninited case */
COMMS_CONN_IR,
COMMS_CONN_IP,
LAST_____FOO
} CommsConnType;
typedef struct CommsAddrRec {
CommsConnType conType;
union {
struct {
XP_U32 ipAddr;
XP_U16 port; /* return port, not sent-from */
} ip;
struct {
/* nothing? */
XP_UCHAR foo; /* wince doesn't like nothing here */
} ir;
} u;
} CommsAddrRec;
typedef XP_S16 (*TransportSend)( XP_U8* buf, XP_U16 len,
CommsAddrRec* addr,
void* closure );
CommsCtxt* comms_make( MPFORMAL XW_UtilCtxt* util,
XP_Bool isServer, TransportSend sendproc,
void* closure );
void comms_reset( CommsCtxt* comms, XP_Bool isServer );
void comms_destroy( CommsCtxt* comms );
void comms_setConnID( CommsCtxt* comms, XP_U32 connID );
void comms_getAddr( CommsCtxt* comms, CommsAddrRec* addr, XP_U16* listenPort );
void comms_setAddr( CommsCtxt* comms, CommsAddrRec* addr, XP_U16 listenPort );
CommsConnType comms_getConType( CommsCtxt* comms );
CommsCtxt* comms_makeFromStream( MPFORMAL XWStreamCtxt* stream,
XW_UtilCtxt* util, TransportSend sendproc,
void* closure );
void comms_writeToStream( CommsCtxt* comms, XWStreamCtxt* stream );
/* void comms_setDefaultTarget( CommsCtxt* comms, char* hostName, */
/* short hostPort ); */
XP_S16 comms_send( CommsCtxt* comms, CommsConnType conType,
XWStreamCtxt* stream );
XP_S16 comms_resendAll( CommsCtxt* comms );
XP_Bool comms_checkIncommingStream( CommsCtxt* comms, XWStreamCtxt* stream,
CommsAddrRec* addr );
# ifdef DEBUG
void comms_getStats( CommsCtxt* comms, XWStreamCtxt* stream );
# endif
#endif /* _COMMS_H_ */

111
xwords4/common/comtypes.h Normal file
View file

@ -0,0 +1,111 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 1997 - 2000 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 _COMTYPES_H_
#define _COMTYPES_H_
#include "xptypes.h"
typedef struct XP_Rect {
XP_S16 left;
XP_S16 top;
XP_S16 width;
XP_S16 height;
} XP_Rect;
typedef XP_U16 CellTile;
typedef XP_U8 Tile;
typedef void* XP_Bitmap;
/* I'm going to try putting all forward "class" decls in the same file */
typedef struct BoardCtxt BoardCtxt;
typedef struct CommMgrCtxt CommMgrCtxt;
typedef struct DictionaryCtxt DictionaryCtxt;
typedef struct DrawCtx DrawCtx;
typedef struct EngineCtxt EngineCtxt;
typedef struct ModelCtxt ModelCtxt;
typedef struct CommsCtxt CommsCtxt;
typedef struct PlayerSocket PlayerSocket;
typedef struct ScoreBdContext ScoreBdContext;
typedef struct ServerCtxt ServerCtxt;
typedef struct XWStreamCtxt XWStreamCtxt;
typedef struct TrayContext TrayContext;
typedef struct PoolContext PoolContext;
typedef struct XW_UtilCtxt XW_UtilCtxt;
typedef XP_U16 XP_PlayerAddr;
typedef enum {
TIMER_PENDOWN = 1, /* ARM doesn't like ids of 0... */
TIMER_TIMERTICK
} XWTimerReason;
#define MAX_NUM_PLAYERS 4
#define PLAYERNUM_NBITS 2
#define NPLAYERS_NBITS 3
#define EMPTIED_TRAY_BONUS 50
/* I need a way to communiate prefs to common/ code. For now, though, I'll
* leave storage of these values up to the platforms. First, because I don't
* want to deal with versioning in the common code. Second, becuase they
* already have the notion of per-game and all-game prefs.
*/
typedef struct CommonPrefs {
XP_Bool showBoardArrow; /* applies to all games */
XP_Bool showRobotScores; /* applies to all games */
} CommonPrefs;
/* used for all vtables */
#define SET_VTABLE_ENTRY( vt, name, prefix ) \
(vt)->m_##name = prefix##_##name
#ifdef DEBUG
# define DEBUG_ASSIGN(a,b) (a) = (b)
#else
# define DEBUG_ASSIGN(a,b)
#endif
#ifdef MEM_DEBUG
# define XP_MALLOC(pool,nbytes) \
mpool_alloc((pool),(nbytes),__FILE__,__LINE__)
# define XP_REALLOC(pool,p,s) mpool_realloc((pool),(p),(s))
# define XP_FREE(pool,p) mpool_free((pool), (p), __FILE__, __LINE__)
# define MPFORMAL_NOCOMMA MemPoolCtx* mpool
# define MPFORMAL MPFORMAL_NOCOMMA,
# define MPSLOT MPFORMAL_NOCOMMA;
# define MPPARM_NOCOMMA(p) (p)
# define MPPARM(p) MPPARM_NOCOMMA(p),
# define MPASSIGN(slot,val) (slot)=(val)
#else
# define MPFORMAL_NOCOMMA
# define MPFORMAL
# define MPSLOT
# define MPPARM_NOCOMMA(p)
# define MPPARM(p)
# define MPASSIGN(slot,val)
#endif
#endif

70
xwords4/common/config.mk Normal file
View file

@ -0,0 +1,70 @@
# -*- mode: makefile -*-
# Copyright 2002 by Eric House
#
# 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.
INCLUDES += -I../common -I./
COMMONDIR=../common
COMMONOBJDIR=../common/$(PLATFORM)
COMMONSRC = \
$(COMMONDIR)/board.c \
$(COMMONDIR)/tray.c \
$(COMMONDIR)/draw.c \
$(COMMONDIR)/model.c \
$(COMMONDIR)/mscore.c \
$(COMMONDIR)/server.c \
$(COMMONDIR)/pool.c \
$(COMMONDIR)/game.c \
$(COMMONDIR)/dictnry.c \
$(COMMONDIR)/engine.c \
$(COMMONDIR)/memstream.c \
$(COMMONDIR)/comms.c \
$(COMMONDIR)/mempool.c \
$(COMMONDIR)/movestak.c \
$(COMMONDIR)/strutils.c \
$(COMMONDIR)/vtabmgr.c \
# PENDING: define this in terms of above!!!
COMMON1 = \
$(COMMONOBJDIR)/board.o \
$(COMMONOBJDIR)/tray.o \
$(COMMONOBJDIR)/draw.o \
$(COMMONOBJDIR)/model.o \
COMMON2 = \
$(COMMONOBJDIR)/mscore.o \
$(COMMONOBJDIR)/server.o \
$(COMMONOBJDIR)/pool.o \
COMMON3 = \
$(COMMONOBJDIR)/game.o \
$(COMMONOBJDIR)/dictnry.o \
$(COMMONOBJDIR)/engine.o \
COMMON4 = \
$(COMMONOBJDIR)/memstream.o \
$(COMMONOBJDIR)/comms.o \
$(COMMONOBJDIR)/mempool.o \
COMMON5 = \
$(COMMONOBJDIR)/movestak.o \
$(COMMONOBJDIR)/strutils.o \
$(COMMONOBJDIR)/vtabmgr.o \
COMMONOBJ = $(COMMON1) $(COMMON2) $(COMMON3) $(COMMON4) $(COMMON5)

75
xwords4/common/dawg.h Normal file
View file

@ -0,0 +1,75 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4;-*- */
/*
* Copyright 1998-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 _DAWG_H_
#define _DAWG_H_
#include "xptypes.h"
/*
* the bits field has five bits for the character (0-based rather than
* 'a'-based, of course; one bit each indicating whether the edge may
* be terminal and whether it's the last edge of a sub-array; and a final
* bit that's overflow from the highByte field allowing indices to be in
* the range 0-(2^^17)-1
*/
#define LETTERMASK 0x1f
#define ACCEPTINGMASK 0x20
#define LASTEDGEMASK 0x40
#define LASTBITMASK 0x80
typedef struct array_edge_old {
XP_U8 highByte;
XP_U8 lowByte;
XP_U8 bits;
} array_edge_old;
typedef struct array_edge_new {
array_edge_old o;
XP_U8 moreBits;
} array_edge_new;
typedef XP_U8 array_edge;
/* This thing exists only in current xwords dicts (on PalmOS); not sure if I
* should do away with it.
*/
typedef struct dawg_header {
unsigned long numWords;
unsigned char firstEdgeRecNum;
unsigned char charTableRecNum;
unsigned char valTableRecNum;
unsigned char reserved[3]; /* worst case this points to a new resource */
} dawg_header;
/* Part of xwords3 dictionaries on PalmOS */
typedef struct Xloc_header {
unsigned char langCodeFlags; /* can't do bitfields; gcc for pilot and x86
seem to generate different code */
unsigned char padding; /* ptrs to the shorts in Xloc_specialEntry
will otherwise be odd */
} Xloc_header;
typedef struct Xloc_specialEntry {
unsigned char textVersion[4]; /* string can be up to 3 chars long */
short hasLarge;
short hasSmall;
} Xloc_specialEntry;
#endif /* _DAWG_H_ */

482
xwords4/common/dictnry.c Normal file
View file

@ -0,0 +1,482 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 1997-2000 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 USE_STDIO
# include <stdio.h>
# include <stdlib.h>
#endif
#include "comtypes.h"
#include "dictnryp.h"
#include "xwstream.h"
#include "strutils.h"
#ifdef CPLUS
extern "C" {
#endif
/*****************************************************************************
*
****************************************************************************/
void
setBlankTile( DictionaryCtxt* dctx )
{
XP_U16 i;
dctx->blankTile = -1; /* no known blank */
for ( i = 0; i < dctx->nFaces; ++i ) {
if ( dctx->faces16[i] == 0 ) {
dctx->blankTile = (XP_U8)i;
XP_LOGF( "blank tile index: %d", i );
break;
}
}
} /* setBlankTile */
XP_Bool
dict_hasBlankTile( DictionaryCtxt* dict )
{
return dict->blankTile >= 0;
} /* dict_hasBlankTile */
Tile
dict_getBlankTile( DictionaryCtxt* dict )
{
XP_ASSERT( dict_hasBlankTile(dict) );
return (Tile)dict->blankTile;
} /* dict_getBlankTile */
XP_U16
dict_getTileValue( DictionaryCtxt* dict, Tile tile )
{
tile &= TILE_VALUE_MASK;
XP_ASSERT( tile < dict->nFaces );
tile *= 2;
return dict->countsAndValues[tile+1];
} /* dict_getTileValue */
static XP_UCHAR
dict_getTileChar( DictionaryCtxt* dict, Tile tile )
{
XP_ASSERT( tile < dict->nFaces );
XP_ASSERT( (dict->faces16[tile] & 0xFF00) == 0 ); /* no unicode yet */
return (XP_UCHAR)dict->faces16[tile];
} /* dict_getTileValue */
XP_U16
dict_numTiles( DictionaryCtxt* dict, Tile tile )
{
tile *= 2;
return dict->countsAndValues[tile];
} /* dict_numTiles */
XP_U16
dict_numTileFaces( DictionaryCtxt* dict )
{
return dict->nFaces;
} /* dict_numTileFaces */
XP_U16
dict_tilesToString( DictionaryCtxt* ctxt, Tile* tiles, XP_U16 nTiles,
XP_UCHAR* buf )
{
XP_UCHAR* bufIn = buf;
while ( nTiles-- ) {
Tile tile = *tiles++;
XP_UCHAR face = dict_getTileChar(ctxt, tile);
if ( IS_SPECIAL(face) ) {
XP_UCHAR* chars = ctxt->chars[(XP_U16)face];
XP_U16 len = XP_STRLEN( chars );
XP_MEMCPY( buf, chars, len );
buf += len;
} else {
XP_ASSERT ( tile != ctxt->blankTile ); /* printing blank should be
handled by specials
mechanism */
*buf++ = face;
}
}
*buf = '\0';
return buf - bufIn;
} /* dict_tilesToString */
Tile
dict_tileForString( DictionaryCtxt* dict, XP_UCHAR* key )
{
XP_U16 nFaces = dict_numTileFaces( dict );
Tile tile;
XP_Bool keyNotSpecial = XP_STRLEN((char*)key) == 1;
for ( tile = 0; tile < nFaces; ++tile ) {
XP_UCHAR face = dict_getTileChar( dict, tile );
if ( IS_SPECIAL(face) ) {
XP_UCHAR* chars = dict->chars[(XP_U16)face];
if ( 0 == XP_STRNCMP( chars, key, XP_STRLEN(chars) ) ) {
return tile;
}
} else if ( keyNotSpecial && (face == *key) ) {
return tile;
}
}
return EMPTY_TILE;
} /* dict_tileForChar */
XP_Bool
dict_tilesAreSame( DictionaryCtxt* dict1, DictionaryCtxt* dict2 )
{
XP_Bool result = XP_FALSE;
Tile i;
XP_U16 nTileFaces = dict_numTileFaces( dict1 );
if ( nTileFaces == dict_numTileFaces( dict2 ) ) {
for ( i = 0; i < nTileFaces; ++i ) {
XP_UCHAR face1, face2;
if ( dict_getTileValue( dict1, i )
!= dict_getTileValue( dict2, i ) ){
break;
}
#if 1
face1 = dict_getTileChar( dict1, i );
face2 = dict_getTileChar( dict2, i );
if ( face1 != face2 ) {
break;
}
#else
face1 = dict_getTileChar( dict1, i );
face2 = dict_getTileChar( dict2, i );
if ( IS_SPECIAL(face1) != IS_SPECIAL(face2) ) {
break;
}
if ( IS_SPECIAL(face1) ) {
XP_UCHAR* chars1 = dict1->chars[face1];
XP_UCHAR* chars2 = dict2->chars[face2];
XP_U16 len = XP_STRLEN(chars1);
if ( 0 != XP_STRNCMP( chars1, chars2, len ) ) {
break;
}
} else if ( face1 != face2 ) {
break;
}
#endif
if ( dict_numTiles( dict1, i ) != dict_numTiles( dict2, i ) ) {
break;
}
}
result = i == nTileFaces; /* did we get that far */
}
return result;
} /* dict_tilesAreSame */
void
dict_writeToStream( DictionaryCtxt* dict, XWStreamCtxt* stream )
{
XP_U16 maxCount = 0;
XP_U16 maxValue = 0;
XP_U16 i, nSpecials;
XP_U16 maxCountBits, maxValueBits;
stream_putBits( stream, 6, dict->nFaces );
for ( i = 0; i < dict->nFaces*2; i+=2 ) {
XP_U16 count, value;
count = dict->countsAndValues[i];
if ( maxCount < count ) {
maxCount = count;
}
value = dict->countsAndValues[i+1];
if ( maxValue < value ) {
maxValue = value;
}
}
maxCountBits = bitsForMax( maxCount );
maxValueBits = bitsForMax( maxValue );
stream_putBits( stream, 3, maxCountBits ); /* won't be bigger than 5 */
stream_putBits( stream, 3, maxValueBits );
for ( i = 0; i < dict->nFaces*2; i+=2 ) {
stream_putBits( stream, maxCountBits, dict->countsAndValues[i] );
stream_putBits( stream, maxValueBits, dict->countsAndValues[i+1] );
}
for ( i = 0; i < dict->nFaces; ++i ) {
stream_putU8( stream, (XP_U8)dict->faces16[i] );
}
for ( nSpecials = i = 0; i < dict->nFaces; ++i ) {
XP_UCHAR face = dict_getTileChar( dict, (Tile)i );
if ( IS_SPECIAL( face ) ) {
stringToStream( stream, dict->chars[nSpecials++] );
}
}
} /* dict_writeToStream */
static void
freeSpecials( DictionaryCtxt* dict )
{
Tile t;
XP_U16 nSpecials;
for ( nSpecials = t = 0; t < dict->nFaces; ++t ) {
XP_UCHAR face = dict_getTileChar( dict, t );
if ( IS_SPECIAL( face ) ) {
XP_ASSERT( !!dict->chars[nSpecials] );
XP_FREE( dict->mpool, dict->chars[nSpecials] );
if ( !!dict->bitmaps[nSpecials].largeBM ) {
XP_FREE( dict->mpool, dict->bitmaps[nSpecials].largeBM );
}
if ( !!dict->bitmaps[nSpecials].smallBM ) {
XP_FREE( dict->mpool, dict->bitmaps[nSpecials].smallBM );
}
++nSpecials;
}
}
if ( nSpecials > 0 ) {
XP_FREE( dict->mpool, dict->chars );
XP_FREE( dict->mpool, dict->bitmaps );
}
} /* freeSpecials */
static void
common_destructor( DictionaryCtxt* dict )
{
freeSpecials( dict );
XP_FREE( dict->mpool, dict->countsAndValues );
XP_FREE( dict->mpool, dict->faces16 );
XP_FREE( dict->mpool, dict );
} /* dict */
void
dict_loadFromStream( DictionaryCtxt* dict, XWStreamCtxt* stream )
{
XP_U8 nFaces;
XP_U16 maxCountBits, maxValueBits;
XP_U16 i, nSpecials;
XP_UCHAR* localTexts[32];
XP_ASSERT( !dict->destructor );
dict->destructor = common_destructor;
nFaces = (XP_U8)stream_getBits( stream, 6 );
maxCountBits = (XP_U16)stream_getBits( stream, 3 );
maxValueBits = (XP_U16)stream_getBits( stream, 3 );
dict->nFaces = nFaces;
dict->countsAndValues =
(XP_U8*)XP_MALLOC( dict->mpool,
sizeof(dict->countsAndValues[0]) * nFaces * 2 );
for ( i = 0; i < dict->nFaces*2; i+=2 ) {
dict->countsAndValues[i] = (XP_U8)stream_getBits( stream,
maxCountBits );
dict->countsAndValues[i+1] = (XP_U8)stream_getBits( stream,
maxValueBits );
}
dict->faces16 = (XP_CHAR16*)XP_MALLOC( dict->mpool,
sizeof(dict->faces16[0]) * nFaces );
for ( i = 0; i < dict->nFaces; ++i ) {
dict->faces16[i] = (XP_CHAR16)stream_getU8( stream );
}
for ( nSpecials = i = 0; i < nFaces; ++i ) {
XP_UCHAR face = dict_getTileChar( dict, (Tile)i );
if ( IS_SPECIAL( face ) ) {
XP_UCHAR* txt = stringFromStream( MPPARM(dict->mpool) stream );
XP_ASSERT( !!txt );
localTexts[nSpecials] = txt;
++nSpecials;
}
}
if ( nSpecials > 0 ) {
dict->bitmaps =
(SpecialBitmaps*)XP_MALLOC( dict->mpool,
nSpecials * sizeof(*dict->bitmaps) );
XP_MEMSET( dict->bitmaps, 0, nSpecials * sizeof(*dict->bitmaps) );
dict->chars = (XP_UCHAR**)XP_MALLOC( dict->mpool,
nSpecials * sizeof(*dict->chars) );
XP_MEMCPY(dict->chars, localTexts, nSpecials * sizeof(*dict->chars));
}
setBlankTile( dict );
} /* dict_loadFromStream */
XP_UCHAR*
dict_getName( DictionaryCtxt* dict )
{
XP_ASSERT( !!dict );
return dict->name;
} /* dict_getName */
XP_Bool
dict_faceIsBitmap( DictionaryCtxt* dict, Tile tile )
{
XP_UCHAR face = dict_getTileChar( dict, tile );
return /* face != 0 && */IS_SPECIAL(face);
} /* dict_faceIsBitmap */
XP_Bitmap
dict_getFaceBitmap( DictionaryCtxt* dict, Tile tile, XP_Bool isLarge )
{
SpecialBitmaps* bitmaps;
XP_UCHAR face = dict_getTileChar( dict, tile );
XP_ASSERT( dict_faceIsBitmap( dict, tile ) );
XP_ASSERT( !!dict->bitmaps );
bitmaps = &dict->bitmaps[(XP_U16)face];
return isLarge? bitmaps->largeBM:bitmaps->smallBM;
} /* dict_getFaceBitmap */
#ifdef STUBBED_DICT
#define BLANK_FACE '\0'
static XP_U8 stub_english_data[] = {
/* count value face */
9, 1, 'A',
2, 3, 'B',
2, 3, 'C',
4, 2, 'D',
12, 1, 'E',
2, 4, 'F',
3, 2, 'G',
2, 4, 'H',
9, 1, 'I',
1, 8, 'J',
1, 5, 'K',
4, 1, 'L',
2, 3, 'M',
6, 1, 'N',
8, 1, 'O',
2, 3, 'P',
1, 10, 'Q',
6, 1, 'R',
4, 1, 'S',
6, 1, 'T',
4, 1, 'U',
2, 4, 'V',
2, 4, 'W',
1, 8, 'X',
2, 4, 'Y',
1, 10, 'Z',
2, 0, BLANK_FACE, /* BLANK1 */
};
void
setStubbedSpecials( DictionaryCtxt* dict )
{
dict->chars = XP_MALLOC( dict->mpool, sizeof(char*) );
dict->chars[0] = "_";
} /* setStubbedSpecials */
void
destroy_stubbed_dict( DictionaryCtxt* dict )
{
XP_FREE( dict->mpool, dict->countsAndValues );
XP_FREE( dict->mpool, dict->faces16 );
XP_FREE( dict->mpool, dict->chars );
XP_FREE( dict->mpool, dict->name );
XP_FREE( dict->mpool, dict->bitmaps );
XP_FREE( dict->mpool, dict );
} /* destroy_stubbed_dict */
DictionaryCtxt*
make_stubbed_dict( MPFORMAL_NOCOMMA )
{
DictionaryCtxt* dict = XP_MALLOC( mpool, sizeof(*dict) );
XP_U8* data = stub_english_data;
XP_U16 datasize = sizeof(stub_english_data);
XP_U16 i;
XP_MEMSET( dict, 0, sizeof(*dict) );
MPASSIGN( dict->mpool, mpool );
dict->name = copyString( MPPARM(mpool) "Stub dictionary" );
dict->nFaces = datasize/3;
dict->destructor = destroy_stubbed_dict;
dict->faces16 = XP_MALLOC( mpool,
dict->nFaces * sizeof(dict->faces16[0]) );
for ( i = 0; i < datasize/3; ++i ) {
dict->faces16[i] = (XP_CHAR16)data[(i*3)+2];
}
dict->countsAndValues = XP_MALLOC( mpool, dict->nFaces*2 );
for ( i = 0; i < datasize/3; ++i ) {
dict->countsAndValues[i*2] = data[(i*3)];
dict->countsAndValues[(i*2)+1] = data[(i*3)+1];
}
dict->bitmaps = XP_MALLOC( mpool, sizeof(SpecialBitmaps) );
dict->bitmaps->largeBM = dict->bitmaps->largeBM = NULL;
setStubbedSpecials( dict );
setBlankTile( dict );
return dict;
} /* make_subbed_dict */
#endif /* STUBBED_DICT */
#ifndef OVERRIDE_EDGE_FOR_INDEX
array_edge*
dict_edge_for_index( DictionaryCtxt* dict, XP_U32 index )
{
array_edge* result;
if ( index == 0 ) {
result = NULL;
} else {
XP_ASSERT( index < dict->numEdges );
#ifdef NODE_CAN_4
index *= dict->nodeSize;
#else
index *= 3;
#endif
result = &dict->base[index];
}
return result;
} /* dict_edge_for_index */
#endif
#ifdef CPLUS
}
#endif

154
xwords4/common/dictnry.h Normal file
View file

@ -0,0 +1,154 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 1997 - 2000 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 __DICTNRY_H__
#define __DICTNRY_H__
#include "comtypes.h"
#include "dawg.h"
#include "model.h"
#include "mempool.h"
#ifdef CPLUS
extern "C" {
#endif
#define LETTER_NONE '\0'
#define IS_SPECIAL(face) (((XP_CHAR16)(face)) < 0x0020)
typedef XP_U16 XP_CHAR16;
typedef enum {
BONUS_NONE,
BONUS_DOUBLE_LETTER,
BONUS_DOUBLE_WORD,
BONUS_TRIPLE_LETTER,
BONUS_TRIPLE_WORD,
BONUS_LAST
} XWBonusType;
typedef enum {
INTRADE_MW_TEXT = BONUS_LAST
} XWMiniTextType;
typedef struct SpecialBitmaps {
XP_Bitmap largeBM;
XP_Bitmap smallBM;
} SpecialBitmaps;
struct DictionaryCtxt {
void (*destructor)( DictionaryCtxt* dict );
array_edge* topEdge;
array_edge* base; /* the physical beginning of the dictionary; not
necessarily the entry point for search!! */
XP_UCHAR* name;
XP_CHAR16* faces16; /* 16 for unicode */
XP_U8* countsAndValues;
SpecialBitmaps* bitmaps;
XP_UCHAR** chars;
XP_U8 nFaces;
#ifdef NODE_CAN_4
XP_U8 nodeSize;
XP_U8 charSize;
#endif
XP_S8 blankTile; /* negative means there's no known blank */
#ifdef DEBUG
XP_U32 numEdges;
#endif
MPSLOT
};
/* This is the datastructure that allows access to a DAWG in a
* platform-independent way.
*/
/* typedef struct DictionaryVtable { */
/* XP_U16 (*m_getTileValue)( DictionaryCtxt* ctxt, CellTile tile ); */
/* unsigned char (*m_getTileChar)( DictionaryCtxt* ctxt, CellTile tile, */
/* XP_FontCode* fontCode ); */
/* XP_U16 (*m_numTiles)( DictionaryCtxt* ctxt, Tile tile ); */
/* XP_U16 (*m_numTileFaces)( DictionaryCtxt* ctxt ); */
/* } DictionaryVtable; */
/* struct DictionaryCtxt { */
/* DictionaryVtable* vtable; */
/* }; */
/* #define dict_getTileValue(dc,t) \ */
/* (dc)->vtable->m_getTileValue((dc),(t)) */
/* #define dict_getTileChar(dc,t,fc) \ */
/* (dc)->vtable->m_getTileChar((dc),(t),(fc)) */
/* #define dict_numTiles(dc,t) (dc)->vtable->m_numTiles((dc),(t)) */
/* #define dict_numTileFaces(dc) (dc)->vtable->m_numTileFaces(dc) */
#define dict_destroy(d) (*((d)->destructor))(d)
XP_Bool dict_tilesAreSame( DictionaryCtxt* dict1, DictionaryCtxt* dict2 );
XP_Bool dict_hasBlankTile( DictionaryCtxt* dict );
Tile dict_getBlankTile( DictionaryCtxt* dict );
XP_U16 dict_getTileValue( DictionaryCtxt* ctxt, Tile tile );
XP_U16 dict_numTiles( DictionaryCtxt* ctxt, Tile tile );
XP_U16 dict_numTileFaces( DictionaryCtxt* ctxt );
XP_U16 dict_tilesToString( DictionaryCtxt* ctxt, Tile* tiles, XP_U16 nTiles,
XP_UCHAR* buf );
XP_UCHAR* dict_getName( DictionaryCtxt* ctxt );
Tile dict_tileForString( DictionaryCtxt* dict, XP_UCHAR* key );
XP_Bool dict_faceIsBitmap( DictionaryCtxt* dict, Tile tile );
XP_Bitmap dict_getFaceBitmap( DictionaryCtxt* dict, Tile tile,
XP_Bool isLarge );
void dict_writeToStream( DictionaryCtxt* ctxt, XWStreamCtxt* stream );
void dict_loadFromStream( DictionaryCtxt* dict, XWStreamCtxt* stream );
/* These methods get "overridden" by subclasses. That is, they must be
implemented by each platform. */
array_edge* dict_edge_for_index( DictionaryCtxt* dict, XP_U32 index );
#ifdef OVERRIDE_GETTOPEDGE
/* platform code will implement this */
array_edge* dict_getTopEdge( DictionaryCtxt* dict );
#else
# define dict_getTopEdge( dict ) ((dict)->topEdge)
#endif
#ifdef STUBBED_DICT
DictionaryCtxt* make_stubbed_dict( MPFORMAL_NOCOMMA );
#endif
#ifdef CPLUS
}
#endif
#endif

36
xwords4/common/dictnryp.h Normal file
View file

@ -0,0 +1,36 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4;-*- */
/*
* Copyright 1997-2000 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 _DICTNRYP_H_
#define _DICTNRYP_H_
#include "dictnry.h"
#ifdef CPLUS
extern "C" {
#endif
void setBlankTile( DictionaryCtxt* dctx );
#ifdef CPLUS
}
#endif
#endif

370
xwords4/common/draw.c Normal file
View file

@ -0,0 +1,370 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 1997 - 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 DRAW_WITH_PRIMITIVES
#include "draw.h"
#include "xptypes.h"
static void
insetRect( XP_Rect* r, XP_S16 amt )
{
r->top += amt;
r->left += amt;
amt *= 2;
r->width -= amt;
r->height -= amt;
} /* insetRect */
static void
getRemText( XP_UCHAR* buf, XP_U16 bufSize, XP_S16 nTilesLeft )
{
if ( nTilesLeft > 0 ) {
XP_SNPRINTF( buf, bufSize, "rem: %d", nTilesLeft );
} else {
buf[0] = '\0';
}
} /* getRemText */
static void
default_draw_measureRemText( DrawCtx* dctx, XP_Rect* r,
XP_S16 nTilesLeft,
XP_U16* widthP, XP_U16* heightP )
{
XP_U16 width, height;
if ( nTilesLeft > 0 ) {
XP_UCHAR buf[20];
getRemText( buf, sizeof(buf), nTilesLeft );
draw_measureText( dctx, buf, &width, &height );
} else {
width = height = 0;
}
*widthP = width;
*heightP = height;
} /* default_draw_measureRemText */
static void
default_draw_drawRemText( DrawCtx* dctx, XP_Rect* rInner,
XP_Rect* rOuter, XP_S16 nTilesLeft )
{
XP_Rect oldClip;
XP_UCHAR buf[10];
getRemText( buf, sizeof(buf), nTilesLeft );
draw_setClip( dctx, rOuter, &oldClip );
draw_drawString( dctx, buf, rOuter->left, rOuter->top );
draw_setClip( dctx, &oldClip, NULL );
} /* default_draw_drawRemText */
static void
formatScore( XP_UCHAR* buf, XP_U16 bufSize, DrawScoreInfo* dsi )
{
XP_UCHAR remBuf[10];
XP_UCHAR* selStr;
if ( dsi->selected ) {
selStr = "*";
} else {
selStr = "";
}
if ( dsi->nTilesLeft >= 0 ) {
XP_SNPRINTF( remBuf, sizeof(remBuf), ":%d", dsi->nTilesLeft );
} else {
remBuf[0] = '\0';
}
XP_SNPRINTF( buf, bufSize, "%s%d%s%s", selStr, dsi->score,
remBuf, selStr );
} /* formatScore */
static void
default_draw_measureScoreText( DrawCtx* dctx, XP_Rect* r,
DrawScoreInfo* dsi,
XP_U16* widthP, XP_U16* heightP )
{
XP_UCHAR buf[20];
formatScore( buf, sizeof(buf), dsi );
draw_measureText( dctx, buf, widthP, heightP );
} /* default_draw_measureScoreText */
static void
default_draw_score_drawPlayer( DrawCtx* dctx,
XP_S16 playerNum, /* -1: don't use */
XP_Rect* rInner, XP_Rect* rOuter,
DrawScoreInfo* dsi )
{
XP_Rect oldClip;
XP_UCHAR buf[20];
draw_setClip( dctx, rInner, &oldClip );
draw_clearRect( dctx, rOuter );
formatScore( buf, sizeof(buf), dsi );
draw_drawString( dctx, buf, rInner->left, rInner->top );
draw_setClip( dctx, &oldClip, NULL );
} /* default_draw_score_drawPlayer */
static XP_Bool
default_draw_drawCell( DrawCtx* dctx, XP_Rect* rect,
/* at least one of these two will be null */
XP_UCHAR* text, XP_Bitmap bitmap,
XP_S16 owner, /* -1 means don't use */
XWBonusType bonus, XP_Bool isBlank,
XP_Bool highlight, XP_Bool isStar)
{
XP_Rect oldClip;
XP_Rect inset = *rect;
insetRect( &inset, 1 );
draw_setClip( dctx, rect, &oldClip );
draw_clearRect( dctx, rect );
if ( !!text && text[0] != 0 ) {
draw_drawString( dctx, text, inset.left, inset.top );
} else if ( !!bitmap ) {
draw_drawBitmap( dctx, bitmap, inset.left, inset.top );
} else if ( bonus != BONUS_NONE ) {
XP_UCHAR* bstr;
switch( bonus ) {
case BONUS_DOUBLE_LETTER:
bstr = "*";
break;
case BONUS_DOUBLE_WORD:
bstr = "%";
break;
case BONUS_TRIPLE_LETTER:
bstr = "#";
break;
case BONUS_TRIPLE_WORD:
bstr = "@";
break;
default:
XP_ASSERT(0);
break;
}
draw_drawString( dctx, bstr, inset.left, inset.top );
}
if ( highlight ) {
draw_invertRect( dctx, &inset );
}
draw_frameRect( dctx, rect );
draw_setClip( dctx, &oldClip, NULL );
return XP_TRUE;
} /* default_draw_drawCell */
static void
default_draw_drawBoardArrow( DrawCtx* dctx, XP_Rect* rect,
XWBonusType bonus, XP_Bool vert )
{
XP_Rect oldClip;
XP_UCHAR* arrow;
if ( vert ) {
arrow = "|";
} else {
arrow = "-";
}
draw_setClip( dctx, rect, &oldClip );
draw_clearRect( dctx, rect );
draw_frameRect( dctx, rect );
draw_drawString( dctx, arrow, rect->left+1, rect->top+1 );
draw_setClip( dctx, &oldClip, NULL );
} /* default_draw_drawBoardArrow */
static void
default_draw_drawTile( DrawCtx* dctx, XP_Rect* rect,
/* at least 1 of these two will be null*/
XP_UCHAR* text, XP_Bitmap bitmap,
XP_S16 val, XP_Bool highlighted )
{
XP_Rect oldClip;
XP_Rect inset = *rect;
draw_setClip( dctx, rect, &oldClip );
draw_clearRect( dctx, rect );
draw_frameRect( dctx, rect );
if ( highlighted ) {
insetRect( &inset, 1 );
draw_frameRect( dctx, &inset );
insetRect( &inset, 1 );
} else {
insetRect( &inset, 2 );
}
if ( !!text && text[0] != '\0' ) {
draw_drawString( dctx, text, inset.left, inset.top );
} else if ( !!bitmap ) {
draw_drawBitmap( dctx, bitmap, inset.left, inset.top );
}
if ( val >= 0 ) {
XP_UCHAR sbuf[4];
XP_U16 width, height;
XP_U16 x, y;
XP_SNPRINTF( sbuf, sizeof(sbuf), "%d", val );
draw_measureText( dctx, sbuf, &width, &height );
x = inset.left + inset.width - width;
y = inset.top + inset.height - height;
draw_drawString( dctx, sbuf, x, y );
}
draw_setClip( dctx, &oldClip, NULL );
} /* default_draw_drawTile */
static void
default_draw_drawTileBack( DrawCtx* dctx, XP_Rect* rect )
{
default_draw_drawTile( dctx, rect, "?", NULL, -1, XP_FALSE );
} /* default_draw_drawTileBack */
static void
default_draw_drawTrayDivider( DrawCtx* dctx, XP_Rect* rect,
XP_Bool selected)
{
XP_Rect r = *rect;
draw_clearRect( dctx, rect );
if ( r.width > 2 ) {
r.width -= 2;
r.left += 1;
}
draw_frameRect( dctx, &r );
} /* default_draw_drawTrayDivider */
static void
default_draw_score_pendingScore( DrawCtx* dctx, XP_Rect* rect,
XP_S16 score, XP_U16 playerNum )
{
XP_UCHAR buf[5];
XP_Rect oldClip;
XP_Rect r;
XP_U16 width, height;
XP_UCHAR* stxt;
draw_setClip( dctx, rect, &oldClip );
XP_MEMCPY( &r, rect, sizeof(r) );
++r.left; /* don't erase neighbor's border */
--r.width;
draw_clearRect( dctx, &r );
draw_drawString( dctx, "pts", r.left, r.top );
if ( score >= 0 ) {
XP_SNPRINTF( buf, sizeof(buf), "%d", score );
stxt = buf;
} else {
stxt = "???";
}
draw_measureText( dctx, stxt, &width, &height );
draw_drawString( dctx, stxt, r.left, r.top + r.height - height );
draw_setClip( dctx, &oldClip, NULL );
} /* default_draw_score_pendingScore */
static XP_UCHAR*
default_draw_getMiniWText( DrawCtx* dctx, XWMiniTextType textHint )
{
unsigned char* str;
switch( textHint ) {
case BONUS_DOUBLE_LETTER:
str = "Double letter"; break;
case BONUS_DOUBLE_WORD:
str = "Double word"; break;
case BONUS_TRIPLE_LETTER:
str = "Triple letter"; break;
case BONUS_TRIPLE_WORD:
str = "Triple word"; break;
case INTRADE_MW_TEXT:
str = "Trading tiles;\nclick D when done"; break;
default:
XP_ASSERT( XP_FALSE );
}
return str;
} /* default_draw_getMiniWText */
static void
default_draw_measureMiniWText( DrawCtx* dctx, XP_UCHAR* textP,
XP_U16* widthP, XP_U16* heightP )
{
draw_measureText( dctx, textP, widthP, heightP );
/* increase for frame */
*widthP += 2;
*heightP += 2;
} /* default_draw_measureMiniWText */
static void
default_draw_drawMiniWindow( DrawCtx* dctx, XP_UCHAR* text,
XP_Rect* rect, void** closure )
{
XP_Rect oldClip;
draw_setClip( dctx, rect, &oldClip );
draw_clearRect( dctx, rect );
draw_frameRect( dctx, rect );
draw_drawString( dctx, text, rect->left+1, rect->top+1 );
draw_setClip( dctx, &oldClip, NULL );
} /* default_draw_drawMiniWindow */
static void
default_draw_eraseMiniWindow( DrawCtx* dctx, XP_Rect* rect,
XP_Bool lastTime, void** closure,
XP_Bool* invalUnder )
{
*invalUnder = XP_TRUE; /* let board.c do the work */
} /* default_draw_eraseMiniWindow */
void
InitDrawDefaults( DrawCtxVTable* vtable )
{
SET_VTABLE_ENTRY( vtable, draw_measureRemText, default );
SET_VTABLE_ENTRY( vtable, draw_drawRemText, default );
SET_VTABLE_ENTRY( vtable, draw_measureScoreText, default );
SET_VTABLE_ENTRY( vtable, draw_score_drawPlayer, default );
SET_VTABLE_ENTRY( vtable, draw_drawCell, default );
SET_VTABLE_ENTRY( vtable, draw_drawBoardArrow, default );
SET_VTABLE_ENTRY( vtable, draw_drawTile, default );
SET_VTABLE_ENTRY( vtable, draw_drawTileBack, default );
SET_VTABLE_ENTRY( vtable, draw_drawTrayDivider, default );
SET_VTABLE_ENTRY( vtable, draw_score_pendingScore, default );
SET_VTABLE_ENTRY( vtable, draw_getMiniWText, default );
SET_VTABLE_ENTRY( vtable, draw_measureMiniWText, default );
SET_VTABLE_ENTRY( vtable, draw_drawMiniWindow, default );
SET_VTABLE_ENTRY( vtable, draw_eraseMiniWindow, default );
} /* InitDrawDefaults */
#endif

242
xwords4/common/draw.h Normal file
View file

@ -0,0 +1,242 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 1997 - 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 _DRAW_H_
#define _DRAW_H_
#include "comtypes.h"
#include "xptypes.h"
#include "model.h"
/* typedef struct DrawCtx DrawCtx; */
typedef struct DrawScoreInfo {
void* closure;
XP_UCHAR* name;
XP_S16 score;
XP_S16 nTilesLeft; /* < 0 means don't use */
XP_Bool isTurn;
XP_Bool selected;
XP_Bool isRemote;
XP_Bool isRobot;
} DrawScoreInfo;
typedef struct DrawCtxVTable {
#ifdef DRAW_WITH_PRIMITIVES
void (*m_draw_setClip)( DrawCtx* dctx, XP_Rect* newClip,
XP_Rect* oldClip );
void (*m_draw_frameRect)( DrawCtx* dctx, XP_Rect* rect );
void (*m_draw_invertRect)( DrawCtx* dctx, XP_Rect* rect );
void (*m_draw_drawString)( DrawCtx* dctx, XP_UCHAR* str,
XP_U16 x, XP_U16 y );
void (*m_draw_drawBitmap)( DrawCtx* dctx, XP_Bitmap bm,
XP_U16 x, XP_U16 y );
void (*m_draw_measureText)( DrawCtx* dctx, XP_UCHAR* buf,
XP_U16* widthP, XP_U16* heightP );
#endif
void (*m_draw_destroyCtxt)( DrawCtx* dctx );
void (*m_draw_boardBegin)( DrawCtx* dctx, XP_Rect* rect,
XP_Bool hasfocus );
void (*m_draw_boardFinished)( DrawCtx* dctx );
XP_Bool (*m_draw_vertScrollBoard)(DrawCtx* dctx, XP_Rect* rect,
XP_S16 dist );
void (*m_draw_trayBegin)( DrawCtx* dctx, XP_Rect* rect,
XP_U16 owner, XP_Bool hasfocus );
void (*m_draw_trayFinished)( DrawCtx* dctx );
void (*m_draw_measureRemText)( DrawCtx* dctx, XP_Rect* r,
XP_S16 nTilesLeft,
XP_U16* width, XP_U16* height );
void (*m_draw_drawRemText)(DrawCtx* dctx, XP_Rect* rInner,
XP_Rect* rOuter, XP_S16 nTilesLeft);
void (*m_draw_scoreBegin)( DrawCtx* dctx, XP_Rect* rect,
XP_U16 numPlayers, XP_Bool hasfocus );
void (*m_draw_measureScoreText)( DrawCtx* dctx, XP_Rect* r,
DrawScoreInfo* dsi,
XP_U16* width, XP_U16* height );
void (*m_draw_score_drawPlayer)( DrawCtx* dctx,
XP_S16 playerNum, /* -1: don't use */
XP_Rect* rInner, XP_Rect* rOuter,
DrawScoreInfo* dsi );
void (*m_draw_score_pendingScore)( DrawCtx* dctx, XP_Rect* rect,
XP_S16 score, XP_U16 playerNum );
void (*m_draw_scoreFinished)( DrawCtx* dctx );
void (*m_draw_drawTimer)( DrawCtx* dctx, XP_Rect* rInner, XP_Rect* rOuter,
XP_U16 player, XP_S16 secondsLeft );
XP_Bool (*m_draw_drawCell)( DrawCtx* dctx, XP_Rect* rect,
/* at least one of these two will be null */
XP_UCHAR* text, XP_Bitmap bitmap,
XP_S16 owner, /* -1 means don't use */
XWBonusType bonus, XP_Bool isBlank,
XP_Bool highlight, XP_Bool isStar);
void (*m_draw_invertCell)( DrawCtx* dctx, XP_Rect* rect );
void (*m_draw_drawTile)( DrawCtx* dctx, XP_Rect* rect,
/* at least 1 of these two will be null*/
XP_UCHAR* text, XP_Bitmap bitmap,
XP_S16 val, XP_Bool highlighted );
void (*m_draw_drawTileBack)( DrawCtx* dctx, XP_Rect* rect );
void (*m_draw_drawTrayDivider)( DrawCtx* dctx, XP_Rect* rect,
XP_Bool selected );
void (*m_draw_clearRect)( DrawCtx* dctx, XP_Rect* rect );
void (*m_draw_drawBoardArrow)( DrawCtx* dctx, XP_Rect* rect,
XWBonusType bonus, XP_Bool vert );
#ifdef KEY_SUPPORT
void (*m_draw_drawTrayCursor)( DrawCtx* dctx, XP_Rect* rect );
void (*m_draw_drawBoardCursor)( DrawCtx* dctx, XP_Rect* rect );
#endif
XP_UCHAR* (*m_draw_getMiniWText)( DrawCtx* dctx,
XWMiniTextType textHint );
void (*m_draw_measureMiniWText)( DrawCtx* dctx, XP_UCHAR* textP,
XP_U16* width, XP_U16* height );
void (*m_draw_drawMiniWindow)( DrawCtx* dctx, XP_UCHAR* text,
XP_Rect* rect, void** closure );
void (*m_draw_eraseMiniWindow)( DrawCtx* dctx, XP_Rect* rect,
XP_Bool lastTime, void** closure,
XP_Bool* invalUnder );
} DrawCtxVTable; /* */
struct DrawCtx {
DrawCtxVTable* vtable;
};
#define draw_destroyCtxt(dc) \
(dc)->vtable->m_draw_destroyCtxt(dc)
#define draw_boardBegin( dc, r, f ) \
(dc)->vtable->m_draw_boardBegin((dc), (r), (f))
#define draw_boardFinished( dc ) \
(dc)->vtable->m_draw_boardFinished(dc)
#define draw_trayBegin( dc, r, o, f ) \
(dc)->vtable->m_draw_trayBegin((dc), (r), (o), (f))
#define draw_trayFinished( dc ) \
(dc)->vtable->m_draw_trayFinished(dc)
#define draw_vertScrollBoard( dc, r, d ) \
(dc)->vtable->m_draw_vertScrollBoard((dc),(r),(d))
#define draw_scoreBegin( dc, r, t, f ) \
(dc)->vtable->m_draw_scoreBegin((dc), (r), (t), (f))
#define draw_measureRemText( dc, r, n, wp, hp ) \
(dc)->vtable->m_draw_measureRemText( (dc), (r), (n), (wp), (hp) )
#define draw_drawRemText( dc, ri, ro, n ) \
(dc)->vtable->m_draw_drawRemText( (dc), (ri), (ro), (n) )
#define draw_measureScoreText(dc,r,dsi,wp,hp) \
(dc)->vtable->m_draw_measureScoreText((dc),(r),(dsi),(wp),(hp))
#define draw_score_drawPlayer(dc, i, ri, ro, dsi) \
(dc)->vtable->m_draw_score_drawPlayer((dc),(i),(ri),(ro),(dsi))
#define draw_score_pendingScore(dc, r, s, p ) \
(dc)->vtable->m_draw_score_pendingScore((dc), (r), (s), (p))
#define draw_scoreFinished( dc ) \
(dc)->vtable->m_draw_scoreFinished(dc)
#define draw_drawTimer( dc, ri, ro, plyr, sec ) \
(dc)->vtable->m_draw_drawTimer((dc),(ri),(ro),(plyr),(sec))
/* #define draw_frameBoard( dc, rect ) \ */
/* (dc)->vtable->m_draw_frameBoard((dc),(rect)) */
/* #define draw_frameTray( dc, rect ) (dc)->vtable->m_draw_frameTray((dc),(rect)) */
#define draw_drawCell( dc, rect, txt, bmap, o, bon, bl, h, s ) \
(dc)->vtable->m_draw_drawCell((dc),(rect),(txt),(bmap),(o),(bon),\
(bl),(h),(s))
#define draw_invertCell( dc, rect ) \
(dc)->vtable->m_draw_invertCell((dc),(rect))
#define draw_drawTile( dc, rect, text, bmp, val, hil ) \
(dc)->vtable->m_draw_drawTile((dc),(rect),(text),(bmp),(val),(hil))
#define draw_drawTileBack( dc, rect ) \
(dc)->vtable->m_draw_drawTileBack( (dc), (rect) )
#define draw_drawTrayDivider( dc, rect, s ) \
(dc)->vtable->m_draw_drawTrayDivider((dc),(rect), (s))
#define draw_clearRect( dc, rect ) (dc)->vtable->m_draw_clearRect((dc),(rect))
#define draw_drawBoardArrow( dc, r, b, v ) \
(dc)->vtable->m_draw_drawBoardArrow((dc),(r),(b), (v))
#ifdef KEY_SUPPORT
# define draw_drawTrayCursor( dc, r ) \
(dc)->vtable->m_draw_drawTrayCursor((dc),(r))
# define draw_drawBoardCursor( dc, r ) \
(dc)->vtable->m_draw_drawBoardCursor((dc),(r))
#else
# define draw_drawTrayCursor( dc, r )
# define draw_drawBoardCursor( dc, r )
#endif
#define draw_getMiniWText( dc, b ) \
(dc)->vtable->m_draw_getMiniWText( (dc),(b) )
#define draw_measureMiniWText( dc, t, wp, hp) \
(dc)->vtable->m_draw_measureMiniWText( (dc),(t), (wp), (hp) )
#define draw_drawMiniWindow( dc, t, r, c ) \
(dc)->vtable->m_draw_drawMiniWindow( (dc), (t), (r), (c) )
#define draw_eraseMiniWindow(dc, r, l, c, b) \
(dc)->vtable->m_draw_eraseMiniWindow( (dc), (r), (l), (c), (b) )
#ifdef DRAW_WITH_PRIMITIVES
#define draw_setClip( dc, rn, ro ) \
(dc)->vtable->m_draw_setClip( (dc), (rn), (ro) )
#define draw_frameRect( dc, r ) \
(dc)->vtable->m_draw_frameRect( (dc), (r) )
#define draw_invertRect( dc, r ) \
(dc)->vtable->m_draw_invertCell( (dc), (r) )
#define draw_drawString( dc, s, x, y ) \
(dc)->vtable->m_draw_drawString( (dc), (s), (x), (y) )
#define draw_drawBitmap( dc, bm, x, y ) \
(dc)->vtable->m_draw_drawBitmap( (dc), (bm), (x), (y) )
#define draw_measureText( dc, t, wp, hp ) \
(dc)->vtable->m_draw_measureText( (dc), (t), (wp), (hp) )
void InitDrawDefaults( DrawCtxVTable* vtable );
#endif /* DRAW_WITH_PRIMITIVES */
#endif

1105
xwords4/common/engine.c Normal file

File diff suppressed because it is too large Load diff

61
xwords4/common/engine.h Normal file
View file

@ -0,0 +1,61 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 1997 - 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 _ENGINE_H_
#define _ENGINE_H_
#include "comtypes.h"
#include "dictnry.h"
#ifdef CPLUS
extern "C" {
#endif
XP_U16 engine_getScoreCache( EngineCtxt* engine, XP_U16 row );
#if 0
#define engine_reset(e) /* nothing for now */
#else
typedef XP_Bool (*Continue_Callback)(void* state);
EngineCtxt* engine_make( MPFORMAL XW_UtilCtxt* util, XP_Bool isRobot );
void engine_writeToStream( EngineCtxt* ctxt, XWStreamCtxt* stream );
EngineCtxt* engine_makeFromStream( MPFORMAL XWStreamCtxt* stream,
XW_UtilCtxt* util, XP_Bool isRobot );
void engine_init( EngineCtxt* ctxt );
void engine_reset( EngineCtxt* ctxt );
void engine_destroy( EngineCtxt* ctxt );
#define NO_SCORE_LIMIT 10000 /* for targetScore */
XP_Bool engine_findMove( EngineCtxt* ctxt, ModelCtxt* model,
DictionaryCtxt* dict, const Tile* tiles,
XP_U16 numTiles, XP_U16 targetScore, XP_Bool* canMove,
MoveInfo* result );
XP_Bool engine_check( DictionaryCtxt* dict, Tile* buf, XP_U16 buflen );
#endif /* 0 */
#ifdef CPLUS
}
#endif
#endif /* _ENGINE_H_ */

425
xwords4/common/game.c Normal file
View file

@ -0,0 +1,425 @@
/* -*-mode: C; fill-column: 76; c-basic-offset: 4; -*- */
/*
* Copyright 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 "game.h"
#include "dictnry.h"
#include "strutils.h"
#ifdef CPLUS
extern "C" {
#endif
#ifdef DEBUG
static void
assertUtilOK( XW_UtilCtxt* util )
{
UtilVtable* vtable = util->vtable;
XP_U16 nSlots = sizeof(vtable) / 4;
while ( nSlots-- ) {
void* fptr = ((void**)vtable)[nSlots];
XP_ASSERT( !!fptr );
}
} /* assertUtilOK */
#else
# define assertUtilOK(u)
#endif
static void
checkServerRole( CurGameInfo* gi )
{
if ( !!gi ) {
if ( gi->serverRole != SERVER_ISCLIENT ) {
XP_Bool standAlone = gi->serverRole == SERVER_STANDALONE;
XP_U16 i, remoteCount = 0;
for ( i = 0; i < gi->nPlayers; ++i ) {
LocalPlayer* player = &gi->players[i];
if ( !player->isLocal ) {
++remoteCount;
if ( standAlone ) {
player->isLocal = XP_TRUE;
}
}
}
if ( remoteCount == 0 ) {
gi->serverRole = SERVER_STANDALONE;
}
}
}
} /* checkServerRole */
void
game_makeNewGame( MPFORMAL XWGame* game, CurGameInfo* gi,
XW_UtilCtxt* util, DrawCtx* draw,
CommonPrefs* cp,
TransportSend sendproc, void* closure )
{
assertUtilOK( util );
checkServerRole( gi );
game->model = model_make( MPPARM(mpool) (DictionaryCtxt*)NULL, util,
gi->boardSize, gi->boardSize );
#ifndef XWFEATURE_STANDALONE_ONLY
if ( !!sendproc ) {
game->comms = comms_make( MPPARM(mpool) util,
gi->serverRole != SERVER_ISCLIENT,
sendproc, closure );
} else {
game->comms = (CommsCtxt*)NULL;
}
#endif
game->server = server_make( MPPARM(mpool) game->model,
#ifndef XWFEATURE_STANDALONE_ONLY
game->comms,
#else
(CommsCtxt*)NULL,
#endif
util );
game->board = board_make( MPPARM(mpool) game->model, game->server,
draw, util );
server_prefsChanged( game->server, cp );
board_prefsChanged( game->board, cp );
} /* game_makeNewGame */
void
game_reset( MPFORMAL XWGame* game, CurGameInfo* gi, XP_U16 gameID,
CommonPrefs* cp, TransportSend sendproc, void* closure )
{
XP_U16 i;
XP_ASSERT( !!game->model );
XP_ASSERT( !!gi );
checkServerRole( gi );
gi->gameID = gameID;
model_init( game->model, gi->boardSize, gi->boardSize );
server_reset( game->server );
board_reset( game->board );
for ( i = 0; i < gi->nPlayers; ++i ) {
LocalPlayer* player = &gi->players[i];
XP_Bool isLocal = player->isLocal;
if ( !isLocal ) {
player->name = (XP_UCHAR*)NULL;
}
player->secondsUsed = 0;
}
#ifndef XWFEATURE_STANDALONE_ONLY
if ( !!game->comms ) {
comms_reset( game->comms, gi->serverRole != SERVER_ISCLIENT );
}
#endif
server_prefsChanged( game->server, cp );
board_prefsChanged( game->board, cp );
} /* game_reset */
void
game_makeFromStream( MPFORMAL XWStreamCtxt* stream, XWGame* game,
CurGameInfo* gi, DictionaryCtxt* dict,
XW_UtilCtxt* util, DrawCtx* draw, CommonPrefs* cp,
TransportSend sendProc, void* closure )
{
#ifndef XWFEATURE_STANDALONE_ONLY
XP_Bool hasComms;
#endif
#ifdef DEBUG
XP_U8 version = stream_getU8( stream );
XP_ASSERT( version == CUR_STREAM_VERS );
#else
(void)stream_getU8( stream );
#endif
gi_readFromStream( MPPARM(mpool) stream, gi );
#ifndef XWFEATURE_STANDALONE_ONLY
hasComms = stream_getU8( stream );
game->comms = hasComms?
comms_makeFromStream( MPPARM(mpool) stream, util, sendProc, closure ):
(CommsCtxt*)NULL;
#endif
game->model = model_makeFromStream( MPPARM(mpool) stream, dict, util );
game->server = server_makeFromStream( MPPARM(mpool) stream,
game->model,
#ifndef XWFEATURE_STANDALONE_ONLY
game->comms,
#else
(CommsCtxt*)NULL,
#endif
util, gi->nPlayers );
game->board = board_makeFromStream( MPPARM(mpool) stream, game->model,
game->server, draw, util,
gi->nPlayers );
server_prefsChanged( game->server, cp );
board_prefsChanged( game->board, cp );
} /* game_makeFromStream */
void
game_saveToStream( XWGame* game, CurGameInfo* gi, XWStreamCtxt* stream )
{
stream_putU8( stream, CUR_STREAM_VERS );
gi_writeToStream( stream, gi );
#ifndef XWFEATURE_STANDALONE_ONLY
stream_putU8( stream, (XP_U8)!!game->comms );
if ( !!game->comms ) {
comms_writeToStream( game->comms, stream );
}
#endif
model_writeToStream( game->model, stream );
server_writeToStream( game->server, stream );
board_writeToStream( game->board, stream );
} /* game_saveToStream */
void
game_dispose( XWGame* game )
{
/* The board should be reused!!! PENDING(ehouse) */
if ( !!game->board ) {
board_destroy( game->board );
}
#ifndef XWFEATURE_STANDALONE_ONLY
if ( !!game->comms ) {
comms_destroy( game->comms );
}
#endif
if ( !!game->model ) {
DictionaryCtxt* dict = model_getDictionary( game->model );
if ( !!dict ) {
dict_destroy( dict );
}
model_destroy( game->model );
}
if ( !!game->server ) {
server_destroy( game->server );
}
} /* game_dispose */
void
gi_initPlayerInfo( MPFORMAL CurGameInfo* gi, XP_UCHAR* nameTemplate )
{
XP_U16 i;
XP_MEMSET( gi, 0, sizeof(*gi) );
gi->nPlayers = 2;
gi->boardSize = 15;
gi->robotSmartness = SMART_ROBOT;
gi->timerEnabled = XP_FALSE;
gi->gameSeconds = 25 * 60; /* 25 minute game is common? */
for ( i = 0; i < MAX_NUM_PLAYERS; ++i ) {
XP_UCHAR buf[20];
LocalPlayer* fp = &gi->players[i];
if ( !!nameTemplate ) {
XP_SNPRINTF( buf, sizeof(buf), nameTemplate, i+1 );
XP_ASSERT( fp->name == NULL );
fp->name = copyString( MPPARM(mpool) buf );
}
fp->isRobot = (i == 0); /* one robot */
fp->isLocal = XP_TRUE;
fp->secondsUsed = 0;
}
} /* game_initPlayerInfo */
static void
disposePlayerInfoInt( MPFORMAL CurGameInfo* gi, XP_Bool namesToo )
{
XP_U16 i;
LocalPlayer* lp;
for ( lp = gi->players, i = 0; i < MAX_NUM_PLAYERS; ++lp, ++i ) {
if ( !!lp->name ) {
XP_FREE( mpool, lp->name );
lp->name = (XP_UCHAR*)NULL;
}
if ( !!lp->password ) {
XP_FREE( mpool, lp->password );
lp->password = (XP_UCHAR*)NULL;
}
}
} /* disposePlayerInfoInt */
void
gi_disposePlayerInfo( MPFORMAL CurGameInfo* gi )
{
disposePlayerInfoInt( MPPARM(mpool) gi, XP_TRUE );
if ( !!gi->dictName ) {
XP_FREE( mpool, gi->dictName );
gi->dictName = (XP_UCHAR*)NULL;
}
} /* gi_disposePlayerInfo */
void
gi_copy( MPFORMAL CurGameInfo* destGI, CurGameInfo* srcGI )
{
XP_U16 nPlayers, i;
LocalPlayer* srcPl;
LocalPlayer* destPl;
replaceStringIfDifferent( MPPARM(mpool) &destGI->dictName,
srcGI->dictName );
destGI->gameID = srcGI->gameID;
destGI->gameSeconds = srcGI->gameSeconds;
destGI->nPlayers = (XP_U8)srcGI->nPlayers;
nPlayers = srcGI->nPlayers;
destGI->boardSize = (XP_U8)srcGI->boardSize;
destGI->serverRole = srcGI->serverRole;
destGI->hintsNotAllowed = srcGI->hintsNotAllowed;
destGI->timerEnabled = srcGI->timerEnabled;
destGI->robotSmartness = (XP_U8)srcGI->robotSmartness;
destGI->phoniesAction = srcGI->phoniesAction;
for ( srcPl = srcGI->players, destPl = destGI->players, i = 0;
i < nPlayers; ++srcPl, ++destPl, ++i ) {
replaceStringIfDifferent( MPPARM(mpool) &destPl->name, srcPl->name );
replaceStringIfDifferent( MPPARM(mpool) &destPl->password,
srcPl->password );
destPl->secondsUsed = srcPl->secondsUsed;
destPl->isRobot = srcPl->isRobot;
destPl->isLocal = srcPl->isLocal;
}
} /* gi_copy */
void
gi_readFromStream( MPFORMAL XWStreamCtxt* stream, CurGameInfo* gi )
{
LocalPlayer* pl;
XP_U16 i;
XP_UCHAR* str;
str = stringFromStream( MPPARM(mpool) stream );
replaceStringIfDifferent( MPPARM(mpool) &gi->dictName, str );
if ( !!str ) {
XP_FREE( mpool, str );
}
gi->nPlayers = (XP_U8)stream_getBits( stream, NPLAYERS_NBITS );
gi->boardSize = (XP_U8)stream_getBits( stream, 4 );
gi->serverRole = (Connectedness)stream_getBits( stream, 2 );
gi->hintsNotAllowed = stream_getBits( stream, 1 );
gi->robotSmartness = (XP_U8)stream_getBits( stream, 2 );
gi->phoniesAction = (XWPhoniesChoice)stream_getBits( stream, 2 );
gi->timerEnabled = stream_getBits( stream, 1 );
gi->gameID = stream_getU16( stream );
if ( gi->timerEnabled ) {
gi->gameSeconds = stream_getU16( stream );
}
for ( pl = gi->players, i = 0; i < gi->nPlayers; ++pl, ++i ) {
str = stringFromStream( MPPARM(mpool) stream );
replaceStringIfDifferent( MPPARM(mpool) &pl->name, str );
if ( !!str ) {
XP_FREE( mpool, str );
}
str = stringFromStream( MPPARM(mpool) stream );
replaceStringIfDifferent( MPPARM(mpool) &pl->password, str );
if ( !!str ) {
XP_FREE( mpool, str );
}
pl->secondsUsed = stream_getU16( stream );
pl->isRobot = stream_getBits( stream, 1 );
pl->isLocal = stream_getBits( stream, 1 );
}
} /* gi_readFromStream */
void
gi_writeToStream( XWStreamCtxt* stream, CurGameInfo* gi )
{
LocalPlayer* pl;
XP_U16 i;
stringToStream( stream, gi->dictName );
stream_putBits( stream, NPLAYERS_NBITS, gi->nPlayers );
stream_putBits( stream, 4, gi->boardSize );
stream_putBits( stream, 2, gi->serverRole );
stream_putBits( stream, 1, gi->hintsNotAllowed );
stream_putBits( stream, 2, gi->robotSmartness );
stream_putBits( stream, 2, gi->phoniesAction );
stream_putBits( stream, 1, gi->timerEnabled );
stream_putU16( stream, gi->gameID );
if ( gi->timerEnabled) {
stream_putU16( stream, gi->gameSeconds );
}
for ( pl = gi->players, i = 0; i < gi->nPlayers; ++pl, ++i ) {
stringToStream( stream, pl->name );
stringToStream( stream, pl->password );
stream_putU16( stream, pl->secondsUsed );
stream_putBits( stream, 1, pl->isRobot );
stream_putBits( stream, 1, pl->isLocal );
}
} /* gi_writeToStream */
XP_Bool
player_hasPasswd( LocalPlayer* player )
{
XP_UCHAR* password = player->password;
/* XP_ASSERT( player->isLocal ); */
return !!password && *password != '\0';
} /* player_hasPasswd */
XP_Bool
player_passwordMatches( LocalPlayer* player, XP_U8* buf, XP_U16 len )
{
XP_ASSERT( player->isLocal );
return (XP_STRLEN(player->password) == len)
&& (0 == XP_STRNCMP( player->password, (XP_UCHAR*)buf, len ));
} /* player_passwordMatches */
XP_U16
player_timePenalty( CurGameInfo* gi, XP_U16 playerNum )
{
XP_S16 seconds = (gi->gameSeconds / gi->nPlayers);
LocalPlayer* player = gi->players + playerNum;
XP_U16 result = 0;
seconds -= player->secondsUsed;
if ( seconds < 0 ) {
seconds = -seconds;
seconds += 59;
result = (seconds/60) * 10;
}
return result;
} /* player_timePenalty */
#ifdef CPLUS
}
#endif

99
xwords4/common/game.h Normal file
View file

@ -0,0 +1,99 @@
/* -*-mode: C; fill-column: 76; c-basic-offset: 4; -*- */
/*
* Copyright 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 _GAME_H_
#define _GAME_H_
#include "model.h"
#include "board.h"
#include "comms.h"
#include "server.h"
#include "util.h"
#ifdef CPLUS
extern "C" {
#endif
#define CUR_STREAM_VERS 0x01
typedef struct LocalPlayer {
XP_UCHAR* name;
XP_UCHAR* password;
XP_U16 secondsUsed;
XP_Bool isRobot;
XP_Bool isLocal;
} LocalPlayer;
#define DUMB_ROBOT 0
#define SMART_ROBOT 1
typedef struct CurGameInfo {
XP_UCHAR* dictName;
LocalPlayer players[MAX_NUM_PLAYERS];
XP_U16 gameID; /* uniquely identifies game */
XP_U16 gameSeconds; /* for timer */
XP_U8 nPlayers;
XP_U8 boardSize;
Connectedness serverRole;
XP_Bool hintsNotAllowed;
XP_Bool timerEnabled;
XP_U8 robotSmartness;
XWPhoniesChoice phoniesAction;
} CurGameInfo;
typedef struct XWGame {
BoardCtxt* board;
ModelCtxt* model;
ServerCtxt* server;
#ifndef XWFEATURE_STANDALONE_ONLY
CommsCtxt* comms;
#endif
} XWGame;
void game_makeNewGame( MPFORMAL XWGame* game, CurGameInfo* gi,
XW_UtilCtxt* util, DrawCtx* draw, CommonPrefs* cp,
TransportSend sendproc, void* closure );
void game_reset( MPFORMAL XWGame* game, CurGameInfo* gi, XP_U16 gameID,
CommonPrefs* cp, TransportSend sendproc, void* closure );
void game_makeFromStream( MPFORMAL XWStreamCtxt* stream, XWGame* game,
CurGameInfo* gi,
DictionaryCtxt* dict, XW_UtilCtxt* util,
DrawCtx* draw, CommonPrefs* cp,
TransportSend sendProc, void* closure );
void game_saveToStream( XWGame* game, CurGameInfo* gi, XWStreamCtxt* stream );
void game_dispose( XWGame* game );
void gi_initPlayerInfo( MPFORMAL CurGameInfo* gi, XP_UCHAR* nameTemplate );
void gi_disposePlayerInfo( MPFORMAL CurGameInfo* gi );
void gi_writeToStream( XWStreamCtxt* stream, CurGameInfo* gi );
void gi_readFromStream( MPFORMAL XWStreamCtxt* stream, CurGameInfo* gi );
void gi_copy( MPFORMAL CurGameInfo* destGI, CurGameInfo* srcGi );
XP_Bool player_hasPasswd( LocalPlayer* player );
XP_Bool player_passwordMatches( LocalPlayer* player, XP_U8* buf, XP_U16 len );
XP_U16 player_timePenalty( CurGameInfo* gi, XP_U16 playerNum );
#ifdef CPLUS
}
#endif
#endif

244
xwords4/common/mempool.c Normal file
View file

@ -0,0 +1,244 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 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.
*/
#ifdef MEM_DEBUG
#include "mempool.h"
#include "comtypes.h"
#include "xwstream.h"
#ifdef CPLUS
extern "C" {
#endif
typedef struct MemPoolEntry {
struct MemPoolEntry* next;
char* fileName;
XP_U32 lineNo;
XP_U32 size;
void* ptr;
} MemPoolEntry;
struct MemPoolCtx {
MemPoolEntry* freeList;
MemPoolEntry* usedList;
XP_U16 nFree;
XP_U16 nUsed;
XP_U16 nAllocs;
};
/*--------------------------------------------------------------------------*/
MemPoolCtx*
mpool_make()
{
MemPoolCtx* result = (MemPoolCtx*)XP_PLATMALLOC( sizeof(*result) );
XP_MEMSET( result, 0, sizeof(*result) );
return result;
} /* mpool_make */
static void
freeList( MemPoolEntry* entry )
{
while ( !!entry ) {
MemPoolEntry* next = entry->next;
XP_ASSERT( !entry->ptr );
XP_PLATFREE( entry );
entry = next;
}
} /* freeList */
#ifdef DEBUG
static char*
checkIsText( MemPoolEntry* entry )
{
unsigned char* txt = (unsigned char*)entry->ptr;
XP_U32 len = entry->size;
while ( len-- ) {
unsigned char c = *txt++;
if ( c < 32 || c > 127 ) {
if ( len == 0 && c == '\0' ) {
return (char*)entry->ptr;
} else {
return (char*)NULL;
}
}
}
return (char*)NULL;
} /* checkIsText */
#endif
void
mpool_destroy( MemPoolCtx* mpool )
{
if ( !!mpool->usedList ) {
MemPoolEntry* entry;
for ( entry = mpool->usedList; !!entry; entry = entry->next ) {
#ifndef FOR_GREMLINS /* I don't want to hear about this right now */
XP_WARNF( "0x%lx from ln %ld of %s\n",
entry->ptr, entry->lineNo, entry->fileName );
#ifdef DEBUG
{
char* tryTxt;
tryTxt = checkIsText( entry );
if ( !!tryTxt ) {
XP_WARNF( "--- looks like text: %s\n", tryTxt );
}
}
#endif
#endif
}
}
#ifndef FOR_GREMLINS
XP_ASSERT( !mpool->usedList && mpool->nUsed == 0 );
#endif
freeList( mpool->freeList );
XP_PLATFREE( mpool );
} /* mpool_destroy */
void*
mpool_alloc( MemPoolCtx* mpool, XP_U32 size, char* file, XP_U32 lineNo )
{
MemPoolEntry* entry;
if ( mpool->nFree > 0 ) {
entry = mpool->freeList;
mpool->freeList = entry->next;
--mpool->nFree;
} else {
entry = (MemPoolEntry*)XP_PLATMALLOC( sizeof(*entry) );
}
entry->next = mpool->usedList;
mpool->usedList = entry;
entry->fileName = file;
entry->lineNo = lineNo;
entry->size = size;
entry->ptr = XP_PLATMALLOC( size );
XP_ASSERT( !!entry->ptr );
++mpool->nUsed;
++mpool->nAllocs;
return entry->ptr;
} /* mpool_alloc */
static MemPoolEntry*
findEntryFor( MemPoolCtx* mpool, void* ptr, MemPoolEntry** prevP )
{
MemPoolEntry* entry;
MemPoolEntry* prev;
for ( prev = (MemPoolEntry*)NULL, entry = mpool->usedList; !!entry;
prev = entry, entry = prev->next ) {
if ( entry->ptr == ptr ) {
if ( !!prevP ) {
*prevP = prev;
}
return entry;
}
}
XP_ASSERT(0);
return (MemPoolEntry*)NULL;
} /* findEntryFor */
void*
mpool_realloc( MemPoolCtx* mpool, void* ptr, XP_U32 newsize )
{
MemPoolEntry* entry = findEntryFor( mpool, ptr, (MemPoolEntry**)NULL );
entry->ptr = XP_PLATREALLOC( entry->ptr, newsize );
XP_ASSERT( !!entry->ptr );
return entry->ptr;
} /* mpool_realloc */
void
mpool_free( MemPoolCtx* mpool, void* ptr, char* file, XP_U32 lineNo )
{
MemPoolEntry* entry;
MemPoolEntry* prev;
entry = findEntryFor( mpool, ptr, &prev );
if ( !!entry ) {
if ( !!prev ) {
prev->next = entry->next;
} else {
mpool->usedList = entry->next;
}
XP_MEMSET( entry->ptr, 0x00, entry->size );
XP_PLATFREE( entry->ptr );
entry->ptr = NULL;
entry->next = mpool->freeList;
mpool->freeList = entry;
++mpool->nFree;
--mpool->nUsed;
return;
}
XP_ASSERT( 0 );
} /* mpool_free */
void
mpool_stats( MemPoolCtx* mpool, XWStreamCtxt* stream )
{
XP_UCHAR buf[128];
MemPoolEntry* entry;
XP_SNPRINTF( buf, sizeof(buf), (XP_UCHAR*)"Number of blocks in use: %d\n"
"Number of free blocks: %d\n"
"Total number of blocks allocated: %d\n",
mpool->nUsed, mpool->nFree, mpool->nAllocs );
stream_putBytes( stream, buf, (XP_U16)XP_STRLEN(buf) );
for ( entry = mpool->usedList; !!entry; entry = entry->next ) {
XP_SNPRINTF( buf, sizeof(buf),
(XP_UCHAR*)"%ld byte block allocated %s: line %ld\n",
entry->size, entry->fileName, entry->lineNo );
stream_putBytes( stream, buf, (XP_U16)XP_STRLEN(buf) );
}
} /* mpool_stats */
XP_U16
mpool_getNUsed( MemPoolCtx* mpool )
{
return mpool->nUsed;
} /* mpool_getNUsed */
#ifdef CPLUS
}
#endif
#endif /* MEM_DEBUG */

52
xwords4/common/mempool.h Normal file
View file

@ -0,0 +1,52 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 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 _MEMPOOL_H_
#define _MEMPOOL_H_
#ifdef MEM_DEBUG
#include "comtypes.h"
#ifdef CPLUS
extern "C" {
#endif
typedef struct MemPoolCtx MemPoolCtx;
MemPoolCtx* mpool_make();
void mpool_destroy( MemPoolCtx* mpool );
void* mpool_alloc( MemPoolCtx* mpool, XP_U32 size, char* file,
XP_U32 lineNo );
void* mpool_realloc( MemPoolCtx* mpool, void* ptr, XP_U32 newsize );
void mpool_free( MemPoolCtx* mpool, void* ptr, char* file, XP_U32 lineNo );
void mpool_stats( MemPoolCtx* mpool, XWStreamCtxt* stream );
XP_U16 mpool_getNUsed( MemPoolCtx* mpool );
#ifdef CPLUS
}
#endif
#else
# define mpool_destroy(p)
#endif /* MEM_DEBUG */
#endif /* _MEMPOOL_H_ */

416
xwords4/common/memstream.c Normal file
View file

@ -0,0 +1,416 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 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 <PalmTypes.h> */
/* #include <SystemMgr.h> */
/* #include <IrLib.h> */
#include "xwstream.h"
#include "comtypes.h"
#include "memstream.h"
#include "vtabmgr.h"
#ifdef CPLUS
extern "C" {
#endif
#define BIT_PART(pos) ((pos)&0x00000007)
#define BYTE_PART(pos) ((pos)>>3)
#define MIN_PACKETBUF_SIZE (1<<6)
#define STREAM_INCR_SIZE 100
#define SOCKET_STREAM_SUPER_COMMON_SLOTS \
StreamCtxVTable* vtable; \
void* closure; \
XP_U32 curReadPos; \
XP_U32 curWritePos; \
XP_PlayerAddr channelNo; \
XP_U8* buf; \
MemStreamCloseCallback onClose; \
XP_U16 nBytesWritten; \
XP_U16 nBytesAllocated; \
XP_U8 nReadBits; \
XP_U8 nWriteBits; \
XP_Bool isOpen; \
MPSLOT
#define SOCKET_STREAM_SUPER_SLOTS \
SOCKET_STREAM_SUPER_COMMON_SLOTS
typedef struct MemStreamCtxt {
SOCKET_STREAM_SUPER_SLOTS
} MemStreamCtxt;
static StreamCtxVTable* make_vtable( MemStreamCtxt* stream );
/* Try to keep this the only entry point to this file, and to keep it at the
* top of the file (first executable code).
*/
XWStreamCtxt*
mem_stream_make( MPFORMAL VTableMgr* vtmgr, void* closure,
XP_PlayerAddr channelNo, MemStreamCloseCallback onClose )
{
StreamCtxVTable* vtable;
MemStreamCtxt* result = (MemStreamCtxt*)XP_MALLOC( mpool,
sizeof(*result) );
XP_MEMSET( result, 0, sizeof(*result) );
MPASSIGN(result->mpool, mpool);
vtable = (StreamCtxVTable*)vtmgr_getVTable( vtmgr, VTABLE_MEM_STREAM );
if ( !vtable ) {
vtable = make_vtable( result );
vtmgr_setVTable( vtmgr, VTABLE_MEM_STREAM, vtable );
}
result->vtable = vtable;
result->closure = closure;
result->channelNo = channelNo;
result->onClose = onClose;
result->isOpen = XP_TRUE;
return (XWStreamCtxt*)result;
} /* make_mem_stream */
static void
mem_stream_getBytes( XWStreamCtxt* p_sctx, void* where, XP_U16 count )
{
MemStreamCtxt* stream = (MemStreamCtxt*)p_sctx;
if ( stream->nReadBits != 0 ) {
stream->nReadBits = 0;
}
XP_ASSERT( stream->curReadPos + count <= stream->nBytesAllocated );
XP_ASSERT( stream->curReadPos + count <= stream->nBytesWritten );
XP_MEMCPY( where, stream->buf + stream->curReadPos, count );
stream->curReadPos += count;
XP_ASSERT( stream->curReadPos <= stream->nBytesWritten );
} /* mem_stream_getBytes */
static XP_U8
mem_stream_getU8( XWStreamCtxt* p_sctx )
{
XP_U8 result;
mem_stream_getBytes( p_sctx, &result, sizeof(result) );
return result;
} /* mem_stream_getU8 */
static XP_U16
mem_stream_getU16( XWStreamCtxt* p_sctx )
{
XP_U16 result;
mem_stream_getBytes( p_sctx, &result, sizeof(result) );
return XP_NTOHS(result);
} /* mem_stream_getU16 */
static XP_U32
mem_stream_getU32( XWStreamCtxt* p_sctx )
{
XP_U32 result;
mem_stream_getBytes( p_sctx, &result, sizeof(result) );
return XP_NTOHL( result );
} /* mem_stream_getU32 */
static XP_Bool
getOneBit( MemStreamCtxt* stream )
{
XP_U8 mask, rack;
XP_Bool result;
if ( stream->nReadBits == 0 ) {
++stream->curReadPos;
}
rack = stream->buf[stream->curReadPos-1];
mask = 1 << stream->nReadBits++;
result = (rack & mask) != 0;
if ( stream->nReadBits == 8 ) {
stream->nReadBits = 0;
}
return result;
} /* getOneBit */
static XP_U32
mem_stream_getBits( XWStreamCtxt* p_sctx, XP_U16 nBits )
{
MemStreamCtxt* stream = (MemStreamCtxt*)p_sctx;
XP_U32 mask;
XP_U32 result = 0;
for ( mask = 1L; nBits--; mask <<= 1 ) {
if ( getOneBit( stream ) ) {
result |= mask;
}
}
return result;
} /* stream_getBits */
static void
mem_stream_putBytes( XWStreamCtxt* p_sctx, void* whence, XP_U16 count )
{
MemStreamCtxt* stream = (MemStreamCtxt*)p_sctx;
XP_U32 newSize;
if ( !stream->buf ) {
XP_ASSERT( stream->nBytesAllocated == 0 );
stream->buf = (XP_U8*)XP_MALLOC( stream->mpool, STREAM_INCR_SIZE );
stream->nBytesAllocated = STREAM_INCR_SIZE;
}
/* I don't yet deal with getting asked to get/put a byte when in the
middle of doing bitwise stuff. It's probably just a matter of skipping
to the next byte, though -- and curPos should already be there. */
if ( stream->nWriteBits != 0 ) {
stream->nWriteBits = 0;
}
/* Reallocation. We may be writing into the middle of an existing stream,
and doing so may still require expanding the stream. So figure out if
the new size is bigger than what we have, and if so expand to hold it
plus something. */
newSize = stream->nBytesWritten + count;
if ( stream->curWritePos < stream->nBytesWritten ) {
newSize -= stream->nBytesWritten - stream->curWritePos;
}
if ( newSize > stream->nBytesAllocated ) {
XP_ASSERT( newSize + STREAM_INCR_SIZE < 0xFFFF );
stream->nBytesAllocated = (XP_U16)newSize + STREAM_INCR_SIZE;
stream->buf =
(XP_U8*)XP_REALLOC( stream->mpool, stream->buf,
stream->nBytesAllocated );
}
XP_MEMCPY( stream->buf + stream->curWritePos, whence, count );
stream->nBytesWritten = (XP_U16)newSize;
stream->curWritePos += count;
} /* mem_stream_putBytes */
static void
mem_stream_putU8( XWStreamCtxt* p_sctx, XP_U8 data )
{
mem_stream_putBytes( p_sctx, &data, sizeof(data) );
} /* mem_stream_putU8 */
static void
mem_stream_putU16( XWStreamCtxt* p_sctx, XP_U16 data )
{
data = XP_HTONS( data );
mem_stream_putBytes( p_sctx, &data, sizeof(data) );
} /* linux_common_stream_putU16 */
static void
mem_stream_putU32( XWStreamCtxt* p_sctx, XP_U32 data )
{
data = XP_HTONL( data );
mem_stream_putBytes( p_sctx, &data, sizeof(data) );
} /* mem_stream_putU32 */
static void
putOneBit( MemStreamCtxt* stream, XP_U16 bit )
{
XP_U8 mask, rack;
if ( stream->nWriteBits == 0 ) {
if ( stream->curWritePos == stream->nBytesWritten ) {
stream_putU8( (XWStreamCtxt*)stream, 0 ); /* increments curPos */
} else {
++stream->curWritePos;
}
}
XP_ASSERT( stream->curWritePos > 0 );
rack = stream->buf[stream->curWritePos-1];
mask = 1 << stream->nWriteBits++;
if ( bit ) {
rack |= mask;
} else {
rack &= ~mask;
}
stream->buf[stream->curWritePos-1] = rack;
stream->nWriteBits %= 8;
} /* putOneBit */
static void
mem_stream_putBits( XWStreamCtxt* p_sctx, XP_U16 nBits, XP_U32 data )
{
MemStreamCtxt* stream = (MemStreamCtxt*)p_sctx;
XP_ASSERT( nBits > 0 );
while ( nBits-- ) {
putOneBit( stream, (XP_U16)(((data & 1L) != 0)? 1:0) );
data >>= 1;
}
XP_ASSERT( data == 0 ); /* otherwise nBits was too smalls */
} /* mem_stream_putBits */
static void
mem_stream_copyFromStream( XWStreamCtxt* p_sctx, XWStreamCtxt* src,
XP_U16 nBytes )
{
while ( nBytes-- ) {
XP_U8 byt = stream_getU8( src );
stream_putU8( p_sctx, byt );
}
} /* mem_stream_copyFromStream */
static void
mem_stream_open( XWStreamCtxt* p_sctx )
{
MemStreamCtxt* stream = (MemStreamCtxt*)p_sctx;
stream->nBytesWritten = 0;
stream->curReadPos = START_OF_STREAM;
stream->curWritePos = START_OF_STREAM;
} /* mem_stream_open */
static void
mem_stream_close( XWStreamCtxt* p_sctx )
{
MemStreamCtxt* stream = (MemStreamCtxt*)p_sctx;
XP_ASSERT( stream->isOpen );
if ( !!stream->onClose ) {
(*stream->onClose)( p_sctx, stream->closure );
}
stream->isOpen = XP_FALSE;
} /* mem_stream_close */
static XP_U16
mem_stream_getSize( XWStreamCtxt* p_sctx )
{
MemStreamCtxt* stream = (MemStreamCtxt*)p_sctx;
return stream->nBytesWritten;
} /* mem_stream_getSize */
static XP_PlayerAddr
mem_stream_getAddress( XWStreamCtxt* p_sctx )
{
MemStreamCtxt* stream = (MemStreamCtxt*)p_sctx;
return stream->channelNo;
} /* mem_stream_getAddress */
static void
mem_stream_setAddress( XWStreamCtxt* p_sctx, XP_PlayerAddr channelNo )
{
MemStreamCtxt* stream = (MemStreamCtxt*)p_sctx;
stream->channelNo = channelNo;
} /* mem_stream_getAddress */
static XWStreamPos
mem_stream_getPos( XWStreamCtxt* p_sctx, PosWhich which )
{
XWStreamPos result;
MemStreamCtxt* stream = (MemStreamCtxt*)p_sctx;
if ( which == POS_WRITE ) {
result = (stream->curWritePos << 3) | stream->nWriteBits;
} else {
result = (stream->curReadPos << 3) | stream->nReadBits;
}
return result;
} /* mem_stream_getPos */
static XWStreamPos
mem_stream_setPos( XWStreamCtxt* p_sctx, XWStreamPos newpos, PosWhich which )
{
MemStreamCtxt* stream = (MemStreamCtxt*)p_sctx;
XWStreamPos oldPos = mem_stream_getPos( p_sctx, which );
if ( which == POS_WRITE ) {
stream->nWriteBits = (XP_U8)BIT_PART(newpos);
stream->curWritePos = (XP_U32)BYTE_PART(newpos);
} else {
stream->nReadBits = (XP_U8)BIT_PART(newpos);
stream->curReadPos = (XP_U32)BYTE_PART(newpos);
}
return oldPos;
} /* mem_stream_setPos */
static void
mem_stream_destroy( XWStreamCtxt* p_sctx )
{
MemStreamCtxt* stream = (MemStreamCtxt*)p_sctx;
if ( stream->isOpen ) {
stream_close( p_sctx );
}
if ( !!stream->buf ) {
XP_FREE( stream->mpool, stream->buf );
}
XP_FREE( stream->mpool, stream );
} /* mem_stream_destroy */
static StreamCtxVTable*
make_vtable( MemStreamCtxt* stream )
{
StreamCtxVTable* vtable;
XP_ASSERT( !stream->vtable );
XP_ASSERT( sizeof(stream->vtable) == sizeof(vtable) );
vtable = (StreamCtxVTable*)XP_MALLOC( stream->mpool,
sizeof(*stream->vtable) );
SET_VTABLE_ENTRY( vtable, stream_getU8, mem );
SET_VTABLE_ENTRY( vtable, stream_getBytes, mem );
SET_VTABLE_ENTRY( vtable, stream_getU16, mem );
SET_VTABLE_ENTRY( vtable, stream_getU32, mem );
SET_VTABLE_ENTRY( vtable, stream_getBits, mem );
SET_VTABLE_ENTRY( vtable, stream_putU8, mem );
SET_VTABLE_ENTRY( vtable, stream_putBytes, mem );
SET_VTABLE_ENTRY( vtable, stream_putU16, mem );
SET_VTABLE_ENTRY( vtable, stream_putU32, mem );
SET_VTABLE_ENTRY( vtable, stream_putBits, mem );
SET_VTABLE_ENTRY( vtable, stream_copyFromStream, mem );
SET_VTABLE_ENTRY( vtable, stream_setPos, mem );
SET_VTABLE_ENTRY( vtable, stream_getPos, mem );
SET_VTABLE_ENTRY( vtable, stream_destroy, mem );
SET_VTABLE_ENTRY( vtable, stream_open, mem );
SET_VTABLE_ENTRY( vtable, stream_close, mem );
SET_VTABLE_ENTRY( vtable, stream_getSize, mem );
SET_VTABLE_ENTRY( vtable, stream_getAddress, mem );
SET_VTABLE_ENTRY( vtable, stream_setAddress, mem );
return vtable;
} /* make_vtable */
#ifdef CPLUS
}
#endif

View file

@ -0,0 +1,46 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 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 _MEMSTREAM_H_
#define _MEMSTREAM_H_
#include "comtypes.h"
#include "mempool.h"
#include "vtabmgr.h"
#ifdef CPLUS
extern "C" {
#endif
typedef void (*MemStreamCloseCallback)( XWStreamCtxt* stream,
void* closure );
XWStreamCtxt* mem_stream_make( MPFORMAL VTableMgr* vtmgr,
void* closure,
XP_PlayerAddr addr, /* should be in a
subclass */
MemStreamCloseCallback onCloseWritten );
#ifdef CPLUS
}
#endif
#endif

1488
xwords4/common/model.c Normal file

File diff suppressed because it is too large Load diff

238
xwords4/common/model.h Normal file
View file

@ -0,0 +1,238 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 1997 - 2000 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 _MODEL_H_
#define _MODEL_H_
#include "comtypes.h"
/* #include "xptypes.h" */
#include "dictnry.h"
#include "mempool.h"
#ifdef CPLUS
extern "C" {
#endif
#define MAX_ROWS 16
#define MAX_COLS 16
#define NUMCOLS_NBITS 4
#ifdef EIGHT_TILES
#define MAX_TRAY_TILES 8
#define NTILES_NBITS 4
#else
#define MAX_TRAY_TILES 7
#define NTILES_NBITS 3
#endif
#define MIN_TRADE_TILES MAX_TRAY_TILES
/* apply to CellTile */
#define TILE_VALUE_MASK 0x001F
#define TILE_BLANK_BIT 0x0020
#define IS_BLANK(t) (((t)&TILE_BLANK_BIT)!= 0)
#define TILE_EMPTY_BIT 0x0040
#define TILE_PENDING_BIT 0x0080
#define PREV_MOVE_BIT 0x100
#define CELL_OWNER_MASK 0x0600
#define CELL_OWNER_OFFSET 9
#define CELL_OWNER(t) (((t)&CELL_OWNER_MASK) >> CELL_OWNER_OFFSET)
#define MAX_UNIQUE_TILES 32 /* max tile non-blank faces */
#define MAX_NUM_BLANKS 4
/* Used by scoring code and engine as fast representation of moves. */
typedef struct MoveInfoTile {
XP_U8 varCoord; /* 5 bits ok (0-16 for 17x17 board) */
Tile tile; /* 5 bits will do */
} MoveInfoTile;
typedef struct MoveInfo {
XP_U8 nTiles; /* 4 bits: 0-7 */
XP_U8 commonCoord; /* 5 bits: 0-16 if 17x17 possible */
XP_Bool isHorizontal; /* 1 bit */
/* If this is to go on an undo stack, we need player num here, or the code
has to keep track of it *and* there must be exactly one entry per
player per turn. */
MoveInfoTile tiles[MAX_TRAY_TILES];
} MoveInfo;
typedef XP_U8 TrayTile;
typedef struct TrayTileSet {
XP_U8 nTiles;
TrayTile tiles[MAX_TRAY_TILES];
} TrayTileSet;
typedef struct BlankQueue {
XP_U16 nBlanks;
XP_U8 col[MAX_NUM_BLANKS];
XP_U8 row[MAX_NUM_BLANKS];
} BlankQueue;
typedef XP_U8 TileBit; /* bits indicating selection of tiles in tray */
#define ALLTILES ((TileBit)~(0xFF<<(MAX_TRAY_TILES)))
#define ILLEGAL_MOVE_SCORE (-1)
#define EMPTY_TILE TILE_EMPTY_BIT
#define TILE_IS_EMPTY(t) (((t)&TILE_EMPTY_BIT)!=0)
#define REVERSED_TILE TILE_PENDING_BIT /* reuse that bit for tile drawing
only */
ModelCtxt* model_make( MPFORMAL DictionaryCtxt* dict, XW_UtilCtxt* util,
XP_U16 nCols, XP_U16 nRows );
ModelCtxt* model_makeFromStream( MPFORMAL XWStreamCtxt* stream,
DictionaryCtxt* dict, XW_UtilCtxt* util );
void model_writeToStream( ModelCtxt* model, XWStreamCtxt* stream );
void model_init( ModelCtxt* model, XP_U16 nCols, XP_U16 nRows );
void model_destroy( ModelCtxt* model );
void model_setNPlayers( ModelCtxt* model, XP_U16 numPlayers );
void model_setDictionary( ModelCtxt* model, DictionaryCtxt* dict );
DictionaryCtxt* model_getDictionary( ModelCtxt* model );
XP_Bool model_getTile( ModelCtxt* model, XP_U16 col, XP_U16 row,
XP_Bool getPending, XP_S16 turn,
Tile* tile, XP_Bool* isBlank,
XP_Bool* isPending, XP_Bool* isRecent );
void model_listPlacedBlanks( ModelCtxt* model, XP_U16 turn,
XP_Bool includePending, BlankQueue* bcp );
XP_U16 model_getCellOwner( ModelCtxt* model, XP_U16 col, XP_U16 row );
void model_assignPlayerTiles( ModelCtxt* model, XP_S16 turn,
TrayTileSet* tiles );
Tile model_getPlayerTile( ModelCtxt* model, XP_S16 turn, XP_S16 index );
Tile model_removePlayerTile( ModelCtxt* model, XP_S16 turn, XP_S16 index );
void model_addPlayerTile( ModelCtxt* model, XP_S16 turn, XP_S16 index,
Tile tile );
/* As an optimization, return a pointer to the model's array of tiles for a
player. Don't even think about modifying the array!!!! */
const TrayTileSet* model_getPlayerTiles( ModelCtxt* model, XP_S16 turn );
XP_U16 model_getNumTilesInTray( ModelCtxt* model, XP_S16 turn );
XP_U16 model_getNumTilesTotal( ModelCtxt* model, XP_S16 turn );
void model_moveBoardToTray( ModelCtxt* model, XP_S16 turn, XP_S16 index );
void model_moveTrayToBoard( ModelCtxt* model, XP_S16 turn, XP_U16 col,
XP_U16 row, XP_S16 tileIndex, Tile blankFace );
XP_S16 model_trayContains( ModelCtxt* model, XP_S16 turn, Tile tile );
/* void model_setTile( ModelCtxt* model, XP_U16 col, XP_U16 row, */
/* Tile tile, XP_Bool isBlank ); */
XP_U16 model_numRows( ModelCtxt* model );
XP_U16 model_numCols( ModelCtxt* model );
/* XP_U16 model_numTilesCurrentTray( ModelCtxt* model ); */
/* Tile model_currentTrayTile( ModelCtxt* model, XP_U16 index ); */
void model_addToCurrentMove( ModelCtxt* model, XP_S16 turn,
XP_U16 col, XP_U16 row,
Tile tile, XP_Bool isBlank );
XP_U16 model_getCurrentMoveCount( ModelCtxt* model, XP_S16 turn );
void model_getCurrentMoveTile( ModelCtxt* model, XP_S16 turn, XP_S16* index,
Tile* tile, XP_U16* col, XP_U16* row,
XP_Bool* isBlank );
void model_commitTurn( ModelCtxt* model, XP_S16 player,
TrayTileSet* newTiles );
void model_commitRejectedPhony( ModelCtxt* model, XP_S16 player );
void model_makeTileTrade( ModelCtxt* model, XP_S16 player,
TrayTileSet* oldTiles, TrayTileSet* newTiles );
XP_Bool model_undoLatestMoves( ModelCtxt* model, PoolContext* pool,
XP_U16 nMovesSought, XP_U16* turn,
XP_S16* moveNum );
void model_rejectPreviousMove( ModelCtxt* model, PoolContext* pool,
XP_U16* turn );
void model_trayToStream( ModelCtxt* model, XP_S16 turn,
XWStreamCtxt* stream );
void model_currentMoveToStream( ModelCtxt* model, XP_S16 turn,
XWStreamCtxt* stream);
void model_makeTurnFromStream( ModelCtxt* model, XP_U16 playerNum,
XWStreamCtxt* stream );
void model_makeTurnFromMoveInfo( ModelCtxt* model, XP_U16 playerNum,
MoveInfo* newMove );
void model_resetCurrentTurn( ModelCtxt* model, XP_S16 turn );
/********************* notification ********************/
typedef void (*BoardListener)(void* data, XP_U16 turn, XP_U16 col,
XP_U16 row, XP_Bool added );
void model_setBoardListener( ModelCtxt* model, BoardListener bl,
void* data );
typedef void (*TrayListener)(void* data, XP_U16 turn, TileBit bits );
void model_setTrayListener( ModelCtxt* model, TrayListener bl,
void* data );
void model_foreachPendingCell( ModelCtxt* model, XP_S16 turn,
BoardListener bl, void* data );
void model_foreachPrevCell( ModelCtxt* model, BoardListener bl, void* data );
void model_writeGameHistory( ModelCtxt* model, XWStreamCtxt* stream,
ServerCtxt* server, /* for player names */
XP_Bool gameOver );
/* for the tile values dialog: total all the tiles in players trays and
tentatively placed on the board. */
void model_countAllTrayTiles( ModelCtxt* model, XP_U16* counts );
/********************* scoring ********************/
typedef XP_Bool (*WordNotifierProc)( XP_UCHAR* word, void* closure );
typedef struct WordNotifierInfo {
WordNotifierProc proc;
void* closure;
} WordNotifierInfo;
XP_Bool getCurrentMoveScoreIfLegal( ModelCtxt* model, XP_S16 turn,
XWStreamCtxt* stream, XP_S16* score );
XP_S16 model_getPlayerScore( ModelCtxt* model, XP_S16 player );
XP_Bool model_checkMoveLegal( ModelCtxt* model, XP_S16 player,
XWStreamCtxt* stream,
WordNotifierInfo* notifyInfo );
void model_figureFinalScores( ModelCtxt* model, XP_S16* scores,
XP_S16* tilePenalties );
/* figureMoveScore is meant only for the engine's use */
XP_U16 figureMoveScore( ModelCtxt* model, MoveInfo* moveInfo,
EngineCtxt* engine, XWStreamCtxt* stream,
XP_Bool silent, WordNotifierInfo* notifyInfo );
/********************* persistence ********************/
#ifdef INCLUDE_IO_SUPPORT
void model_load( ModelCtxt* model, XP_Stream* inStream );
void model_store( ModelCtxt* model, XP_Stream* outStream );
#endif
#ifdef CPLUS
}
#endif
#endif

78
xwords4/common/modelp.h Normal file
View file

@ -0,0 +1,78 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 2000 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 _MODELP_H_
#define _MODELP_H_
#include "model.h"
#include "movestak.h"
#ifdef CPLUS
extern "C" {
#endif
typedef struct PendingTile {
XP_U8 col;
XP_U8 row;
Tile tile; /* includes face and blank bit */
} PendingTile;
typedef struct PlayerCtxt {
XP_S16 score;
XP_S16 curMoveScore; /* negative means illegal */
XP_Bool curMoveValid;
TrayTileSet trayTiles;
XP_U8 nPending; /* still in tray but "on board" */
PendingTile pendingTiles[MAX_TRAY_TILES];
} PlayerCtxt;
typedef struct ModelVolatiles {
XW_UtilCtxt* util;
struct CurGameInfo* gi;
DictionaryCtxt* dict;
BoardListener boardListenerFunc;
StackCtxt* stack;
void* boardListenerData;
TrayListener trayListenerFunc;
void* trayListenerData;
MPSLOT
} ModelVolatiles;
struct ModelCtxt {
ModelVolatiles vol;
CellTile tiles[MAX_COLS][MAX_ROWS];
PlayerCtxt players[MAX_NUM_PLAYERS];
XP_U16 nPlayers;
XP_U16 nCols;
XP_U16 nRows;
};
void invalidateScore( ModelCtxt* model, XP_S16 player );
XP_Bool tilesInLine( ModelCtxt* model, XP_S16 turn, XP_Bool* isHorizontal );
void normalizeMoves( ModelCtxt* model, XP_S16 turn,
XP_Bool isHorizontal, MoveInfo* moveInfo );
void adjustScoreForUndone( ModelCtxt* model, MoveInfo* mi, XP_U16 turn );
#ifdef CPLUS
}
#endif
#endif

351
xwords4/common/movestak.c Normal file
View file

@ -0,0 +1,351 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 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 "modelp.h"
#include "mempool.h"
#include "xwstream.h"
#include "movestak.h"
#include "memstream.h"
#include "strutils.h"
#ifdef CPLUS
extern "C" {
#endif
struct StackCtxt {
VTableMgr* vtmgr;
XWStreamCtxt* data;
XWStreamPos top;
XWStreamPos cachedPos;
XP_U16 cacheNext;
XP_U16 nEntries;
XP_U16 highWaterMark;
MPSLOT
};
void
stack_init( StackCtxt* stack )
{
stack->nEntries = stack->highWaterMark = 0;
stack->top = START_OF_STREAM;
/* I see little point in freeing or shrinking stack->data. It'll get
shrunk to fit as soon as we serialize/deserialize anyway. */
} /* stack_init */
StackCtxt*
stack_make( MPFORMAL VTableMgr* vtmgr )
{
StackCtxt* result = (StackCtxt*)XP_MALLOC( mpool, sizeof( *result ) );
if ( !!result ) {
XP_MEMSET( result, 0, sizeof(*result) );
MPASSIGN(result->mpool, mpool);
result->vtmgr = vtmgr;
}
return result;
} /* stack_make */
void
stack_destroy( StackCtxt* stack )
{
if ( !!stack->data ) {
stream_destroy( stack->data );
}
XP_FREE( stack->mpool, stack );
} /* stack_destroy */
void
stack_loadFromStream( StackCtxt* stack, XWStreamCtxt* stream )
{
XP_U16 nBytes = stream_getU16( stream );
if ( nBytes > 0 ) {
stack->highWaterMark = stream_getU16( stream );
stack->nEntries = stream_getU16( stream );
stack->top = stream_getU32( stream );
stack->data = mem_stream_make( MPPARM(stack->mpool) stack->vtmgr,
NULL, 0,
(MemStreamCloseCallback)NULL );
stream_copyFromStream( stack->data, stream, nBytes );
} else {
XP_ASSERT( stack->nEntries == 0 );
XP_ASSERT( stack->top == 0 );
}
} /* stack_makeFromStream */
void
stack_writeToStream( StackCtxt* stack, XWStreamCtxt* stream )
{
XP_U16 nBytes = !!stack->data? stream_getSize( stack->data ): 0;
stream_putU16( stream, nBytes );
if ( nBytes > 0 ) {
stream_putU16( stream, stack->highWaterMark );
stream_putU16( stream, stack->nEntries );
stream_putU32( stream, stack->top );
stream_setPos( stack->data, START_OF_STREAM, POS_READ );
stream_copyFromStream( stream, stack->data, nBytes );
}
} /* stack_writeToStream */
static void
pushEntry( StackCtxt* stack, StackEntry* entry )
{
XP_U16 i;
XWStreamPos oldLoc;
XP_U16 nTiles = entry->u.move.moveInfo.nTiles;
XWStreamCtxt* stream = stack->data;
if ( !stream ) {
stream = mem_stream_make( MPPARM(stack->mpool) stack->vtmgr, NULL, 0,
(MemStreamCloseCallback)NULL );
stack->data = stream;
}
oldLoc = stream_setPos( stream, stack->top, POS_WRITE );
stream_putBits( stream, 2, entry->moveType );
stream_putBits( stream, 2, entry->playerNum );
switch( entry->moveType ) {
case MOVE_TYPE:
case PHONY_TYPE:
stream_putBits( stream, NTILES_NBITS, nTiles );
stream_putBits( stream, 5, entry->u.move.moveInfo.commonCoord );
stream_putBits( stream, 1, entry->u.move.moveInfo.isHorizontal );
for ( i = 0; i < nTiles; ++i ) {
stream_putBits( stream, 5,
entry->u.move.moveInfo.tiles[i].varCoord );
stream_putBits( stream, 6, entry->u.move.moveInfo.tiles[i].tile );
}
if ( entry->moveType == MOVE_TYPE ) {
traySetToStream( stream, &entry->u.move.newTiles );
}
break;
case ASSIGN_TYPE:
traySetToStream( stream, &entry->u.assign.tiles );
break;
case TRADE_TYPE:
XP_ASSERT( entry->u.trade.newTiles.nTiles
== entry->u.trade.oldTiles.nTiles );
traySetToStream( stream, &entry->u.trade.oldTiles );
/* could save three bits per trade by just writing the tiles of the
second guy */
traySetToStream( stream, &entry->u.trade.newTiles );
break;
}
++stack->nEntries;
stack->highWaterMark = stack->nEntries;
stack->top = stream_setPos( stream, oldLoc, POS_WRITE );
} /* pushEntry */
static void
readEntry( StackCtxt* stack, StackEntry* entry )
{
XP_U16 nTiles, i;
XWStreamCtxt* stream = stack->data;
entry->moveType = (StackMoveType)stream_getBits( stream, 2 );
entry->playerNum = (XP_U8)stream_getBits( stream, 2 );
switch( entry->moveType ) {
case MOVE_TYPE:
case PHONY_TYPE:
nTiles = entry->u.move.moveInfo.nTiles =
(XP_U8)stream_getBits( stream, NTILES_NBITS );
XP_ASSERT( nTiles <= MAX_TRAY_TILES );
entry->u.move.moveInfo.commonCoord = (XP_U8)stream_getBits(stream, 5);
entry->u.move.moveInfo.isHorizontal = (XP_U8)stream_getBits(stream, 1);
for ( i = 0; i < nTiles; ++i ) {
entry->u.move.moveInfo.tiles[i].varCoord =
(XP_U8)stream_getBits(stream, 5);
entry->u.move.moveInfo.tiles[i].tile =
(Tile)stream_getBits( stream, 6 );
}
if ( entry->moveType == MOVE_TYPE ) {
traySetFromStream( stream, &entry->u.move.newTiles );
}
break;
case ASSIGN_TYPE:
traySetFromStream( stream, &entry->u.assign.tiles );
break;
case TRADE_TYPE:
traySetFromStream( stream, &entry->u.trade.oldTiles );
traySetFromStream( stream, &entry->u.trade.newTiles );
XP_ASSERT( entry->u.trade.newTiles.nTiles
== entry->u.trade.oldTiles.nTiles );
break;
}
} /* readEntry */
void
stack_addMove( StackCtxt* stack, XP_U16 turn, MoveInfo* moveInfo,
TrayTileSet* newTiles )
{
StackEntry move;
move.playerNum = (XP_U8)turn;
move.moveType = MOVE_TYPE;
XP_MEMCPY( &move.u.move.moveInfo, moveInfo, sizeof(move.u.move.moveInfo));
move.u.move.newTiles = *newTiles;
pushEntry( stack, &move );
} /* stack_addMove */
void
stack_addPhony( StackCtxt* stack, XP_U16 turn, MoveInfo* moveInfo )
{
StackEntry move;
move.playerNum = (XP_U8)turn;
move.moveType = PHONY_TYPE;
XP_MEMCPY( &move.u.phony.moveInfo, moveInfo,
sizeof(move.u.phony.moveInfo));
pushEntry( stack, &move );
} /* stack_addPhony */
void
stack_addTrade( StackCtxt* stack, XP_U16 turn,
TrayTileSet* oldTiles, TrayTileSet* newTiles )
{
StackEntry move;
move.playerNum = (XP_U8)turn;
move.moveType = TRADE_TYPE;
move.u.trade.oldTiles = *oldTiles;
move.u.trade.newTiles = *newTiles;
pushEntry( stack, &move );
} /* stack_addTrade */
void
stack_addAssign( StackCtxt* stack, XP_U16 turn, TrayTileSet* tiles )
{
StackEntry move;
move.playerNum = (XP_U8)turn;
move.moveType = ASSIGN_TYPE;
move.u.assign.tiles = *tiles;
pushEntry( stack, &move );
} /* stack_addAssign */
static XP_Bool
setCacheReadyFor( StackCtxt* stack, XP_U16 n )
{
StackEntry dummy;
XP_U16 i;
stream_setPos( stack->data, START_OF_STREAM, POS_READ );
for ( i = 0; i < n; ++i ) {
readEntry( stack, &dummy );
}
stack->cacheNext = n;
stack->cachedPos = stream_getPos( stack->data, XP_FALSE );
return XP_TRUE;
} /* setCacheReadyFor */
XP_U16
stack_getNEntries( StackCtxt* stack )
{
return stack->nEntries;
} /* stack_getNEntries */
XP_Bool
stack_getNthEntry( StackCtxt* stack, XP_U16 n, StackEntry* entry )
{
XP_Bool found;
if ( n >= stack->nEntries ) {
found = XP_FALSE;
} else if ( stack->cacheNext != n ) {
XP_ASSERT( !!stack->data );
found = setCacheReadyFor( stack, n );
XP_ASSERT( stack->cacheNext == n );
} else {
found = XP_TRUE;
}
if ( found ) {
XWStreamPos oldPos = stream_setPos( stack->data, stack->cachedPos,
POS_READ );
readEntry( stack, entry );
entry->moveNum = (XP_U8)n;
stack->cachedPos = stream_setPos( stack->data, oldPos, POS_READ );
++stack->cacheNext;
}
return found;
} /* stack_getNthEntry */
XP_Bool
stack_popEntry( StackCtxt* stack, StackEntry* entry )
{
XP_U16 n = stack->nEntries - 1;
XP_Bool found = stack_getNthEntry( stack, n, entry );
if ( found ) {
stack->nEntries = n;
setCacheReadyFor( stack, n ); /* set cachedPos by side-effect */
stack->top = stack->cachedPos;
}
return found;
} /* stack_popEntry */
void
stack_redo( StackCtxt* stack )
{
if( (stack->nEntries + 1) <= stack->highWaterMark ) {
++stack->nEntries;
setCacheReadyFor( stack, stack->nEntries );
stack->top = stack->cachedPos;
}
} /* stack_redo */
#ifdef CPLUS
}
#endif

94
xwords4/common/movestak.h Normal file
View file

@ -0,0 +1,94 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 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 _MOVESTAK_H_
#define _MOVESTAK_H_
#include "comtypes.h"
#include "model.h"
#include "vtabmgr.h"
#ifdef CPLUS
extern "C" {
#endif
enum { ASSIGN_TYPE, MOVE_TYPE, TRADE_TYPE, PHONY_TYPE };
typedef XP_U8 StackMoveType;
typedef struct AssignRec {
TrayTileSet tiles;
} AssignRec;
typedef struct TradeRec {
TrayTileSet oldTiles;
TrayTileSet newTiles;
} TradeRec;
typedef struct MoveRec {
MoveInfo moveInfo;
TrayTileSet newTiles;
} MoveRec;
typedef struct PhonyRec {
MoveInfo moveInfo;
} PhonyRec;
typedef union EntryData {
AssignRec assign;
TradeRec trade;
MoveRec move;
PhonyRec phony;
} EntryData;
typedef struct StackEntry {
StackMoveType moveType;
XP_U8 playerNum;
XP_U8 moveNum;
EntryData u;
} StackEntry;
typedef struct StackCtxt StackCtxt;
StackCtxt* stack_make( MPFORMAL VTableMgr* vtmgr );
void stack_destroy( StackCtxt* stack );
void stack_init( StackCtxt* stack );
void stack_loadFromStream( StackCtxt* stack, XWStreamCtxt* stream );
void stack_writeToStream( StackCtxt* stack, XWStreamCtxt* stream );
void stack_addMove( StackCtxt* stack, XP_U16 turn, MoveInfo* moveInfo,
TrayTileSet* newTiles );
void stack_addPhony( StackCtxt* stack, XP_U16 turn, MoveInfo* moveInfo );
void stack_addTrade( StackCtxt* stack, XP_U16 turn,
TrayTileSet* oldTiles, TrayTileSet* newTiles );
void stack_addAssign( StackCtxt* stack, XP_U16 turn, TrayTileSet* tiles );
XP_U16 stack_getNEntries( StackCtxt* stack );
XP_Bool stack_getNthEntry( StackCtxt* stack, XP_U16 n, StackEntry* entry );
XP_Bool stack_popEntry( StackCtxt* stack, StackEntry* entry );
void stack_redo( StackCtxt* stack );
#ifdef CPLUS
}
#endif
#endif

828
xwords4/common/mscore.c Normal file
View file

@ -0,0 +1,828 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 1998-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 "modelp.h"
#include "util.h"
#include "engine.h"
#include "game.h"
#include "LocalizedStrIncludes.h"
#ifdef CPLUS
extern "C" {
#endif
#define IMPOSSIBLY_LOW_PENALTY (-20*MAX_TRAY_TILES)
/****************************** prototypes ******************************/
static XP_Bool isLegalMove( ModelCtxt* model, MoveInfo* moves, XP_Bool silent );
static XP_U16 word_multiplier( ModelCtxt* model, XP_U16 col, XP_U16 row );
static XP_U16 find_end( ModelCtxt* model, XP_U16 col, XP_U16 row,
XP_Bool isHorizontal );
static XP_U16 find_start( ModelCtxt* model, XP_U16 col, XP_U16 row,
XP_Bool isHorizontal );
static XP_S16 checkScoreMove( ModelCtxt* model, XP_S16 turn,
EngineCtxt* engine, XWStreamCtxt* stream,
XP_Bool silent, WordNotifierInfo* notifyInfo );
/* static XP_U16 figureWordScore( ModelCtxt* model, MoveInfo* moveInfo, */
/* EngineCtxt* engine, */
/* XP_Bool silent, short moveMultiplier, */
/* WordNotifierInfo* notifyInfo ); */
static XP_U16 scoreWord( ModelCtxt* model, MoveInfo* movei,
EngineCtxt* engine, XWStreamCtxt* stream,
XP_Bool silent, WordNotifierInfo* notifyInfo );
/* for formatting when caller wants an explanation of the score. These live
in separate function called only when stream != NULL so that they'll have
as little impact as possible on the speed when the robot's looking for FAST
scoring */
typedef struct WordScoreFormatter {
DictionaryCtxt* dict;
XWStreamCtxt* stream;
XP_UCHAR fullBuf[80];
XP_U16 bufLen, nTiles;
XP_Bool firstPass;
} WordScoreFormatter;
static void wordScoreFormatterInit( WordScoreFormatter* fmtr,
XWStreamCtxt* stream,
DictionaryCtxt* dict );
static void wordScoreFormatterAddTile( WordScoreFormatter* fmtr, Tile tile,
XP_U16 tileMultiplier,
XP_Bool isBlank );
static void wordScoreFormatterFinish( WordScoreFormatter* fmtr, Tile* word );
static void formatWordScore( XWStreamCtxt* stream, XP_U16 wordScore,
XP_U16 moveMultiplier );
static void formatSummary( XWStreamCtxt* stream, ModelCtxt* model,
XP_U16 score );
/* Calculate the score of the current move as it stands. Flag the score
* current so we won't have to do this again until something changes to
* invalidate the score.
*/
static void
scoreCurrentMove( ModelCtxt* model, XP_S16 turn, XWStreamCtxt* stream )
{
PlayerCtxt* player = &model->players[turn];
XP_S16 score;
XP_ASSERT( !player->curMoveValid );
/* recalc goes here */
score = checkScoreMove( model, turn, (EngineCtxt*)NULL, stream,
XP_TRUE, (WordNotifierInfo*)NULL );
XP_ASSERT( score >= 0 || score == ILLEGAL_MOVE_SCORE );
player->curMoveScore = score;
player->curMoveValid = XP_TRUE;
} /* scoreCurrentMove */
void
adjustScoreForUndone( ModelCtxt* model, MoveInfo* mi, XP_U16 turn )
{
XP_U16 moveScore;
PlayerCtxt* player = &model->players[turn];
if ( mi->nTiles == 0 ) {
moveScore = 0;
} else {
moveScore = figureMoveScore( model, mi, (EngineCtxt*)NULL,
(XWStreamCtxt*)NULL, XP_TRUE,
(WordNotifierInfo*)NULL );
}
player->score -= moveScore;
player->curMoveScore = 0;
player->curMoveValid = XP_TRUE;
} /* adjustScoreForUndone */
XP_Bool
model_checkMoveLegal( ModelCtxt* model, XP_S16 turn, XWStreamCtxt* stream,
WordNotifierInfo* notifyInfo )
{
XP_S16 score;
score = checkScoreMove( model, turn, (EngineCtxt*)NULL, stream, XP_FALSE,
notifyInfo );
return score != ILLEGAL_MOVE_SCORE;
} /* model_checkMoveLegal */
void
invalidateScore( ModelCtxt* model, XP_S16 turn )
{
model->players[turn].curMoveValid = XP_FALSE;
} /* invalidateScore */
XP_Bool
getCurrentMoveScoreIfLegal( ModelCtxt* model, XP_S16 turn,
XWStreamCtxt* stream, XP_S16* score )
{
PlayerCtxt* player = &model->players[turn];
if ( !player->curMoveValid ) {
scoreCurrentMove( model, turn, stream );
}
*score = player->curMoveScore;
return player->curMoveScore != ILLEGAL_MOVE_SCORE;
} /* getCurrentMoveScoreIfLegal */
XP_S16
model_getPlayerScore( ModelCtxt* model, XP_S16 player )
{
return model->players[player].score;
} /* model_getPlayerScore */
/* Based on the current scores based on tiles played and the tiles left in the
* tray, return an array giving the left-over-tile-adjusted scores for each
* player.
*/
void
model_figureFinalScores( ModelCtxt* model, XP_S16* finalScoresP,
XP_S16* tilePenalties )
{
XP_S16 i, j;
XP_S16 penalties[MAX_NUM_PLAYERS];
XP_S16 totalPenalty;
XP_U16 nPlayers = model->nPlayers;
XP_S16 firstDoneIndex = -1; /* not set unless FIRST_DONE_BONUS is set */
const TrayTileSet* tray;
PlayerCtxt* player;
DictionaryCtxt* dict = model_getDictionary( model );
CurGameInfo* gi = model->vol.gi;
XP_MEMSET( finalScoresP, 0, sizeof(*finalScoresP) * MAX_NUM_PLAYERS );
totalPenalty = 0;
for ( player = model->players, i = 0; i < nPlayers; ++player, ++i ) {
tray = model_getPlayerTiles( model, i );
penalties[i] = 0;
/* if there are no tiles left and this guy's the first done, make a
note of it in case he's to get a bonus. Note that this assumes
only one player can be out of tiles. */
if ( (tray->nTiles == 0) && (firstDoneIndex == -1) ) {
firstDoneIndex = i;
} else {
for ( j = tray->nTiles-1; j >= 0; --j ) {
penalties[i] += dict_getTileValue( dict, tray->tiles[j] );
}
}
/* include tiles in pending move too for the player whose turn it
is. */
for ( j = player->nPending - 1; j >= 0; --j ) {
Tile tile = player->pendingTiles[j].tile;
penalties[i] += dict_getTileValue(dict, (Tile)(tile & TILE_VALUE_MASK));
}
totalPenalty += penalties[i];
}
/* now total everybody's scores */
for ( i = 0; i < nPlayers; ++i ) {
XP_S16 score = model_getPlayerScore( model, i );
XP_S16 penalty;
penalty = (i == firstDoneIndex)? totalPenalty: -penalties[i];
finalScoresP[i] = score + penalty;
if ( !!tilePenalties ) {
tilePenalties[i] = penalty;
}
if ( gi->timerEnabled ) {
penalty = player_timePenalty( gi, i );
finalScoresP[i] -= penalty;
}
}
} /* model_figureFinalScores */
/* checkScoreMove.
* Negative score means illegal.
*/
static XP_S16
checkScoreMove( ModelCtxt* model, XP_S16 turn, EngineCtxt* engine,
XWStreamCtxt* stream, XP_Bool silent,
WordNotifierInfo* notifyInfo )
{
XP_Bool isHorizontal;
XP_S16 score = ILLEGAL_MOVE_SCORE;
PlayerCtxt* player = &model->players[turn];
XP_ASSERT( player->nPending <= MAX_TRAY_TILES );
if ( player->nPending == 0 ) {
return 0;
}
if ( tilesInLine( model, turn, &isHorizontal ) ) {
MoveInfo moveInfo;
normalizeMoves( model, turn, isHorizontal, &moveInfo );
if ( isLegalMove( model, &moveInfo, silent ) ) {
score = figureMoveScore( model, &moveInfo, engine, stream,
silent, notifyInfo );
}
} else if ( !silent ) { /* tiles out of line */
util_userError( model->vol.util, ERR_TILES_NOT_IN_LINE );
}
return score;
} /* checkScoreMove */
XP_Bool
tilesInLine( ModelCtxt* model, XP_S16 turn, XP_Bool* isHorizontal )
{
XP_Bool xIsCommon, yIsCommon;
PlayerCtxt* player = &model->players[turn];
PendingTile* pt = player->pendingTiles;
XP_U16 commonX = pt->col;
XP_U16 commonY = pt->row;
short i;
xIsCommon = yIsCommon = XP_TRUE;
for ( i = 1; ++pt, i < player->nPending; ++i ) {
// test the boolean first in case it's already been made false
// (to save time)
if ( xIsCommon && (pt->col != commonX) ) {
xIsCommon = XP_FALSE;
}
if ( yIsCommon && (pt->row != commonY) ) {
yIsCommon = XP_FALSE;
}
}
*isHorizontal = !xIsCommon; // so will be vertical if both true
return xIsCommon || yIsCommon;
} /* tilesInLine */
void
normalizeMoves( ModelCtxt* model, XP_S16 turn, XP_Bool isHorizontal,
MoveInfo* moveInfo )
{
XP_S16 lowCol, i, j, thisCol; /* unsigned is a problem on palm */
PlayerCtxt* player = &model->players[turn];
XP_U16 nTiles = player->nPending;
XP_S16 lastTaken;
short lowIndex = 0;
PendingTile* pt;
moveInfo->isHorizontal = isHorizontal;
moveInfo->nTiles = (XP_U8)nTiles;
lastTaken = -1;
for ( i = 0; i < nTiles; ++i ) {
lowCol = 100; /* high enough to always be changed */
for ( j = 0; j < nTiles; ++j ) {
pt = &player->pendingTiles[j];
thisCol = isHorizontal? pt->col:pt->row;
if (thisCol < lowCol && thisCol > lastTaken ) {
lowCol = thisCol;
lowIndex = j;
}
}
/* we've found the next to transfer (4 bytes smaller without a temp
local ptr. */
pt = &player->pendingTiles[lowIndex];
moveInfo->tiles[i].varCoord = (XP_U8)(lastTaken = lowCol);
moveInfo->tiles[i].tile = pt->tile;
}
pt = &player->pendingTiles[0];
moveInfo->commonCoord = isHorizontal? pt->row:pt->col;
} /* normalizeMoves */
static XP_Bool
modelIsEmptyAt( ModelCtxt* model, XP_U16 col, XP_U16 row )
{
Tile tile;
XP_Bool ignore;
XP_Bool found;
found = model_getTile( model, col, row, XP_FALSE, -1, &tile,
&ignore, &ignore, (XP_Bool*)NULL );
return !found;
} /* modelIsEmptyAt */
/*****************************************************************************
* Called only after moves have been confirmed to be in the same row, this
* function works whether the word is horizontal or vertical.
*
* For a move to be legal, either of the following must be true: a)
* if there are squares between those added in this move they must be occupied
* by previously placed pieces; or b) if these pieces are contiguous then at
* least one must touch a previously played piece (unless this is the first
* move) NOTE: this function does not verify that a newly placed piece is on an
* empty square. It's assumed that the calling code, most likely that which
* handles dragging the tiles, will have taken care of that.
****************************************************************************/
static XP_Bool
isLegalMove( ModelCtxt* model, MoveInfo* mInfo, XP_Bool silent )
{
XP_S16 high, low;
XP_S16 col, row;
XP_S16* incr;
XP_S16* commonP;
XP_U16 star_row = model_numRows(model) / 2;
XP_S16 nTiles = mInfo->nTiles;
MoveInfoTile* moves = mInfo->tiles;
XP_U16 commonCoord = mInfo->commonCoord;
/* First figure out what the low and high coordinates are in the dimension
not in common */
low = moves[0].varCoord;
high = moves[nTiles-1].varCoord;
XP_ASSERT( (nTiles == 1) || (low < high) );
if ( mInfo->isHorizontal ) {
row = commonCoord;
incr = &col;
commonP = &row;
} else {
col = commonCoord;
incr = &row;
commonP = &col;
}
/* are we looking at 2a above? */
if ( (high - low + 1) > nTiles ) {
/* there should be no empty tiles between the ends */
MoveInfoTile* newTile = moves; /* the newly placed tile to be checked */
for ( *incr = low; *incr <= high; ++*incr ) {
if ( newTile->varCoord == *incr ) {
++newTile;
} else if ( modelIsEmptyAt( model, col, row ) ) {
if ( !silent ) {
util_userError( model->vol.util, ERR_NO_EMPTIES_IN_TURN );
}
return XP_FALSE;
}
}
XP_ASSERT( newTile == &moves[nTiles] );
return XP_TRUE;
/* else we're looking at 2b: make sure there's some contact UNLESS
this is the first move */
} else {
/* check the ends first */
if ( low != 0 ) {
*incr = low - 1;
if ( !modelIsEmptyAt( model, col, row ) ) {
return XP_TRUE;
}
}
if ( high != MAX_ROWS-1 ) {
*incr = high+1;
if ( !modelIsEmptyAt( model, col, row ) ) {
return XP_TRUE;
}
}
/* now the neighbors above... */
if ( commonCoord != 0 ) {
--*commonP; /* decrement whatever's not being looped over */
for ( *incr = low; *incr <= high; ++*incr ) {
if ( !modelIsEmptyAt( model, col, row ) ) {
return XP_TRUE;
}
}
++*commonP; /* undo the decrement */
}
/* ...and below */
if ( commonCoord <= MAX_ROWS - 1 ) {
++*commonP;
for ( *incr = low; *incr <= high; ++*incr ) {
if ( !modelIsEmptyAt( model, col, row ) ) {
return XP_TRUE;
}
}
--*commonP;
}
/* if we got here, it's illegal unless this is the first move -- i.e.
unless one of the tiles is on the STAR */
if ( ( commonCoord == star_row) &&
( low <= star_row) && ( high >= star_row ) ) {
if ( nTiles > 1 ) {
return XP_TRUE;
} else {
if ( !silent ) {
util_userError(model->vol.util, ERR_TWO_TILES_FIRST_MOVE);
}
return XP_FALSE;
}
} else {
if ( !silent ) {
util_userError( model->vol.util, ERR_TILES_MUST_CONTACT );
}
return XP_FALSE;
}
}
XP_ASSERT( XP_FALSE );
return XP_FALSE; /* keep compiler happy */
} /* isLegalMove */
XP_U16
figureMoveScore( ModelCtxt* model, MoveInfo* moveInfo, EngineCtxt* engine,
XWStreamCtxt* stream, XP_Bool silent,
WordNotifierInfo* notifyInfo )
{
XP_U16 col, row;
XP_U16* incr;
XP_U16 oneScore;
XP_U16 score = 0;
short i;
short moveMultiplier = 1;
short multipliers[MAX_TRAY_TILES];
MoveInfo tmpMI;
MoveInfoTile* tiles;
XP_U16 nTiles = moveInfo->nTiles;
XP_ASSERT( nTiles > 0 );
if ( moveInfo->isHorizontal ) {
row = moveInfo->commonCoord;
incr = &col;
} else {
col = moveInfo->commonCoord;
incr = &row;
}
for ( i = 0; i < nTiles; ++i ) {
*incr = moveInfo->tiles[i].varCoord;
moveMultiplier *= multipliers[i] = word_multiplier( model, col, row );
}
oneScore = scoreWord( model, moveInfo, (EngineCtxt*)NULL, stream,
silent, notifyInfo );
if ( !!stream ) {
formatWordScore( stream, oneScore, moveMultiplier );
}
oneScore *= moveMultiplier;
score += oneScore;
/* set up the invariant slots in tmpMI */
tmpMI.isHorizontal = !moveInfo->isHorizontal;
tmpMI.nTiles = 1;
tmpMI.tiles[0].varCoord = moveInfo->commonCoord;
for ( i = 0, tiles = moveInfo->tiles; i < nTiles; ++i, ++tiles ) {
tmpMI.commonCoord = tiles->varCoord;
tmpMI.tiles[0].tile = tiles->tile;
oneScore = scoreWord( model, &tmpMI, engine, stream, silent,
notifyInfo );
if ( !!stream ) {
formatWordScore( stream, oneScore, multipliers[i] );
}
oneScore *= multipliers[i];
score += oneScore;
}
/* did he use all 7 tiles? */
if ( nTiles == MAX_TRAY_TILES ) {
score += EMPTIED_TRAY_BONUS;
if ( !!stream ) {
XP_UCHAR* bstr = util_getUserString( model->vol.util,
STR_BONUS_ALL );
stream_putBytes( stream, bstr, (XP_U8)XP_STRLEN(bstr) );
}
}
if ( !!stream ) {
formatSummary( stream, model, score );
}
return score;
} /* figureMoveScore */
static XP_U16
word_multiplier( ModelCtxt* model, XP_U16 col, XP_U16 row )
{
XWBonusType bonus = util_getSquareBonus( model->vol.util, model, col, row );
switch ( bonus ) {
case BONUS_DOUBLE_WORD:
return 2;
case BONUS_TRIPLE_WORD:
return 3;
default:
return 1;
}
} /* word_multiplier */
static XP_U16
tile_multiplier( ModelCtxt* model, XP_U16 col, XP_U16 row )
{
XWBonusType bonus = util_getSquareBonus( model->vol.util, model,
col, row );
switch ( bonus ) {
case BONUS_DOUBLE_LETTER:
return 2;
case BONUS_TRIPLE_LETTER:
return 3;
default:
return 1;
}
} /* tile_multiplier */
static XP_U16
scoreWord( ModelCtxt* model, MoveInfo* movei, /* new tiles */
EngineCtxt* engine,/* for crosswise caching */
XWStreamCtxt* stream,
XP_Bool silent, /* report error via dialog */
WordNotifierInfo* notifyInfo )
{
XP_U16 tileMultiplier;
XP_U16 restScore, scoreFromCache;
XP_U16 thisTileValue;
XP_U16 nTiles = movei->nTiles;
Tile tile;
XP_U16 start, end;
XP_U16* incr;
XP_U16 col, row;
MoveInfoTile* tiles = movei->tiles;
XP_U16 firstCoord = tiles->varCoord;
DictionaryCtxt* dict = model->vol.dict;
WordScoreFormatter fmtr;
if ( !!stream ) {
wordScoreFormatterInit( &fmtr, stream, dict );
}
if ( movei->isHorizontal ) {
row = movei->commonCoord;
incr = &col;
} else {
col = movei->commonCoord;
incr = &row;
}
*incr = tiles[nTiles-1].varCoord;
end = find_end( model, col, row, movei->isHorizontal );
/* This is the value *incr needs to start with below */
*incr = tiles[0].varCoord;
start = find_start( model, col, row, movei->isHorizontal );
if ( (end - start) < 1 ) { /* one-letter word: score 0 */
return 0;
}
if ( IS_BLANK(tiles->tile) ) {
tile = dict_getBlankTile( dict );
} else {
tile = tiles->tile & TILE_VALUE_MASK;
}
thisTileValue = dict_getTileValue( dict, tile );
XP_ASSERT( *incr == tiles[0].varCoord );
thisTileValue *= tile_multiplier( model, col, row );
XP_ASSERT( engine == NULL || nTiles == 1 );
if ( engine != NULL ) {
XP_ASSERT( nTiles==1 );
scoreFromCache = engine_getScoreCache( engine, movei->commonCoord );
}
/* for a while, at least, calculate and use the cached crosscheck score
* each time through in the debug case */
if ( 0 ) { /* makes keeping parens balanced easier */
#ifdef DEBUG
} else if ( 1 ) {
#else
} else if ( engine == NULL ) {
#endif
Tile checkWordBuf[MAX_ROWS];
Tile* curTile = checkWordBuf;
restScore = 0;
for ( *incr = start; *incr <= end; ++*incr ) {
XP_U16 tileScore = 0;
XP_Bool isBlank;
/* a new move? */
if ( (nTiles > 0) && (*incr == tiles->varCoord) ) {
tile = tiles->tile & TILE_VALUE_MASK;
isBlank = IS_BLANK(tiles->tile);
/* don't call localGetBlankTile when in silent (robot called)
* mode, as the blank won't be known there. (Assert will
* fail.) */
tileMultiplier = tile_multiplier( model, col, row );
++tiles;
--nTiles;
} else { /* placed on the board before this move */
XP_Bool ignore;
tileMultiplier = 1;
(void)model_getTile( model, col, row, XP_FALSE, -1, &tile,
&isBlank, &ignore, (XP_Bool*)NULL );
XP_ASSERT( (tile & TILE_VALUE_MASK) == tile );
}
*curTile++ = tile; /* save in case we're checking phonies */
if ( !!stream ) {
wordScoreFormatterAddTile( &fmtr, tile, tileMultiplier,
isBlank );
}
if ( isBlank ) {
tile = dict_getBlankTile( dict );
}
tileScore = dict_getTileValue( dict, tile );
/* The first tile in the move is already accounted for in
thisTileValue, so skip it here. */
if ( *incr != firstCoord ) {
restScore += tileScore * tileMultiplier;
}
} /* for each tile */
if ( !!notifyInfo ) {
XP_U16 len = curTile - checkWordBuf;
XP_Bool legal = engine_check( dict, checkWordBuf, len );
if ( !legal ) {
XP_UCHAR buf[(MAX_ROWS*2)+1];
dict_tilesToString( dict, checkWordBuf, len, buf );
(*notifyInfo->proc)( buf, notifyInfo->closure );
}
}
if ( !!stream ) {
wordScoreFormatterFinish( &fmtr, checkWordBuf );
}
#ifdef DEBUG
} else if ( engine != NULL ) {
#else
} else { /* non-debug case we know it's non-null */
#endif
XP_ASSERT( nTiles==1 );
XP_ASSERT( engine_getScoreCache( engine, movei->commonCoord )
== restScore );
restScore = engine_getScoreCache( engine, movei->commonCoord );
}
return (restScore + thisTileValue);
} /* scoreWord */
static XP_U16
find_start( ModelCtxt* model, XP_U16 col, XP_U16 row, XP_Bool isHorizontal )
{
XP_U16* incr = isHorizontal? &col: &row;
for ( ; ; ) {
if ( *incr == 0 ) {
return 0;
} else {
--*incr;
if ( modelIsEmptyAt( model, col, row ) ) {
return *incr + 1;
}
}
}
} /* find_start */
static XP_U16
find_end( ModelCtxt* model, XP_U16 col, XP_U16 row, XP_Bool isHorizontal )
{
XP_U16* incr = isHorizontal? &col: &row;
XP_U16 limit = isHorizontal? MAX_COLS-1:MAX_ROWS-1;
XP_U16 lastGood = *incr;
XP_ASSERT( col < MAX_COLS );
XP_ASSERT( row < MAX_ROWS );
for ( ; ; ) {
XP_ASSERT( *incr <= limit );
if ( *incr == limit ) {
return limit;
} else {
++*incr;
if ( modelIsEmptyAt( model, col, row ) ) {
return lastGood;
} else {
lastGood = *incr;
}
}
}
} /* find_end */
static void
wordScoreFormatterInit( WordScoreFormatter* fmtr, XWStreamCtxt* stream,
DictionaryCtxt* dict )
{
XP_ASSERT( !!stream );
fmtr->stream = stream;
fmtr->dict = dict;
fmtr->bufLen = 0;
fmtr->nTiles = 0;
fmtr->firstPass = XP_TRUE;
} /* initWordScoreFormatter */
static void
wordScoreFormatterAddTile( WordScoreFormatter* fmtr, Tile tile,
XP_U16 tileMultiplier, XP_Bool isBlank )
{
XP_UCHAR buf[4];
XP_UCHAR* fullBufPtr;
XP_UCHAR* prefix;
XP_U16 tileScore;
++fmtr->nTiles;
dict_tilesToString( fmtr->dict, &tile, 1, buf );
if ( isBlank ) {
tile = dict_getBlankTile( fmtr->dict );
}
tileScore = dict_getTileValue( fmtr->dict, tile );
if ( fmtr->firstPass ) {
prefix = (XP_UCHAR*)" [";
fmtr->firstPass = XP_FALSE;
} else {
prefix = (XP_UCHAR*)"+";
}
fullBufPtr = fmtr->fullBuf + fmtr->bufLen;
fmtr->bufLen +=
XP_SNPRINTF( fullBufPtr,
(XP_U16)(sizeof(fmtr->fullBuf) - fmtr->bufLen),
(XP_UCHAR*)(tileMultiplier > 1?"%s(%dx%d)":"%s%d"),
prefix, tileScore, tileMultiplier );
XP_ASSERT( XP_STRLEN(fmtr->fullBuf) == fmtr->bufLen );
XP_ASSERT( fmtr->bufLen < sizeof(fmtr->fullBuf) );
} /* wordScoreFormatterAddTile */
static void
wordScoreFormatterFinish( WordScoreFormatter* fmtr, Tile* word )
{
XP_UCHAR buf[(MAX_ROWS*2)+1];
XP_U16 len = dict_tilesToString( fmtr->dict, word, fmtr->nTiles, buf );
stream_putBytes( fmtr->stream, buf, len );
stream_putBytes( fmtr->stream, fmtr->fullBuf, fmtr->bufLen );
stream_putU8( fmtr->stream, ']' );
} /* wordScoreFormatterFinish */
static void
formatWordScore( XWStreamCtxt* stream, XP_U16 wordScore,
XP_U16 moveMultiplier )
{
if ( wordScore > 0 ) {
XP_U16 multipliedScore = wordScore * moveMultiplier;
XP_UCHAR tmpBuf[40];
if ( moveMultiplier > 1 ) {
XP_SNPRINTF( tmpBuf, sizeof(tmpBuf),
(XP_UCHAR*)" => %d x %d = %d" XP_CR,
wordScore, moveMultiplier, multipliedScore );
} else {
XP_SNPRINTF( tmpBuf, sizeof(tmpBuf), (XP_UCHAR*)" = %d" XP_CR,
multipliedScore );
}
XP_ASSERT( XP_STRLEN(tmpBuf) < sizeof(tmpBuf) );
stream_putBytes( stream, tmpBuf, (XP_U16)XP_STRLEN(tmpBuf) );
}
} /* formatWordScore */
static void
formatSummary( XWStreamCtxt* stream, ModelCtxt* model, XP_U16 score )
{
XP_UCHAR buf[60];
XP_SNPRINTF(buf, sizeof(buf),
util_getUserString(model->vol.util, STRD_TURN_SCORE),
score);
XP_ASSERT( XP_STRLEN(buf) < sizeof(buf) );
stream_putBytes( stream, buf, (XP_U16)XP_STRLEN(buf) );
} /* formatSummary */
#ifdef CPLUS
}
#endif

233
xwords4/common/pool.c Normal file
View file

@ -0,0 +1,233 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 2000 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 <assert.h> */
#include "pool.h"
#include "dictnry.h"
#include "xwstream.h"
#define pEND 0x70454e44
// #define BLANKS_FIRST 1
struct PoolContext {
XP_U8* lettersLeft;
XP_U16 numTilesLeft;
XP_U16 numFaces;
#ifdef BLANKS_FIRST
XP_S16 blankIndex;
#endif
MPSLOT
};
PoolContext*
pool_make( MPFORMAL_NOCOMMA )
{
PoolContext* result = (PoolContext*)XP_MALLOC(mpool, sizeof(*result) );
if ( result != NULL ) {
XP_MEMSET( result, 0, sizeof( *result ) );
MPASSIGN(result->mpool, mpool);
#ifdef BLANKS_FIRST
result->blankIndex = -1;
#endif
}
return result;
} /* pool_make */
void
pool_writeToStream( PoolContext* pool, XWStreamCtxt* stream )
{
stream_putU16( stream, pool->numTilesLeft );
stream_putU16( stream, pool->numFaces );
stream_putBytes( stream, pool->lettersLeft,
(XP_U16)(pool->numFaces * sizeof(pool->lettersLeft[0])) );
#ifdef DEBUG
stream_putU32( stream, pEND );
#endif
} /* pool_writeToStream */
PoolContext*
pool_makeFromStream( MPFORMAL XWStreamCtxt* stream )
{
PoolContext* pool = pool_make( MPPARM_NOCOMMA(mpool) );
pool->numTilesLeft = stream_getU16( stream );
pool->numFaces = stream_getU16( stream );
pool->lettersLeft = (XP_U8*)
XP_MALLOC( mpool, pool->numFaces * sizeof(pool->lettersLeft[0]) );
stream_getBytes( stream, pool->lettersLeft,
(XP_U16)(pool->numFaces * sizeof(pool->lettersLeft[0])) );
XP_ASSERT( stream_getU32( stream ) == pEND );
return pool;
} /* pool_makeFromStream */
void
pool_destroy( PoolContext* pool )
{
XP_ASSERT( pool != NULL );
XP_FREE( pool->mpool, pool->lettersLeft );
XP_FREE( pool->mpool, pool );
} /* pool_destroy */
static Tile
getNthPoolTile( PoolContext* pool, short index )
{
Tile result;
/* given an array of counts of remaining letters, subtract each in turn
from the total we seek until that total is at or below zero. The count
that put it (or would put it) under 0 is the one to pick. */
if ( 0 ) {
#ifdef BLANKS_FIRST
} else if ( pool->blankIndex >= 0 && pool->lettersLeft[pool->blankIndex] > 0 ) {
result = pool->blankIndex;
#endif
} else {
XP_S16 nextCount = index;
Tile curLetter = 0;
for ( ; ; ) {
nextCount -= pool->lettersLeft[(short)curLetter];
if ( nextCount < 0 ) {
XP_ASSERT( pool->lettersLeft[(short)curLetter] > 0 );
result = curLetter;
break;
} else {
++curLetter;
}
}
}
return result;
} /* getNthPoolTile */
static Tile
getRandomTile( PoolContext* pool )
{
XP_U16 index = (XP_U16)(XP_RANDOM() % pool->numTilesLeft);
Tile result;
result = getNthPoolTile( pool, index );
--pool->lettersLeft[result];
--pool->numTilesLeft;
return result;
} /* getRandomTile */
void
pool_requestTiles( PoolContext* pool, Tile* tiles, XP_U8* maxNum )
{
XP_S16 numWanted = *maxNum;
XP_U16 numWritten = 0;
XP_ASSERT( numWanted >= 0 );
while ( pool->numTilesLeft > 0 && numWanted-- ) {
Tile t = getRandomTile( pool );
*tiles++ = t;
++numWritten;
}
*maxNum = (XP_U8)numWritten;
} /* pool_requestTiles */
void
pool_replaceTiles( PoolContext* pool, TrayTileSet* tiles )
{
XP_U16 nTiles = tiles->nTiles;
Tile* tilesP = tiles->tiles;
while ( nTiles-- ) {
Tile tile = *tilesP++; /* do I need to filter off high bits? */
XP_ASSERT( nTiles < MAX_TRAY_TILES );
XP_ASSERT( tile < pool->numFaces );
++pool->lettersLeft[tile];
++pool->numTilesLeft;
}
} /* pool_replaceTiles */
void
pool_removeTiles( PoolContext* pool, TrayTileSet* tiles )
{
XP_U16 nTiles = tiles->nTiles;
Tile* tilesP = tiles->tiles;
XP_ASSERT( nTiles <= MAX_TRAY_TILES );
while ( nTiles-- ) {
Tile tile = *tilesP++; /* do I need to filter off high bits? */
XP_ASSERT( tile < pool->numFaces );
XP_ASSERT( pool->lettersLeft[tile] > 0 );
XP_ASSERT( pool->numTilesLeft > 0 );
--pool->lettersLeft[tile];
--pool->numTilesLeft;
}
} /* pool_removeTiles */
XP_U16
pool_getNTilesLeft( PoolContext* pool )
{
return pool->numTilesLeft;
} /* pool_remainingTileCount */
XP_U16
pool_getNTilesLeftFor( PoolContext* pool, Tile tile )
{
return pool->lettersLeft[tile];
} /* pool_remainingTileCount */
void
pool_initFromDict( PoolContext* pool, DictionaryCtxt* dict )
{
XP_U16 numFaces = dict_numTileFaces( dict );
Tile i;
if ( pool->lettersLeft != NULL ) {
XP_FREE( pool->mpool, pool->lettersLeft );
}
pool->lettersLeft
= (XP_U8*)XP_MALLOC( pool->mpool,
numFaces * sizeof(pool->lettersLeft[0]) );
pool->numTilesLeft = 0;
for ( i = 0; i < numFaces; ++i ) {
XP_U16 numTiles = dict_numTiles( dict, i );
pool->lettersLeft[i] = (XP_U8)numTiles;
pool->numTilesLeft += numTiles;
}
pool->numFaces = numFaces;
#ifdef BLANKS_FIRST
if ( dict_hasBlankTile( dict ) ) {
pool->blankIndex = dict_getBlankTile(dict);
} else {
pool->blankIndex = -1;
}
#endif
} /* pool_initFromDict */

43
xwords4/common/pool.h Normal file
View file

@ -0,0 +1,43 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 2000-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 _POOL_H_
#define _POOL_H_
#include "comtypes.h"
#include "mempool.h"
#include "model.h"
void pool_requestTiles( PoolContext* pool, Tile* tiles,
/*in out*/ XP_U8* maxNum );
void pool_replaceTiles( PoolContext* pool, TrayTileSet* tiles );
void pool_removeTiles( PoolContext* pool, TrayTileSet* tiles );
XP_U16 pool_getNTilesLeft( PoolContext* pool );
XP_U16 pool_getNTilesLeftFor( PoolContext* pool, Tile tile );
PoolContext* pool_make( MPFORMAL_NOCOMMA );
void pool_destroy( PoolContext* pool );
void pool_initFromDict( PoolContext* pool, DictionaryCtxt* dict );
void pool_writeToStream( PoolContext* pool, XWStreamCtxt* stream );
PoolContext* pool_makeFromStream( MPFORMAL XWStreamCtxt* stream );
#endif

31
xwords4/common/rules.mk Normal file
View file

@ -0,0 +1,31 @@
# -*- mode: makefile -*-
# Copyright 2002 by Eric House
#
# 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.
all: $(TARGET)
# Rule for (xplatform) objfiles in this directory
$(COMMONOBJDIR)/%.o: ../common/%.c
ls -d $(COMMONOBJDIR) || mkdir $(COMMONOBJDIR)
$(CC) -c $(CFLAGS) $(INCLUDES) $(DEFINES) -DPLATFORM=$(PLATFORM) \
$< -o $@
# Rule for single-platform objfiles in directory of including Makefile
$(PLATFORM)/%.o: %.c
ls -d $(PLATFORM) || mkdir $(PLATFORM)
$(CC) -c -dD $(CFLAGS) $(INCLUDES) $(DEFINES) -DPLATFORM=$(PLATFORM) $< -o $@

2270
xwords4/common/server.c Normal file

File diff suppressed because it is too large Load diff

124
xwords4/common/server.h Normal file
View file

@ -0,0 +1,124 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 1997 - 2000 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 _SERVER_H_
#define _SERVER_H_
#include "comtypes.h" /* that's *common* types */
#include "commmgr.h"
#include "model.h"
#ifdef CPLUS
extern "C" {
#endif
enum {
PHONIES_IGNORE,
PHONIES_WARN,
PHONIES_DISALLOW
};
typedef XP_U8 XWPhoniesChoice;
enum {
SERVER_STANDALONE,
SERVER_ISSERVER,
SERVER_ISCLIENT
};
typedef XP_U8 Connectedness;
/* typedef struct ServerCtxt ServerCtxt; */
/* typedef struct ServerVtable { */
/* void (*m_registerPlayer)( ServerCtxt* server, XP_U16 playerNum, */
/* XP_PlayerSocket socket ); */
/* void (*m_getTileValueInfo)( ServerCtxt* server, void* valueBuf ); */
/* } ServerVtable; */
ServerCtxt* server_make( MPFORMAL ModelCtxt* model, CommsCtxt* comms,
XW_UtilCtxt* util );
ServerCtxt* server_makeFromStream( MPFORMAL XWStreamCtxt* stream,
ModelCtxt* model, CommsCtxt* comms,
XW_UtilCtxt* util, XP_U16 nPlayers );
void server_writeToStream( ServerCtxt* server, XWStreamCtxt* stream );
void server_reset( ServerCtxt* server );
void server_destroy( ServerCtxt* server );
void server_prefsChanged( ServerCtxt* server, CommonPrefs* cp );
typedef void (*TurnChangeListener)( void* data );
void server_setTurnChangeListener( ServerCtxt* server, TurnChangeListener tl,
void* data );
typedef void (*GameOverListener)( void* data );
void server_setGameOverListener( ServerCtxt* server, GameOverListener gol,
void* data );
/* support random assignment by telling creator of new player what it's
* number will be */
/* XP_U16 server_assignNum( ServerCtxt* server ); */
EngineCtxt* server_getEngineFor( ServerCtxt* server, XP_U16 playerNum );
void server_resetEngine( ServerCtxt* server, XP_U16 playerNum );
XP_U16 server_secondsUsedBy( ServerCtxt* server, XP_U16 playerNum );
/* It might make more sense to have the board supply the undo method clients
call... */
XP_Bool server_handleUndo( ServerCtxt* server );
/* signed because negative number means nobody's turn yet */
XP_S16 server_getCurrentTurn( ServerCtxt* server );
XP_Bool server_getGameIsOver( ServerCtxt* server );
/* Signed in case no dictionary available */
XP_S16 server_countTilesInPool( ServerCtxt* server );
XP_Bool server_do( ServerCtxt* server );
XP_Bool server_commitMove( ServerCtxt* server );
XP_Bool server_commitTrade( ServerCtxt* server, TileBit bits );
/* call this when user wants to end the game */
void server_endGame( ServerCtxt* server );
/* called when running as either client or server */
XP_Bool server_receiveMessage( ServerCtxt* server, XWStreamCtxt* incomming );
/* client-side messages. Client (platform code)owns the stream used to talk
* to the server, and passes it in. */
#ifndef XWFEATURE_STANDALONE_ONLY
void server_initClientConnection( ServerCtxt* server, XWStreamCtxt* stream );
#endif
void server_formatPoolCounts( ServerCtxt* server, XWStreamCtxt* stream,
XP_U16 nCols );
void server_writeFinalScores( ServerCtxt* server, XWStreamCtxt* stream );
#ifdef CPLUS
}
#endif
#endif

61
xwords4/common/states.h Normal file
View file

@ -0,0 +1,61 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 2000 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 _STATES_H_
#define _STATES_H_
typedef enum {
XWSTATE_NONE,
XWSTATE_BEGIN,
XWSTATE_POOL_INITED,
XWSTATE_NEED_SHOWSCORE, /* client-only */
XWSTATE_WAITING_ALL_REG, /* includes waiting for dict from server */
XWSTATE_RECEIVED_ALL_REG, /* includes waiting for dict from server */
XWSTATE_NEEDSEND_BADWORD_INFO,
XWSTATE_MOVE_CONFIRM_WAIT, /* client's waiting to hear back */
XWSTATE_MOVE_CONFIRM_MUSTSEND, /* server should tell client asap */
XWSTATE_NEEDSEND_ENDGAME,
XWSTATE_INTURN,
XWSTATE_GAMEOVER
} XW_State;
/* Game starts out in BEGIN. If the server expects other players, it goes
* into XWSTATE_WAITING_ALL_REG. Likewise goes any client waiting to hear
* from the server after sending off its info. A stand-alone game (server)
* goes immediately from BEGIN to WAITING_INFO.
*
* When a device gets tiles for all players (which happens in a single
* message where there's communication involved) it moves to INTURN (either
* ONDEVICE or OFFDEVICE). ONDEVICE changes to WAITING_INFO when the device
* sends its move to the server; OFFDEVICE changes to ONDEVICE if a
* notification that a move's been made is received and it's now a local
* player's turn; otherwise that notification may arrive with no change in
* XW_State (but a change in whose turn it is.)
After a move is made (current player's device
* sends move
*/
#endif

165
xwords4/common/strutils.c Normal file
View file

@ -0,0 +1,165 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 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 "strutils.h"
#include "xwstream.h"
#ifdef CPLUS
extern "C" {
#endif
XP_U16
bitsForMax( XP_U32 n )
{
XP_U16 result = 0;
XP_ASSERT( n > 0 );
while ( n != 0 ) {
n >>= 1;
++result;
}
return result;
} /* bitsForMax */
static void
tilesToStream( XWStreamCtxt* stream, Tile* tiles, XP_U16 nTiles )
{
while ( nTiles-- ) {
stream_putBits( stream, TILE_NBITS, *tiles++ );
}
} /* tilesToStream */
void
traySetToStream( XWStreamCtxt* stream, TrayTileSet* ts )
{
XP_U16 nTiles = ts->nTiles;
stream_putBits( stream, NTILES_NBITS, nTiles );
tilesToStream( stream, ts->tiles, nTiles );
} /* traySetFromStream */
static void
tilesFromStream( XWStreamCtxt* stream, Tile* tiles, XP_U16 nTiles )
{
while ( nTiles-- ) {
*tiles++ = (Tile)stream_getBits( stream, TILE_NBITS );
}
} /* tilesFromStream */
void
traySetFromStream( XWStreamCtxt* stream, TrayTileSet* ts )
{
XP_U16 nTiles = (XP_U16)stream_getBits( stream, NTILES_NBITS );
tilesFromStream( stream, ts->tiles, nTiles );
ts->nTiles = (XP_U8)nTiles;
} /* traySetFromStream */
#if 0
static void
signedToStream( XWStreamCtxt* stream, XP_U16 nBits, XP_S32 num )
{
XP_Bool negative = num < 0;
stream_putBits( stream, 1, negative );
if ( negative ) {
num *= -1;
}
stream_putBits( stream, nBits, num );
} /* signedToStream */
XP_S32
signedFromStream( XWStreamCtxt* stream, XP_U16 nBits )
{
XP_S32 result;
XP_Bool negative = stream_getBits( stream, 1 );
result = stream_getBits( stream, nBits );
if ( negative ) {
result *= -1;
}
return result;
} /* signedFromStream */
#endif
XP_UCHAR*
stringFromStream( MPFORMAL XWStreamCtxt* stream )
{
XP_UCHAR* str = (XP_UCHAR*)NULL;
XP_U16 len = stream_getU8( stream );
if ( len > 0 ) {
str = (XP_UCHAR*)XP_MALLOC( mpool, len + 1 );
stream_getBytes( stream, str, len );
str[len] = '\0';
}
return str;
} /* makeStringFromStream */
void
stringToStream( XWStreamCtxt* stream, XP_UCHAR* str )
{
XP_U16 len = str==NULL? 0: XP_STRLEN( (const char*)str );
XP_ASSERT( len < 0xFF );
stream_putU8( stream, (XP_U8)len );
stream_putBytes( stream, str, len );
} /* putStringToStream */
/*****************************************************************************
*
****************************************************************************/
XP_UCHAR*
copyString( MPFORMAL XP_UCHAR* instr )
{
XP_UCHAR* result = (XP_UCHAR*)NULL;
if ( !!instr ) {
XP_U16 len = XP_STRLEN( (const char*)instr );
result = (XP_UCHAR*)XP_MALLOC( (MemPoolCtx*)mpool, len + 1 );
XP_ASSERT( !!result );
XP_MEMCPY( result, instr, len + 1);
}
return result;
} /* copyString */
void
replaceStringIfDifferent( MPFORMAL XP_UCHAR** curLoc, XP_UCHAR* newStr )
{
XP_UCHAR* curStr = *curLoc;
if ( !!newStr && !!curStr &&
(0 == XP_STRCMP( (const char*)curStr, (const char*)newStr ) ) ) {
/* do nothing; we're golden */
} else {
if ( !!curStr ) {
XP_FREE( mpool, curStr );
}
curStr = copyString( MPPARM(mpool) newStr );
}
*curLoc = curStr;
} /* replaceStringIfDifferent */
/*
* A wrapper for printing etc. potentially null strings.
*/
XP_UCHAR*
emptyStringIfNull( XP_UCHAR* str )
{
return !!str? str : (XP_UCHAR*)"";
} /* emptyStringIfNull */
#ifdef CPLUS
}
#endif

52
xwords4/common/strutils.h Normal file
View file

@ -0,0 +1,52 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 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 _STRUTILS_H_
#define _STRUTILS_H_
#include "comtypes.h"
#include "model.h"
#ifdef CPLUS
extern "C" {
#endif
#define TILE_NBITS 6 /* 32 tiles plus the blank */
XP_U16 bitsForMax( XP_U32 n );
void traySetToStream( XWStreamCtxt* stream, TrayTileSet* ts );
void traySetFromStream( XWStreamCtxt* stream, TrayTileSet* ts );
XP_S32 signedFromStream( XWStreamCtxt* stream, XP_U16 nBits );
void signedToStream( XWStreamCtxt* stream, XP_U16 nBits, XP_S32 num );
XP_UCHAR* stringFromStream( MPFORMAL XWStreamCtxt* stream );
void stringToStream( XWStreamCtxt* stream, XP_UCHAR* str );
XP_UCHAR* copyString( MPFORMAL XP_UCHAR* instr );
void replaceStringIfDifferent( MPFORMAL XP_UCHAR** curLoc, XP_UCHAR* newStr );
XP_UCHAR* emptyStringIfNull( XP_UCHAR* str );
#ifdef CPLUS
}
#endif
#endif /* _STRUTILS_H_ */

659
xwords4/common/tray.c Normal file
View file

@ -0,0 +1,659 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 1997 - 2000 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 "boardp.h"
#include "engine.h"
#ifdef CPLUS
extern "C" {
#endif
/****************************** prototypes ******************************/
static XP_Bool startDividerDrag( BoardCtxt* board );
static XP_Bool startTileDrag( BoardCtxt* board, XP_U8 startIndex );
static void figureDividerRect( BoardCtxt* board, XP_Rect* rect );
static void drawPendingScore( BoardCtxt* board );
static void invalTrayTilesBetween( BoardCtxt* board, XP_U16 tileIndex1,
XP_U16 tileIndex2 );
static XP_Bool endTileDragIndex( BoardCtxt* board, TileBit last );
static XP_S16
trayLocToIndex( BoardCtxt* board, XP_U16 loc )
{
if ( loc >= model_getNumTilesInTray( board->model,
board->selPlayer ) ) {
loc *= -1;
/* (0 * -1) is still 0, so reduce by 1. Will need to adjust
below. NOTE: this is something of a hack.*/
--loc;
}
return loc;
} /* trayLocToIndex */
static XP_S16
pointToTileIndex( BoardCtxt* board, XP_U16 x, XP_U16 y, XP_Bool* onDividerP )
{
XP_S16 result = -1; /* not on a tile */
XP_Rect divider;
XP_Bool onDivider;
figureDividerRect( board, &divider );
onDivider = rectContainsPt( &divider, x, y );
if ( !onDivider ) {
if ( x > divider.left ) {
XP_ASSERT( divider.width == board->dividerWidth );
x -= divider.width;
}
XP_ASSERT( x >= board->trayBounds.left );
x -= board->trayBounds.left;
result = x / board->trayScaleH;
result = trayLocToIndex( board, result );
}
if ( onDividerP != NULL ) {
*onDividerP = onDivider;
}
return result;
} /* pointToTileIndex */
static void
figureTrayTileRect( BoardCtxt* board, XP_U16 index, XP_Rect* rect )
{
rect->left = board->trayBounds.left + (index * board->trayScaleH);
rect->top = board->trayBounds.top/* + 1 */;
rect->width = board->trayScaleH;
rect->height = board->trayScaleV;
if ( board->dividerLoc[board->selPlayer] <= index ) {
rect->left += board->dividerWidth;
}
} /* figureTileRect */
void
drawTray( BoardCtxt* board, XP_Bool focussed )
{
DictionaryCtxt* dictionary;
XP_Rect tileRect;
short i;
if ( (board->trayInvalBits != 0) || board->dividerInvalid ) {
XP_S16 turn = board->selPlayer;
dictionary = model_getDictionary( board->model );
draw_trayBegin( board->draw, &board->trayBounds, turn,
focussed );
if ( board->eraseTray ) {
draw_clearRect( board->draw, &board->trayBounds );
board->eraseTray = XP_FALSE;
}
if ( (board->trayVisState != TRAY_HIDDEN) && dictionary != NULL ) {
XP_Bool showFaces = board->trayVisState == TRAY_REVEALED;
if ( turn >= 0 ) {
XP_U16 numInTray = showFaces?
model_getNumTilesInTray( board->model, turn ):
model_getNumTilesTotal( board->model, turn );
/* draw in reverse order so drawing happens after erasing */
for ( i = MAX_TRAY_TILES - 1; i >= 0; --i ) {
if ( (board->trayInvalBits & (1 << i)) == 0 ) {
continue;
}
figureTrayTileRect( board, i, &tileRect );
if ( i >= numInTray/* && showFace */ ) {
draw_clearRect( board->draw, &tileRect );
} else if ( showFaces ) {
XP_UCHAR buf[4];
XP_Bitmap bitmap = NULL;
XP_UCHAR* textP = (XP_UCHAR*)NULL;
XP_U8 flags = board->traySelBits[turn];
XP_Bool highlighted = (flags & (1<<i)) != 0;
Tile tile = model_getPlayerTile( board->model,
turn, i );
XP_S16 value;
if ( dict_faceIsBitmap( dictionary, tile ) ) {
bitmap = dict_getFaceBitmap( dictionary, tile,
XP_TRUE );
} else {
textP = buf;
dict_tilesToString( dictionary, &tile, 1, textP );
}
value = dict_getTileValue( dictionary, tile );
draw_drawTile( board->draw, &tileRect, textP, bitmap,
value, highlighted );
} else {
draw_drawTileBack( board->draw, &tileRect );
}
}
}
if ( (board->dividerWidth > 0) && board->dividerInvalid ) {
XP_Rect divider;
figureDividerRect( board, &divider );
draw_drawTrayDivider( board->draw, &divider,
board->divDragState.dragInProgress );
board->dividerInvalid = XP_FALSE;
}
#ifdef KEYBOARD_NAV
if ( showFaces ) {
TileBit cursorLoc = board->trayCursorLoc[turn];
if ( !!cursorLoc ) {
XP_U16 index = indexForBits( cursorLoc );
figureTrayTileRect( board, index, &tileRect );
draw_drawTrayCursor( board->draw, &tileRect );
}
}
#endif
}
draw_trayFinished(board->draw);
board->trayInvalBits = 0;
}
drawPendingScore( board );
} /* drawTray */
static void
drawPendingScore( BoardCtxt* board )
{
/* Draw the pending score down in the last tray's rect */
XP_U16 selPlayer = board->selPlayer;
if ( board->trayVisState == TRAY_REVEALED ) {
XP_U16 tilesInTray = model_getNumTilesInTray( board->model, selPlayer );
if ( tilesInTray < MAX_TRAY_TILES ) {
XP_S16 turnScore = 0;
XP_Rect lastTileR;
(void)getCurrentMoveScoreIfLegal( board->model, selPlayer,
(XWStreamCtxt*)NULL, &turnScore );
figureTrayTileRect( board, MAX_TRAY_TILES-1, &lastTileR );
draw_score_pendingScore( board->draw, &lastTileR, turnScore,
selPlayer );
}
}
} /* drawPendingScore */
#ifdef DEBUG
static XP_U16
countSelectedTiles( XP_U8 ti )
{
XP_U16 result = 0;
while ( ti != 0 ) {
++result;
ti &= ti-1;
}
return result;
} /* countSelectedTiles */
#endif
static void
figureDividerRect( BoardCtxt* board, XP_Rect* rect )
{
figureTrayTileRect( board, board->dividerLoc[board->selPlayer], rect );
rect->left -= board->dividerWidth;
rect->width = board->dividerWidth;
} /* figureDividerRect */
static XP_Bool
handleTrayDuringTrade( BoardCtxt* board, XP_S16 index )
{
TileBit bits;
XP_ASSERT( index >= 0 );
bits = 1 << index;
board->traySelBits[board->selPlayer] ^= bits;
board_invalTrayTiles( board, bits );
return XP_TRUE;
} /* handleTrayDuringTrade */
static XP_Bool
handleActionInTray( BoardCtxt* board, XP_S16 index, XP_Bool onDivider,
XP_Bool waitPenUp )
{
XP_Bool result = XP_FALSE;
XP_U16 selPlayer = board->selPlayer;
if ( onDivider ) {
result = startDividerDrag( board );
} else if ( board->tradeInProgress[selPlayer]
/* && MY_TURN(board) */ ) {
if ( index >= 0 ) {
result = handleTrayDuringTrade( board, index );
}
} else if ( index >= 0 ) {
TileBit newIndex = 1 << index;
BoardArrow* arrow = &board->boardArrow[selPlayer];
if ( arrow->visible ) {
result = moveTileToArrowLoc( board, (XP_U8)index );
} else {
XP_U8 selFlags = board->traySelBits[selPlayer];
/* Tap on selected tile unselects. If we don't do this,
then there's no way to unselect and so no way to turn
off the placement arrow */
if ( newIndex == selFlags ) {
board_invalTrayTiles( board, selFlags );
selFlags = NO_TILES;
board->traySelBits[selPlayer] = selFlags;
result = XP_TRUE;
} else {
result = startTileDrag( board, newIndex );
if ( !waitPenUp ) {
/* key interface means pen up and down happen in the same
event. No dragging. */
result = endTileDragIndex( board, newIndex ) || result;
}
}
}
} else { /* tap on emptied part of tray */
if ( index == -(MAX_TRAY_TILES) ) { /* pending score tile */
result = board_commitTurn( board );
} else { /* other empty area */
/* it better be true */
(void)board_replaceTiles( board );
result = XP_TRUE;
}
}
return result;
} /* handleActionInTray */
XP_Bool
handlePenDownInTray( BoardCtxt* board, XP_U16 x, XP_U16 y )
{
XP_Bool onDivider = XP_FALSE;
XP_S16 index = pointToTileIndex( board, x, y, &onDivider );
return handleActionInTray( board, index, onDivider, XP_TRUE );
} /* handleActionInTray */
static XP_Bool
startTileDrag( BoardCtxt* board, TileBit startBit/* , XP_U16 x, XP_U16 y */ )
{
XP_Bool result = XP_FALSE;
XP_U16 turn = board->selPlayer;
XP_U8 startSel = board->traySelBits[turn];
TileDragState* state = &board->tileDragState;
XP_ASSERT( countSelectedTiles( startBit ) == 1 );
XP_ASSERT( !state->dragInProgress );
state->wasHilited = startSel == startBit;
state->selectionAtStart = startSel;
state->movePending = XP_TRUE;
state->dragInProgress = XP_TRUE;
XP_STATUSF( "startTileDrag: set dragInProgress\n" );
state->prevIndex = board->traySelBits[turn] = startBit;
if ( !state->wasHilited ) {
board_invalTrayTiles( board, (TileBit)(startBit | startSel) );
result = XP_TRUE;
}
return result;
} /* startTileDrag */
static void
moveTileInTray( BoardCtxt* board, TileBit prevTile, TileBit newTile )
{
XP_S16 selPlayer = board->selPlayer;
ModelCtxt* model = board->model;
XP_U16 moveTo = indexForBits( prevTile );
XP_U16 moveFrom = indexForBits( newTile );
Tile tile;
XP_U16 dividerLoc;
XP_STATUSF( "moveTileInTray: %d -> %d\n", prevTile, newTile );
tile = model_removePlayerTile( model, selPlayer, moveFrom );
model_addPlayerTile( model, selPlayer, moveTo, tile );
dividerLoc = board->dividerLoc[selPlayer];
if ( moveTo < dividerLoc || moveFrom < dividerLoc ) {
server_resetEngine( board->server, selPlayer );
}
} /* moveTileInTray */
TileBit
continueTileDrag( BoardCtxt* board, XP_U16 x, XP_U16 y )
{
TileDragState* state = &board->tileDragState;
TileBit overTile = 0;
XP_S16 index = pointToTileIndex( board, x, y, (XP_Bool*)NULL );
if ( index >= 0 ) {
overTile = 1 << index;
if ( overTile != state->prevIndex ) {
moveTileInTray( board, overTile, state->prevIndex );
state->movePending = XP_FALSE;
state->wasHilited = XP_FALSE; // so we won't deselect
state->prevIndex = board->traySelBits[board->selPlayer] = overTile;
}
}
return overTile;
} /* continueTileDrag */
XP_U16
indexForBits( XP_U8 bits )
{
XP_U16 result = 0;
XP_U16 mask = 1;
XP_ASSERT( bits != 0 ); /* otherwise loops forever */
while ( (mask & bits) == 0 ) {
++result;
mask <<= 1;
}
return result;
} /* indexForBits */
static XP_Bool
endTileDragIndex( BoardCtxt* board, TileBit last )
{
XP_Bool result = XP_FALSE;
XP_U16 selPlayer = board->selPlayer;
TileDragState* state = &board->tileDragState;
if ( state->movePending ) { /* no drag took place */
if ( state->wasHilited ) { /* if the user just clicked; deselect */
board_invalTrayTiles( board, state->selectionAtStart );
board->traySelBits[selPlayer] = NO_TILES;
result = XP_TRUE;
} else if ( (last > 0)
&& !board->boardArrow[selPlayer].visible
&& (state->selectionAtStart != NO_TILES ) ) {
if ( model_getCurrentMoveCount( board->model, selPlayer) == 0 ) {
moveTileInTray( board, last, state->selectionAtStart );
board->traySelBits[selPlayer] = NO_TILES;
} else {
board_invalTrayTiles(
board,
(TileBit)(state->selectionAtStart|last) );
board->traySelBits[selPlayer] = last;
}
result = XP_TRUE;
}
} else {
board_invalTrayTiles( board, state->prevIndex );
board->traySelBits[selPlayer] = NO_TILES;
result = XP_TRUE;
}
state->dragInProgress = XP_FALSE;
XP_STATUSF( "endTileDrag: cleared dragInProgress\n" );
return result;
} /* endTileDragIndex */
XP_Bool
endTileDrag( BoardCtxt* board, XP_U16 x, XP_U16 y )
{
TileBit newTile = continueTileDrag( board, x, y );
return endTileDragIndex( board, newTile );
} /* endTileDrag */
static XP_Bool
startDividerDrag( BoardCtxt* board )
{
board->divDragState.dragInProgress = XP_TRUE;
board->dividerInvalid = XP_TRUE;
return XP_TRUE;
} /* startDividerDrag */
static void
dividerMoved( BoardCtxt* board, XP_U8 newLoc )
{
XP_U8 oldLoc = board->dividerLoc[board->selPlayer];
board->dividerLoc[board->selPlayer] = newLoc;
/* This divider's index corresponds to the tile it's to the left of, and
there's no need to invalidate any tiles to the left of the uppermore
divider position. */
if ( oldLoc > newLoc ) {
--oldLoc;
} else {
--newLoc;
}
invalTrayTilesBetween( board, newLoc, oldLoc );
board->dividerInvalid = XP_TRUE;
/* changed number of available tiles */
board_resetEngine( board );
} /* dividerMoved */
XP_Bool
continueDividerDrag( BoardCtxt* board, XP_U16 x, XP_U16 y )
{
XP_U8 newOffset;
XP_U16 trayScale = board->trayScaleH;
XP_Bool result = XP_FALSE;
XP_ASSERT( board->divDragState.dragInProgress );
/* Pen might have been dragged out of the tray */
if ( rectContainsPt( &board->trayBounds, x, y ) ) {
x -= board->trayBounds.left;
newOffset = x / trayScale;
if ( (x % trayScale) > (trayScale/2) ) {
++newOffset;
}
result = newOffset != board->dividerLoc[board->selPlayer];
if ( result ) {
dividerMoved( board, newOffset );
}
}
return result;
} /* continueDividerDrag */
XP_Bool
endDividerDrag( BoardCtxt* board, XP_U16 x, XP_U16 y )
{
XP_Bool result = XP_TRUE; /* b/c hilited state looks different */
(void)continueDividerDrag( board, x, y );
board->dividerInvalid = XP_TRUE;
board->divDragState.dragInProgress = XP_FALSE;
return result;
} /* endDividerDrag */
void
board_invalTrayTiles( BoardCtxt* board, TileBit what )
{
board->trayInvalBits |= what;
} /* invalTrayTiles */
static void
invalTrayTilesBetween( BoardCtxt* board, XP_U16 tileIndex1,
XP_U16 tileIndex2 )
{
TileBit bits = 0;
if ( tileIndex1 > tileIndex2 ) {
XP_U16 tmp = tileIndex1;
tileIndex1 = tileIndex2;
tileIndex2 = tmp;
}
while ( tileIndex1 <= tileIndex2 ) {
bits |= (1 << tileIndex1);
++tileIndex1;
}
board_invalTrayTiles( board, bits );
} /* invalTrayTilesBetween */
static void
juggleTiles( Tile* tiles, XP_U16 nTiles )
{
Tile newTiles[MAX_TRAY_TILES];
XP_ASSERT( nTiles <= MAX_TRAY_TILES );
XP_MEMCPY( newTiles, tiles, nTiles * sizeof(newTiles[0]) );
/* Pull out a tile at random, and swap the top tile down into its place so
the array doesn't get sparse. */
while ( nTiles > 0 ) {
XP_U16 rIndex = ((XP_U16)XP_RANDOM()) % nTiles;
*tiles++ = newTiles[rIndex];
newTiles[rIndex] = newTiles[--nTiles];
}
} /* juggleTiles */
XP_Bool
board_juggleTray( BoardCtxt* board )
{
XP_Bool result = XP_FALSE;
XP_S16 turn = board->selPlayer;
if ( checkRevealTray( board ) ) {
XP_S16 nTiles;
XP_U16 dividerLoc = board->dividerLoc[board->selPlayer];
ModelCtxt* model = board->model;
nTiles = model_getNumTilesInTray( model, turn ) - dividerLoc;
if ( nTiles > 1 ) {
XP_S16 i;
Tile tmpT[MAX_TRAY_TILES];
Tile newT[MAX_TRAY_TILES];
/* create unique indices to be juggled; then juggle 'em until
changed. */
for ( i = 0; i < nTiles; ++i ) {
tmpT[i] = newT[i] = (Tile)i;
}
do {
juggleTiles( newT, nTiles );
} while ( XP_MEMCMP( newT, tmpT, nTiles * sizeof(newT[0]) ) == 0 );
/* save copies of the tiles in juggled order */
for ( i = 0; i < nTiles; ++i ) {
tmpT[i] = model_getPlayerTile( model, turn,
dividerLoc + newT[i] );
}
/* delete tiles off right end; put juggled ones back on the other */
for ( i = nTiles - 1; i >= 0; --i ) {
(void)model_removePlayerTile( model, turn, -1 );
model_addPlayerTile( model, turn, dividerLoc, tmpT[i] );
}
board->traySelBits[turn] = 0;
result = XP_TRUE;
}
}
return result;
} /* board_juggleTray */
#ifdef KEYBOARD_NAV
XP_Bool
tray_moveCursor( BoardCtxt* board, XP_Key cursorKey )
{
XP_U16 selPlayer = board->selPlayer;
XP_U16 numTrayTiles = model_getNumTilesInTray( board->model,
selPlayer );
XP_U16 pos;
TileBit newSel;
TileBit oldSel = board->trayCursorLoc[selPlayer];
numTrayTiles = MAX_TRAY_TILES;
if ( oldSel == 0 ) {
if ( cursorKey == XP_CURSOR_KEY_LEFT ) {
newSel = 1 << (numTrayTiles - 1);
} else {
XP_ASSERT( cursorKey == XP_CURSOR_KEY_RIGHT );
newSel = 1;
}
} else {
pos = indexForBits( oldSel );
pos += numTrayTiles; /* add what we'll mod by below: makes circular */
if ( cursorKey == XP_CURSOR_KEY_LEFT ) {
--pos;
} else if ( cursorKey == XP_CURSOR_KEY_RIGHT ) {
++pos;
}
newSel = 1 << (pos % numTrayTiles);
}
board->trayCursorLoc[selPlayer] = newSel;
board_invalTrayTiles( board, newSel | oldSel );
return XP_TRUE;
} /* tray_moveCursor */
XP_Bool
tray_keyAction( BoardCtxt* board )
{
TileBit cursor = board->trayCursorLoc[board->selPlayer];
XP_Bool result;
if ( !!cursor ) {
XP_S16 index = trayLocToIndex( board, indexForBits( cursor ) );
result = handleActionInTray( board, index, XP_FALSE, XP_FALSE );
} else {
result = XP_FALSE;
}
return result;
} /* tray_selectCurTile */
#endif
#if defined FOR_GREMLINS || defined KEYBOARD_NAV
XP_Bool
board_moveDivider( BoardCtxt* board, XP_Bool right )
{
XP_Bool result = board->trayVisState == TRAY_REVEALED;
if ( result ) {
XP_U8 loc = board->dividerLoc[board->selPlayer];
loc += MAX_TRAY_TILES + 1;
loc += right? 1:-1;
loc %= MAX_TRAY_TILES + 1;
dividerMoved( board, loc );
}
return result;
} /* dividerMovedOne */
#endif
#ifdef CPLUS
}
#endif

199
xwords4/common/util.h Normal file
View file

@ -0,0 +1,199 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 1997 - 2000 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 _UTIL_H_
#define _UTIL_H_
#include "comtypes.h"
#include "dawg.h"
#include "model.h"
#include "board.h"
#include "mempool.h"
#include "vtabmgr.h"
#define LETTER_NONE '\0'
typedef enum {
ERR_TILES_NOT_IN_LINE, /* scoring a move where tiles aren't in line */
ERR_NO_EMPTIES_IN_TURN,
ERR_TWO_TILES_FIRST_MOVE,
ERR_TILES_MUST_CONTACT,
/* ERR_NO_HINT_MID_TURN, */
ERR_TOO_FEW_TILES_LEFT_TO_TRADE,
ERR_NOT_YOUR_TURN,
ERR_NO_PEEK_ROBOT_TILES,
#ifndef XWFEATURE_STANDALONE_ONLY
ERR_SERVER_DICT_WINS,
ERR_NO_PEEK_REMOTE_TILES,
ERR_REG_UNEXPECTED_USER, /* server asked to register too many remote
users */
#endif
ERR_CANT_TRADE_MID_MOVE,
ERR_CANT_ENGINE_MID_MOVE
/* ERR_NOT_YOUR_TURN_TO_TRADE, */
/* ERR_NOT_YOUR_TURN_TO_MOVE, */
} UtilErrID;
typedef enum {
QUERY_COMMIT_TURN, /* 0 means cancel; 1 means commit */
QUERY_COMMIT_TRADE,
QUERY_ROBOT_MOVE,
QUERY_ROBOT_TRADE
} UtilQueryID;
typedef struct BadWordInfo {
XP_U16 nWords;
XP_UCHAR* words[MAX_TRAY_TILES+1]; /* can form in both directions */
} BadWordInfo;
/* Platform-specific utility functions that need to be
*/
typedef struct UtilVtable {
VTableMgr* (*m_util_getVTManager)(XW_UtilCtxt* uc);
XWStreamCtxt* (*m_util_makeStreamFromAddr )(XW_UtilCtxt* uc,
XP_U16 channelNo );
XWBonusType (*m_util_getSquareBonus)( XW_UtilCtxt* uc, ModelCtxt* model,
XP_U16 col, XP_U16 row );
void (*m_util_userError)( XW_UtilCtxt* uc, UtilErrID id );
XP_U16 (*m_util_userQuery)( XW_UtilCtxt* uc, UtilQueryID id,
XWStreamCtxt* stream );
void (*m_util_askBlankFace)( XW_UtilCtxt* uc, DictionaryCtxt* dict,
XP_UCHAR* buf );
XP_Bool (*m_util_askPassword)( XW_UtilCtxt* uc, const XP_UCHAR* name,
XP_UCHAR* buf, XP_U16* len );
void (*m_util_trayHiddenChange)(XW_UtilCtxt* uc,
XW_TrayVisState newState );
void (*m_util_yOffsetChange)(XW_UtilCtxt* uc, XP_U16 oldOffset,
XP_U16 newOffset );
void (*m_util_notifyGameOver)( XW_UtilCtxt* uc );
XP_Bool (*m_util_hiliteCell)( XW_UtilCtxt* uc, XP_U16 col, XP_U16 row );
XP_Bool (*m_util_engineProgressCallback)( XW_UtilCtxt* uc );
void (*m_util_setTimer)( XW_UtilCtxt* uc, XWTimerReason why );
void (*m_util_requestTime)( XW_UtilCtxt* uc );
XP_U32 (*m_util_getCurSeconds)( XW_UtilCtxt* uc );
DictionaryCtxt* (*m_util_makeEmptyDict)( XW_UtilCtxt* uc );
XP_UCHAR* (*m_util_getUserString)( XW_UtilCtxt* uc, XP_U16 stringCode );
XP_Bool (*m_util_warnIllegalWord)( XW_UtilCtxt* uc, BadWordInfo* bwi,
XP_U16 turn, XP_Bool turnLost );
#ifdef BEYOND_IR
void (*m_util_listenPortChange)( XW_UtilCtxt* uc, XP_U16 listenPort );
#endif
#ifdef SHOW_PROGRESS
void (*m_util_engineStarting)( XW_UtilCtxt* uc );
void (*m_util_engineStopping)( XW_UtilCtxt* uc );
#endif
} UtilVtable;
struct XW_UtilCtxt {
UtilVtable* vtable;
struct CurGameInfo* gameInfo;
void* closure;
MPSLOT
};
#define util_getVTManager(uc) \
(uc)->vtable->m_util_getVTManager((uc))
#define util_makeStreamFromAddr(uc,a) \
(uc)->vtable->m_util_makeStreamFromAddr((uc),(a))
#define util_getSquareBonus(uc,m,c,r) \
(uc)->vtable->m_util_getSquareBonus((uc),(m),(c),(r))
#define util_userError(uc,err) \
(uc)->vtable->m_util_userError((uc),(err))
#define util_userQuery(uc,qcode,str) \
(uc)->vtable->m_util_userQuery((uc),(qcode),(str))
#define util_askBlankFace( uc, d, b ) \
(uc)->vtable->m_util_askBlankFace((uc), (d), (b) )
#define util_askPassword( uc, n, b, lp ) \
(uc)->vtable->m_util_askPassword( (uc), (n), (b), (lp) )
#define util_trayHiddenChange( uc, b ) \
(uc)->vtable->m_util_trayHiddenChange((uc), (b))
#define util_yOffsetChange( uc, o, n ) \
(uc)->vtable->m_util_yOffsetChange((uc), (o), (n) )
#define util_notifyGameOver( uc ) \
(uc)->vtable->m_util_notifyGameOver((uc))
#define util_hiliteCell( uc, c, r ) \
(uc)->vtable->m_util_hiliteCell((uc), (c), (r))
#define util_engineProgressCallback( uc ) \
(uc)->vtable->m_util_engineProgressCallback((uc))
#define util_setTimer( uc, why ) \
(uc)->vtable->m_util_setTimer((uc),(why))
#define util_requestTime( uc ) \
(uc)->vtable->m_util_requestTime((uc))
#define util_getCurSeconds(uc) \
(uc)->vtable->m_util_getCurSeconds((uc))
#define util_makeEmptyDict( uc ) \
(uc)->vtable->m_util_makeEmptyDict((uc))
#define util_getUserString( uc, c ) \
(uc)->vtable->m_util_getUserString((uc),(c))
#define util_warnIllegalWord( uc, w, p, b ) \
(uc)->vtable->m_util_warnIllegalWord((uc),(w),(p),(b))
#ifdef BEYOND_IR
#define util_listenPortChange( uc, port ) \
(uc)->vtable->m_util_listenPortChange((uc), (port))
#endif
# ifdef SHOW_PROGRESS
# define util_engineStarting( uc ) \
(uc)->vtable->m_util_engineStarting((uc))
# define util_engineStopping( uc ) \
(uc)->vtable->m_util_engineStopping((uc))
# else
# define util_engineStarting( uc )
# define util_engineStopping( uc )
# endif
#endif

33
xwords4/common/virtuals.h Normal file
View file

@ -0,0 +1,33 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 1997 - 2000 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 _VIRTUALS_H_
#define _VIRTUALS_H_
/* List of classes requiring vtables -- for allocating and keeping track of
vtables in some central location. */
enum {
VIRTUAL_UTIL,
VIRTUAL_DRAW,
VIRTUAL_STREAM,
VIRTUAL_NUM_VIRTUALS /* must be last */
} XW_VIRTUALS;
#endif

75
xwords4/common/vtabmgr.c Normal file
View file

@ -0,0 +1,75 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 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 "vtabmgr.h"
#define VTABLE_NUM_SLOTS VTABLE_LAST_ENTRY
#ifdef CPLUS
extern "C" {
#endif
struct VTableMgr {
void* slots[VTABLE_NUM_SLOTS];
};
VTableMgr*
make_vtablemgr( MPFORMAL_NOCOMMA )
{
VTableMgr* result = (VTableMgr*)XP_MALLOC( mpool, sizeof(*result) );
XP_MEMSET( result, 0, sizeof(*result) );
return result;
} /* make_vtablemgr */
void
vtmgr_destroy( MPFORMAL VTableMgr* vtmgr )
{
XP_U16 i;
XP_ASSERT( !!vtmgr );
for ( i = 0; i < VTABLE_NUM_SLOTS; ++i ) {
void* vtable = vtmgr->slots[i];
if ( !!vtable ) {
XP_FREE( mpool, vtable );
}
}
XP_FREE( mpool, vtmgr );
} /* vtmgr_destroy */
void
vtmgr_setVTable( VTableMgr* vtmgr, VtableType typ, void* vtable )
{
XP_ASSERT( typ < VTABLE_NUM_SLOTS );
XP_ASSERT( !vtmgr->slots[typ] );
vtmgr->slots[typ] = vtable;
} /* VTMSetVtable */
void*
vtmgr_getVTable( VTableMgr* vtmgr, VtableType typ )
{
XP_ASSERT( typ < VTABLE_NUM_SLOTS );
return vtmgr->slots[typ];
} /* VTMGetVtable */
#ifdef CPLUS
}
#endif

50
xwords4/common/vtabmgr.h Normal file
View file

@ -0,0 +1,50 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 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 _VTABMGR_H_
#define _VTABMGR_H_
#include "comtypes.h"
#include "mempool.h"
#ifdef CPLUS
extern "C" {
#endif
typedef enum {
VTABLE_MEM_STREAM = 0,
VTABLE_LAST_ENTRY
} VtableType;
typedef struct VTableMgr VTableMgr;
VTableMgr* make_vtablemgr( MPFORMAL_NOCOMMA );
void vtmgr_destroy( MPFORMAL VTableMgr* vtmgr );
void vtmgr_setVTable( VTableMgr* vtmgr, VtableType typ, void* vtable );
void* vtmgr_getVTable( VTableMgr* vtmgr, VtableType typ );
#ifdef CPLUS
}
#endif
#endif

53
xwords4/common/xwproto.h Normal file
View file

@ -0,0 +1,53 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 2000 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 _XWPROTO_H_
#define _XWPROTO_H_
typedef enum {
XWPROTO_ERROR = 0, /* illegal value */
XWPROTO_CHAT, /* reserved... */
XWPROTO_DEVICE_REGISTRATION, /* client's first message to server */
XWPROTO_CLIENT_SETUP, /* server's first message to client */
XWPROTO_MOVEMADE_INFO_CLIENT, /* client reports a move it made */
XWPROTO_MOVEMADE_INFO_SERVER, /* server tells all clients about a move
made by it or another client */
XWPROTO_UNDO_INFO_CLIENT, /* client reports undo[s] on the device */
XWPROTO_UNDO_INFO_SERVER, /* server reports undos[s] happening
elsewhere*/
//XWPROTO_CLIENT_MOVE_INFO, /* client says "I made this move" */
//XWPROTO_SERVER_MOVE_INFO, /* server says "Player X made this move" */
/* XWPROTO_CLIENT_TRADE_INFO, */
/* XWPROTO_TRADEMADE_INFO, */
XWPROTO_BADWORD_INFO,
XWPROTO_MOVE_CONFIRM, /* server tells move sender that move was
legal */
//XWPROTO_MOVEMADE_INFO, /* info about tiles placed and received */
XWPROTO_CLIENT_REQ_END_GAME, /* non-server wants to end the game */
XWPROTO_END_GAME /* server says to end game */
} XW_Proto;
#define XWPROTO_NBITS 4
#endif

29
xwords4/common/xwstate.h Normal file
View file

@ -0,0 +1,29 @@
/*
* Copyright 2000 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 XWSTATE_H_
#define XWSTATE_H_
typedef enum {
XW_UNDEFINED,
XW_SERVER_WAITING_CLIENT_SIGNON,
XW_SERVER_READY_TO_PLAY,
} XWGameState;
#endif

136
xwords4/common/xwstream.h Normal file
View file

@ -0,0 +1,136 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 1997 - 2000 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 _XWSTREAM_H_
#define _XWSTREAM_H_
#include "comtypes.h"
/* #include "xptypes.h" */
#define START_OF_STREAM 0
#define END_OF_STREAM -1
typedef XP_U32 XWStreamPos; /* low 3 bits are bit offset; rest byte offset */
enum { POS_READ, POS_WRITE };
typedef XP_U8 PosWhich;
typedef struct StreamCtxVTable {
void (*m_stream_destroy)( XWStreamCtxt* dctx );
XP_U8 (*m_stream_getU8)( XWStreamCtxt* dctx );
void (*m_stream_getBytes)( XWStreamCtxt* dctx, void* where,
XP_U16 count );
XP_U16 (*m_stream_getU16)( XWStreamCtxt* dctx );
XP_U32 (*m_stream_getU32)( XWStreamCtxt* dctx );
XP_U32 (*m_stream_getBits)( XWStreamCtxt* dctx, XP_U16 nBits );
void (*m_stream_putU8)( XWStreamCtxt* dctx, XP_U8 byt );
void (*m_stream_putBytes)( XWStreamCtxt* dctx, void* whence,
XP_U16 count );
void (*m_stream_putU16)( XWStreamCtxt* dctx, XP_U16 data );
void (*m_stream_putU32)( XWStreamCtxt* dctx, XP_U32 data );
void (*m_stream_putBits)( XWStreamCtxt* dctx, XP_U16 nBits, XP_U32 bits );
void (*m_stream_copyFromStream)( XWStreamCtxt* dctx, XWStreamCtxt* src,
XP_U16 nBytes );
XWStreamPos (*m_stream_getPos)( XWStreamCtxt* dctx, PosWhich which );
XWStreamPos (*m_stream_setPos)( XWStreamCtxt* dctx, XWStreamPos newpos,
PosWhich which );
void (*m_stream_open)( XWStreamCtxt* dctx );
void (*m_stream_close)( XWStreamCtxt* dctx );
XP_U16 (*m_stream_getSize)( XWStreamCtxt* dctx );
/* void (*m_stream_makeReturnAddr)( XWStreamCtxt* dctx, XP_PlayerAddr* addr, */
/* XP_U16* addrLen ); */
XP_PlayerAddr (*m_stream_getAddress)( XWStreamCtxt* dctx );
void (*m_stream_setAddress)( XWStreamCtxt* dctx, XP_PlayerAddr channelNo );
} StreamCtxVTable;
struct XWStreamCtxt {
StreamCtxVTable* vtable;
};
#define stream_destroy(sc) \
(sc)->vtable->m_stream_destroy(sc)
#define stream_getU8(sc) \
(sc)->vtable->m_stream_getU8(sc)
#define stream_getBytes(sc, wh, c ) \
(sc)->vtable->m_stream_getBytes((sc), (wh), (c))
#define stream_getU16(sc) \
(sc)->vtable->m_stream_getU16(sc)
#define stream_getU32(sc) \
(sc)->vtable->m_stream_getU32(sc)
#define stream_getBits(sc, n) \
(sc)->vtable->m_stream_getBits((sc), (n))
#define stream_putU8(sc, b) \
(sc)->vtable->m_stream_putU8((sc), (b))
#define stream_putBytes( sc, w, c ) \
(sc)->vtable->m_stream_putBytes((sc), (w), (c))
#define stream_putU16(sc, d) \
(sc)->vtable->m_stream_putU16((sc), (d))
#define stream_putU32(sc, d) \
(sc)->vtable->m_stream_putU32((sc), (d))
#define stream_putBits(sc, n, b) \
(sc)->vtable->m_stream_putBits((sc), (n), (b))
#define stream_copyFromStream( sc, src, nb ) \
(sc)->vtable->m_stream_copyFromStream((sc), (src), (nb))
#define stream_getPos(sc, w) \
(sc)->vtable->m_stream_getPos((sc), (w))
#define stream_setPos(sc, p, w) \
(sc)->vtable->m_stream_setPos((sc), (p), (w))
#define stream_open(sc) \
(sc)->vtable->m_stream_open((sc))
#define stream_close(sc) \
(sc)->vtable->m_stream_close((sc))
#define stream_getSize(sc) \
(sc)->vtable->m_stream_getSize((sc))
#define stream_makeReturnAddr(sc,addr,len) \
(sc)->vtable->m_stream_makeReturnAddr((sc),(addr),(len))
#define stream_getAddress(sc) \
(sc)->vtable->m_stream_getAddress((sc))
#define stream_setAddress(sc,ch) \
(sc)->vtable->m_stream_setAddress((sc),(ch))
#endif /* _XWSTREAM_H_ */

View file

@ -0,0 +1,51 @@
/* Copyright 2001 by Eric House (fixin@peak.org) (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.
*/
/* This is the linux version of what's always been a palm file. There's
* probably a better way of doing this, but this is it for now.
*/
#ifndef _LOCALIZEDSTRINCLUDES_H_
#define _LOCALIZEDSTRINCLUDES_H_
enum {
STRD_REMAINING_TILES_ADD,
STRD_UNUSED_TILES_SUB,
STR_COMMIT_CONFIRM,
STRD_TURN_SCORE,
STR_BONUS_ALL,
STR_NONLOCAL_NAME,
STRD_TIME_PENALTY_SUB,
STRD_CUMULATIVE_SCORE,
STRS_TRAY_AT_START,
STRS_MOVE_DOWN,
STRS_MOVE_ACROSS,
STRS_NEW_TILES,
STRSS_TRADED_FOR,
STR_PASS,
STR_PHONY_REJECTED,
STRD_ROBOT_TRADED,
STR_ROBOT_MOVED,
STR_REMOTE_MOVED,
STR_LAST
};
#endif

120
xwords4/linux/Makefile Normal file
View file

@ -0,0 +1,120 @@
# -*- 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=linux
TARGET=$(PLATFORM)/xwords
CC = gcc
include ../common/config.mk
DEFINES = -DPLATFORM_LINUX -DKEY_SUPPORT -DKEYBOARD_NAV -DNODE_CAN_4
DEFINES += -DSTUBBED_DICT
# build with GTK
DEFINES += -DPLATFORM_GTK
# build with ncurses (-u flag chooses over gtk if both present)
DEFINES += -DPLATFORM_NCURSES
#DEFINES += -DDRAW_WITH_PRIMITIVES
DEFINES += -DBEYOND_IR
#-DDEBUG -DEIGHT_TILES
#GPROFFLAG = -pg
# INCLUDES += -I/usr/lib/glib/include
INCLUDES += -I$(HOME)/usr/local/include/ -I/home/ehouse/usr/local/lib/glib/include -I$(HOME)/usr/local/pilot/include
OBJ = $(PLATFORM)/linuxmain.o \
$(PLATFORM)/linuxdict.o \
$(PLATFORM)/gtkmain.o \
$(PLATFORM)/gtkdraw.o \
$(PLATFORM)/gtkask.o \
$(PLATFORM)/gtkletterask.o \
$(PLATFORM)/gtkpasswdask.o \
$(PLATFORM)/gtknewgame.o \
$(PLATFORM)/cursesmain.o \
$(PLATFORM)/cursesdraw.o \
$(PLATFORM)/cursesask.o \
$(PLATFORM)/filestream.o \
# $(PLATFORM)/linuxcommpipe.o \
LIBS = -lm -L $(HOME)/usr/local/pilot/lib $(GPROFFLAG)
# -lprc
# -pg
CFLAGS = -g -Wall -O2 $(GPROFFLAG)
ifneq (,$(findstring DPLATFORM_GTK,$(DEFINES)))
# LIBS += `pkg-config --libs gtk+-2.0`
# CFLAGS += `pkg-config --cflags gtk+-2.0` -DPOINTER_SUPPORT
LIBS += $$(gtk-config --libs)
CFLAGS += `gtk-config --cflags` -DPOINTER_SUPPORT
endif
ifneq (,$(findstring DPLATFORM_NCURSES,$(DEFINES)))
LIBS += -lncurses
endif
ifeq (,$(findstring -DCLIENT_ONLY,$(DEFINES)))
# LIBS += -lprc
endif
# provides an all: target
include ../common/rules.mk
help:
@echo -n "make debug | curses_debug | gtk_debug | "
@echo "curses_client_debug | gtk_client_debug"
#test:
# $(MAKE) test1 DEFINES="$(DEFINES) -FOOBAR"
#test1:
# echo $(findstring FOO,$(DEFINES))
# echo $(DEFINES)
curses_client_debug:
$(MAKE) $(TARGET) DEFINES="$(DEFINES) -DDEBUG -DPLATFORM_NCURSES -DCLIENT_ONLY"
curses_debug:
$(MAKE) $(TARGET) DEFINES="$(DEFINES) -DDEBUG -DPLATFORM_NCURSES"
gtk_client_debug:
$(MAKE) $(TARGET) DEFINES="$(DEFINES) -DDEBUG -DPLATFORM_GTK -DCLIENT_ONLY"
gtk_debug:
$(MAKE) $(TARGET) DEFINES="$(DEFINES) -DDEBUG -DPLATFORM_GTK"
debug:
$(MAKE) $(TARGET) DEFINES="$(DEFINES) -DDEBUG -DPLATFORM_NCURSES -DPLATFORM_GTK"
memdebug:
$(MAKE) $(TARGET) DEFINES="$(DEFINES) -DDEBUG -DPLATFORM_NCURSES -DPLATFORM_GTK -DMEM_DEBUG"
gprof:
$(MAKE) $(TARGET) GPROFFLAG=-pg DEFINES="$(DEFINES) -DPLATFORM_NCURSES -DPLATFORM_GTK"
$(TARGET): $(COMMONOBJ) $(OBJ) *.h Makefile
@ls -d $(PLATFORM) >/dev/null || mkdir $(PLATFORM)
$(CC) $(CFLAGS) $(DEFINES) $(COMMONOBJ) $(OBJ) $(LIBS) -o $@
$(PLATFORM)/%.o: %.c
@ls -d $(PLATFORM) >/dev/null || mkdir $(PLATFORM)
$(CC) -c $(INCLUDES) $(DEFINES) -DPLATFORM=$(PLATFORM) $(CFLAGS) $< -o $@
clean:
rm -rf $(PLATFORM)/*.o $(TARGET)
cd ../common && $(MAKE) PLATFORM=$(PLATFORM) $@

52
xwords4/linux/README.txt Normal file
View file

@ -0,0 +1,52 @@
This directory contains the desktop Linux port of Crosswords.
To build, run a shell in this directory and type
# make
or
# make debug
or
# make memdebug
Any will work as long as you have both libncurses and libgtk-1.2 and
the associated headers installed on your system. If you don't you can
play with the Makefile to build with only GTK or ncurses.
Once you've built, go to the linux directory that will be created
within this one and type, at a minimum
# ./xwords -s -n SomeName
to get a GTK-based game with the built-in (English) tiles. (Add the
-u flag to run with ncurses instead of GTK.) There will be no robot
player, and the hint feature ('?' button) won't work. For that you
need a real dictionary, which you can build in the dawg directory. If
you build the BasEnglish2to8.xwd one in dawg/English, this command
will run a two person game between you and the machine:
# ./xwords -s -r robot -n SomeName -d ../../dawg/English/BasEnglish2to8.xwd
Here are the commands to launch two copies playing against each other
over the network. Do these in separate shells both in the same
directory as the above commands ran in. Launch the one with the -s
flag (the "server") first.
s1# ./xwords -s -r Eric -N -p 4000 -l 4001
s2# ./xwords -d ../../dawg/English/BasEnglish2to8.xwd -r Kati -p 4001 -l 6002
Both of these have "robot" players. Turn one or both -r flags to -n
for human players who make their own moves.
If you want to run them on different machines, just add the -a flag to
the client telling it on what machine to find the server (since it
sends the first message, and the server will use the return address
from that message.)
*****
Please keep in mind that these Linux desktop clients are meant for
development only, as testbeds for code in ../common/ that will also be
used for the "real" products on PalmOS, PocketPC, eBookman, etc.
They're not supposed to be polished.

189
xwords4/linux/cursesask.c Normal file
View file

@ -0,0 +1,189 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 2000 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.
*/
/* Put up a "dialog" with a question and tab-selectable buttons below.
* On <CR>, return the number of the button selected at the time.
*/
#ifdef PLATFORM_NCURSES
#include <ncurses.h>
#include "cursesask.h"
#define ASK_HEIGHT 5
#define PAD 2
#define MAX_LINES 15
#define MIN_WIDTH 25
static void
drawButtons( WINDOW* confWin, XP_U16 line, short spacePerButton,
short numButtons, short curSelButton, char** button1 )
{
short i;
for ( i = 0; i < numButtons; ++i ) {
short len = strlen( *button1 );
if ( i == curSelButton ) {
wstandout( confWin );
}
mvwprintw( confWin, line, ((i+1) * spacePerButton) - (len/2),
"[%s]", *button1 );
if ( i == curSelButton ) {
wstandend( confWin );
}
++button1;
}
wrefresh( confWin );
} /* drawButtons */
/* Figure out how many lines there are and how wide the widest is.
*/
typedef struct FormatInfo {
XP_U16 nLines;
XP_U16 maxLen;
struct {
XP_UCHAR* substr;
XP_U16 len;
} line[MAX_LINES];
} FormatInfo;
static void
measureAskText( XP_UCHAR* question, FormatInfo* fip )
{
XP_U16 i;
XP_U16 maxWidth = 0;
XP_Bool done = XP_FALSE;
for ( i = 0; i < MAX_LINES && !done; ++i ) {
XP_UCHAR* next = strstr( question, XP_CR );
XP_U16 thisWidth;
fip->line[i].substr = question;
if ( !!next ) {
thisWidth = next - question;
} else {
thisWidth = strlen(question);
done = XP_TRUE;
}
fip->line[i].len = thisWidth;
if ( thisWidth > maxWidth ) {
maxWidth = thisWidth;
}
question = next + strlen(XP_CR);
}
fip->nLines = i;
fip->maxLen = maxWidth;
} /* measureAskText */
short
cursesask( CursesAppGlobals* globals, char* question, short numButtons,
char* button1, ... )
{
WINDOW* confWin;
int x, y, rows, row, nLines;
short newSelButton = 0;
short curSelButton = 1; /* force draw by being different */
short spacePerButton, num;
short maxWidth;
XP_Bool dismissed = XP_FALSE;
FormatInfo fi;
int len;
measureAskText( question, &fi );
len = fi.maxLen;
if ( len < MIN_WIDTH ) {
len = MIN_WIDTH;
}
getmaxyx(globals->boardWin, y, x);
rows = fi.nLines;
maxWidth = x - (PAD*2) - 2; /* 2 for two borders */
if ( len > x-2 ) {
rows = (len / maxWidth) + 1;
len = maxWidth;
}
nLines = ASK_HEIGHT + rows - 1;
confWin = newwin( nLines, len+(PAD*2),
(y/2) - (nLines/2), (x-len-2)/2 );
wclear( confWin );
box( confWin, '|', '-');
for ( row = 0; row < rows; ++row ) {
mvwaddnstr( confWin, row+1, PAD,
fi.line[row].substr, fi.line[row].len );
}
spacePerButton = (len+(PAD*2)) / (numButtons + 1);
while ( !dismissed ) {
int ch;
if ( newSelButton != curSelButton ) {
drawButtons( confWin, rows+1, spacePerButton, numButtons,
curSelButton=newSelButton, &button1 );
}
ch = fgetc( stdin );
/* char ch = wgetch( globals->menuWin ); */
switch ( ch ) {
case '\t':
case 'L':
newSelButton = (curSelButton+1) % numButtons;
break;
case 'H':
newSelButton = (numButtons+curSelButton-1) % numButtons;
break;
case EOF:
case 4: /* C-d */
case 27: /* ESC */
curSelButton = 0; /* should be the cancel case */
case '\r':
case '\n':
dismissed = XP_TRUE;
break;
case '1':
case '2':
case '3':
case '4':
num = ch - '1';
if ( num < numButtons ) {
newSelButton = num;
}
break;
default:
beep();
}
}
delwin( confWin );
/* this leaves a ghost line, but I can't figure out a better way. */
wtouchln( globals->boardWin, (y/2)-(nLines/2), ASK_HEIGHT + rows - 1, 1 );
wrefresh( globals->boardWin );
return curSelButton;
} /* ask */
#endif /* PLATFORM_NCURSES */

29
xwords4/linux/cursesask.h Normal file
View file

@ -0,0 +1,29 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 2000 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 _CURSESASK_H_
#define _CURSESASK_H_
#include "cursesmain.h"
short cursesask( CursesAppGlobals* globals, char* question,
short numButtons, char* button1, ... );
#endif

500
xwords4/linux/cursesdraw.c Normal file
View file

@ -0,0 +1,500 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 1997-2000 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 PLATFORM_NCURSES
#include <stdlib.h>
#include <assert.h>
#include "cursesmain.h"
#include "draw.h"
#include "board.h"
static void
drawRect( WINDOW* win, XP_Rect* rect, char vert, char hor )
{
wmove( win, rect->top-1, rect->left );
whline( win, hor, rect->width );
wmove( win, rect->top+rect->height, rect->left );
whline( win, hor, rect->width );
wmove( win, rect->top, rect->left-1 );
wvline( win, vert, rect->height );
wmove( win, rect->top, rect->left+rect->width );
wvline( win, vert, rect->height );
} /* drawRect */
static void
eraseRect( CursesDrawCtx* dctx, XP_Rect* rect )
{
int y, bottom = rect->top + rect->height;
for ( y = rect->top; y < bottom; ++y ) {
mvwhline( dctx->boardWin, y, rect->left, ' ', rect->width );
}
} /* eraseRect */
static void
curses_draw_destroyCtxt( DrawCtx* p_dctx )
{
// CursesDrawCtx* dctx = (CursesDrawCtx*)p_dctx;
} /* draw_setup */
static void
curses_draw_boardBegin( DrawCtx* p_dctx, XP_Rect* rect, XP_Bool hasfocus )
{
CursesDrawCtx* dctx = (CursesDrawCtx*)p_dctx;
if ( hasfocus ) {
drawRect( dctx->boardWin, rect, '+', '+' );
} else {
drawRect( dctx->boardWin, rect, '|', '-' );
}
} /* draw_finish */
static void
curses_draw_trayBegin( DrawCtx* p_dctx, XP_Rect* rect, XP_U16 owner,
XP_Bool hasfocus )
{
CursesDrawCtx* dctx = (CursesDrawCtx*)p_dctx;
if ( hasfocus ) {
drawRect( dctx->boardWin, rect, '+', '+' );
} else {
drawRect( dctx->boardWin, rect, '|', '-' );
}
} /* draw_finish */
static void
curses_draw_scoreBegin( DrawCtx* p_dctx, XP_Rect* rect, XP_U16 numPlayers,
XP_Bool hasfocus )
{
CursesDrawCtx* dctx = (CursesDrawCtx*)p_dctx;
if ( hasfocus ) {
drawRect( dctx->boardWin, rect, '+', '+' );
} else {
drawRect( dctx->boardWin, rect, '|', '-' );
}
} /* curses_draw_scoreBegin */
static void
formatRemText( char* buf, XP_S16 nTilesLeft )
{
strcpy( buf, "Tiles left in pool: " );
buf += strlen( buf );
if ( nTilesLeft < 0 ) {
strcpy( buf, "***" );
} else {
sprintf( buf, "%.3d", nTilesLeft );
}
} /* formatRemText */
static void
curses_draw_measureRemText( DrawCtx* dctx, XP_Rect* r,
XP_S16 nTilesLeft,
XP_U16* width, XP_U16* height )
{
char buf[32];
formatRemText( buf, nTilesLeft );
*width = strlen(buf);
*height = 1;
} /* curses_draw_measureRemText */
static void
curses_draw_drawRemText( DrawCtx* p_dctx, XP_Rect* rInner, XP_Rect* rOuter,
XP_S16 nTilesLeft )
{
CursesDrawCtx* dctx = (CursesDrawCtx*)p_dctx;
char buf[32];
formatRemText( buf, nTilesLeft );
mvwprintw( dctx->boardWin, rInner->top, rInner->left, buf );
} /* curses_draw_drawRemText */
static void
formatScoreText( char* buf, DrawScoreInfo* dsi )
{
XP_S16 nTilesLeft = dsi->nTilesLeft;
char label;
XP_Bool isRobot = dsi->isRobot;
if ( nTilesLeft < 0 ) {
nTilesLeft = MAX_TRAY_TILES;
}
if ( dsi->isRemote ) {
if ( isRobot ) {
label = 'R';
} else {
label = 'N';
}
} else {
if ( isRobot ) {
label = 'r';
} else {
label = 'n';
}
}
sprintf( buf, "%s[%c] %s (%d)", (dsi->isTurn?"->":" "),
label, dsi->name, nTilesLeft );
} /* formatScoreText */
static void
curses_draw_measureScoreText( DrawCtx* p_dctx, XP_Rect* r,
DrawScoreInfo* dsi,
XP_U16* width, XP_U16* height )
{
char buf[100];
formatScoreText( buf, dsi );
*width = strlen( buf );
*height = 1; /* one line per player */
} /* curses_draw_measureScoreText */
static void
curses_draw_score_pendingScore( DrawCtx* p_dctx, XP_Rect* rect, XP_S16 score,
XP_U16 playerNum )
{
CursesDrawCtx* dctx = (CursesDrawCtx*)p_dctx;
char buf[4];
if ( score >= 0 ) {
sprintf( buf, "%.3d", score );
} else {
strcpy( buf, "???" );
}
mvwprintw( dctx->boardWin, rect->top+1, rect->left, "pt:" );
mvwprintw( dctx->boardWin, rect->top+2, rect->left, "%s", buf );
wrefresh( dctx->boardWin );
} /* curses_draw_score_pendingScore */
static void
curses_draw_boardFinished( DrawCtx* p_dctx )
{
CursesDrawCtx* dctx = (CursesDrawCtx*)p_dctx;
wrefresh( dctx->boardWin );
} /* curses_draw_boardFinished */
static void
curses_draw_trayFinished( DrawCtx* p_dctx )
{
CursesDrawCtx* dctx = (CursesDrawCtx*)p_dctx;
wrefresh( dctx->boardWin );
} /* draw_finished */
static void
curses_draw_scoreFinished( DrawCtx* p_dctx )
{
CursesDrawCtx* dctx = (CursesDrawCtx*)p_dctx;
wrefresh( dctx->boardWin );
} /* draw_finished */
#define MY_PAIR 1
static void
curses_draw_score_drawPlayer( DrawCtx* p_dctx, XP_S16 playerNum,
XP_Rect* rInner, XP_Rect* rOuter,
DrawScoreInfo* dsi )
{
CursesDrawCtx* dctx = (CursesDrawCtx*)p_dctx;
char curSBuf[6];
char buf[100];
int y = rInner->top;
if ( dsi->selected ) {
wstandout( dctx->boardWin );
}
/* first blank out the whole thing! */
mvwhline( dctx->boardWin, y, rOuter->left, ' ', rOuter->width );
/* print the name and turn/remoteness indicator */
formatScoreText( buf, dsi );
mvwprintw( dctx->boardWin, y, rOuter->left, buf );
if ( 0 && dsi->isTurn/* && !remote */ ) {
/* print score:curscore at the right edge. If curscore is illegal,
replace with "??" */
mvwprintw( dctx->boardWin, y, rOuter->left + rOuter->width - 7,
"%s:%.3d", curSBuf, dsi->score );
} else {
mvwprintw( dctx->boardWin, y, rOuter->left + rOuter->width - 3,
"%.3d", dsi->score );
}
if ( dsi->selected ) {
wstandend( dctx->boardWin );
}
/* (void)wcolor_set( dctx->boardWin, prev, NULL ); */
} /* curses_draw_score_drawPlayer */
static XP_Bool
curses_draw_drawCell( DrawCtx* p_dctx, XP_Rect* rect,
XP_UCHAR* letter, XP_Bitmap bitmap,
XP_S16 owner, XWBonusType bonus, XP_Bool isBlank,
XP_Bool highlight, XP_Bool isStar )
{
CursesDrawCtx* dctx = (CursesDrawCtx*)p_dctx;
if ( *letter == LETTER_NONE ) {
switch ( bonus ) {
case BONUS_DOUBLE_LETTER:
letter[0] = '+'; break;
case BONUS_DOUBLE_WORD:
letter[0] = '*'; break;
case BONUS_TRIPLE_LETTER:
letter[0] = '^'; break;
case BONUS_TRIPLE_WORD:
letter[0] = '#'; break;
default:
letter[0] = ' ';
} /* switch */
}
if ( highlight ) {
wstandout( dctx->boardWin );
}
mvwaddnstr( dctx->boardWin, rect->top, rect->left, letter,
strlen(letter) );
if ( highlight ) {
wstandend( dctx->boardWin );
}
return XP_TRUE;
} /* curses_draw_drawCell */
static void
curses_stringInTile( CursesDrawCtx* dctx, XP_Rect* rect,
XP_UCHAR* letter, XP_UCHAR* val )
{
eraseRect( dctx, rect );
mvwaddnstr( dctx->boardWin, rect->top+1, rect->left+(rect->width/2),
letter, strlen(letter) );
if ( !!val ) {
int len = strlen( val );
mvwaddnstr( dctx->boardWin, rect->top+rect->height-2,
rect->left + rect->width - len, val, len );
}
} /* curses_stringInTile */
static void
curses_draw_drawTile( DrawCtx* p_dctx, XP_Rect* rect,
XP_UCHAR* textP, XP_Bitmap bitmap,
XP_S16 val, XP_Bool highlighted )
{
char numbuf[5];
char letterbuf[5];
char* nump = NULL;
CursesDrawCtx* dctx = (CursesDrawCtx*)p_dctx;
letterbuf[0] = !!textP? *textP: '_'; /* BLANK or bitmap */
letterbuf[1] = '\0';
if ( val >= 0 ) {
sprintf( numbuf, "%.2d", val );
if ( numbuf[0] == '0' ) {
numbuf[0] = ' ';
}
nump = numbuf;
}
curses_stringInTile( dctx, rect, letterbuf, nump );
if ( highlighted ) {
mvwaddnstr( dctx->boardWin, rect->top+rect->height-1,
rect->left, "*-*", 3 );
}
} /* curses_draw_drawTile */
static void
curses_draw_drawTileBack( DrawCtx* p_dctx, XP_Rect* rect )
{
CursesDrawCtx* dctx = (CursesDrawCtx*)p_dctx;
curses_stringInTile( dctx, rect, "?", "?" );
} /* curses_draw_drawTileBack */
static void
curses_draw_drawTrayDivider( DrawCtx* p_dctx, XP_Rect* rect,
XP_Bool selected )
{
CursesDrawCtx* dctx = (CursesDrawCtx*)p_dctx;
wmove( dctx->boardWin, rect->top, rect->left );
wvline( dctx->boardWin, '#', rect->height );
} /* curses_draw_drawTrayDivider */
static void
curses_draw_drawBoardArrow( DrawCtx* p_dctx, XP_Rect* rect,
XWBonusType cursorBonus, XP_Bool vertical )
{
CursesDrawCtx* dctx = (CursesDrawCtx*)p_dctx;
#if 1
char ch = vertical?'|':'-';
mvwaddch( dctx->boardWin, rect->top, rect->left, ch );
#else
chtype curChar = mvwinch(dctx->boardWin, rect->top, rect->left );
wstandout( dctx->boardWin );
mvwaddch( dctx->boardWin, rect->top, rect->left, curChar);
wstandend( dctx->boardWin );
#endif
} /* curses_draw_drawBoardArrow */
static void
curses_draw_drawBoardCursor( DrawCtx* p_dctx, XP_Rect* rect )
{
CursesDrawCtx* dctx = (CursesDrawCtx*)p_dctx;
chtype curChar = mvwinch(dctx->boardWin, rect->top, rect->left );
wstandout( dctx->boardWin );
mvwaddch( dctx->boardWin, rect->top, rect->left, curChar);
wstandend( dctx->boardWin );
} /* curses_draw_drawBoardCursor */
static void
curses_draw_drawTrayCursor( DrawCtx* p_dctx, XP_Rect* rect )
{
CursesDrawCtx* dctx = (CursesDrawCtx*)p_dctx;
wmove( dctx->boardWin, rect->top, rect->left );
whline( dctx->boardWin, 'v', rect->width );
} /* curses_draw_drawTrayCursor */
static void
curses_draw_clearRect( DrawCtx* p_dctx, XP_Rect* rectP )
{
CursesDrawCtx* dctx = (CursesDrawCtx*)p_dctx;
XP_Rect rect = *rectP;
eraseRect( dctx, &rect );
} /* curses_draw_clearRect */
static XP_UCHAR*
curses_draw_getMiniWText( DrawCtx* p_dctx, XWMiniTextType textHint )
{
return "Trading...";
} /* curses_draw_getMiniWText */
static void
curses_draw_measureMiniWText( DrawCtx* p_dctx, unsigned char* str,
XP_U16* widthP, XP_U16* heightP )
{
*widthP = strlen(str) + 4;
*heightP = 3;
} /* curses_draw_measureMiniWText */
static void
curses_draw_drawMiniWindow( DrawCtx* p_dctx, XP_UCHAR* text,
XP_Rect* rect, void** closure )
{
CursesDrawCtx* dctx = (CursesDrawCtx*)p_dctx;
XP_Rect smallerR;
smallerR.top = rect->top + 1;
smallerR.left = rect->left + 1;
smallerR.width = rect->width - 2;
smallerR.height = rect->height - 2;
eraseRect( dctx, rect );
drawRect( dctx->boardWin, &smallerR, '|', '-' );
mvwprintw( dctx->boardWin, smallerR.top, smallerR.left, text,
strlen(text) );
} /* curses_draw_drawMiniWindow */
static void
curses_draw_eraseMiniWindow( DrawCtx* p_dctx, XP_Rect* rect,
XP_Bool lastTime, void** closure,
XP_Bool* invalUnder )
{
*invalUnder = XP_TRUE;
} /* curses_draw_eraseMiniWindow*/
#if 0
static void
curses_draw_frameTray( DrawCtx* p_dctx, XP_Rect* rect )
{
CursesDrawCtx* dctx = (CursesDrawCtx*)p_dctx;
box( dctx->boardWin, '*', '+');
} /* curses_draw_frameTray */
#endif
static void
draw_doNothing( DrawCtx* dctx, ... )
{
} /* draw_doNothing */
DrawCtx*
cursesDrawCtxtMake( WINDOW* boardWin )
{
CursesDrawCtx* dctx = malloc( sizeof(CursesDrawCtx) );
short i;
dctx->vtable = malloc( sizeof(*(((CursesDrawCtx*)dctx)->vtable)) );
for ( i = 0; i < sizeof(*dctx->vtable)/4; ++i ) {
((void**)(dctx->vtable))[i] = draw_doNothing;
}
SET_VTABLE_ENTRY( dctx->vtable, draw_destroyCtxt, curses );
SET_VTABLE_ENTRY( dctx->vtable, draw_boardBegin, curses );
SET_VTABLE_ENTRY( dctx->vtable, draw_boardFinished, curses );
SET_VTABLE_ENTRY( dctx->vtable, draw_trayBegin, curses );
SET_VTABLE_ENTRY( dctx->vtable, draw_trayFinished, curses );
SET_VTABLE_ENTRY( dctx->vtable, draw_scoreBegin, curses );
SET_VTABLE_ENTRY( dctx->vtable, draw_measureRemText, curses );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawRemText, curses );
SET_VTABLE_ENTRY( dctx->vtable, draw_measureScoreText, curses );
SET_VTABLE_ENTRY( dctx->vtable, draw_score_pendingScore, curses );
SET_VTABLE_ENTRY( dctx->vtable, draw_score_drawPlayer, curses );
SET_VTABLE_ENTRY( dctx->vtable, draw_scoreFinished, curses );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawCell, curses );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawTile, curses );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawTileBack, curses );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawTrayDivider, curses );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawBoardArrow, curses );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawBoardCursor, curses );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawTrayCursor, curses );
SET_VTABLE_ENTRY( dctx->vtable, draw_clearRect, curses );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawMiniWindow, curses );
SET_VTABLE_ENTRY( dctx->vtable, draw_eraseMiniWindow, curses );
SET_VTABLE_ENTRY( dctx->vtable, draw_getMiniWText, curses );
SET_VTABLE_ENTRY( dctx->vtable, draw_measureMiniWText, curses );
/* SET_VTABLE_ENTRY( dctx, draw_getBonusText, gtk ); */
/* SET_VTABLE_ENTRY( dctx, draw_eraseMiniWindow, gtk ); */
/* SET_VTABLE_ENTRY( dctx, draw_frameTray, curses ); */
dctx->boardWin = boardWin;
return (DrawCtx*)dctx;
} /* curses_drawctxt_init */
#endif /* PLATFORM_NCURSES */

950
xwords4/linux/cursesmain.c Normal file
View file

@ -0,0 +1,950 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 2000 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 PLATFORM_NCURSES
#include <ncurses.h>
#include <signal.h>
#include <assert.h>
#include <ctype.h>
#include <netdb.h> /* gethostbyname */
#include <errno.h>
//#include <net/netinet.h>
#include <sys/poll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "linuxmain.h"
#include "cursesmain.h"
#include "cursesask.h"
#include "model.h"
#include "draw.h"
#include "board.h"
#include "engine.h"
/* #include "compipe.h" */
#include "xwproto.h"
#include "xwstream.h"
#include "xwstate.h"
#include "server.h"
#include "memstream.h"
#include "util.h"
#define MENU_WINDOW_HEIGHT 5 /* three lines plus borders */
#define INFINITE_TIMEOUT -1
CursesAppGlobals globals; /* must be global b/c of SIGWINCH_handler */
static void changeFocus( CursesAppGlobals* globals );
#ifdef MEM_DEBUG
# define MEMPOOL params->util->mpool,
#else
# define MEMPOOL
#endif
/* extern int errno; */
static void
cursesUserError( CursesAppGlobals* globals, char* format, ... )
{
char buf[512];
va_list ap;
va_start( ap, format );
vsprintf( buf, format, ap );
(void)cursesask( globals, buf, 1, "OK" );
va_end(ap);
} /* cursesUserError */
static void
curses_util_listenPortChange( XW_UtilCtxt* uc, XP_U16 newPort )
{
CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure;
XP_LOGF( "listenPortChange called: not sure what to do" );
/* if this isn't true, need to tear down and rebind socket */
XP_ASSERT( newPort == globals->cGlobals.params->defaultListenPort );
} /* curses_util_listenPortChange */
static void
curses_util_userError( XW_UtilCtxt* uc, UtilErrID id )
{
CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure;
XP_UCHAR* message = linux_getErrString( id );
cursesUserError( globals, message );
} /* curses_util_userError */
static XP_U16
curses_util_userQuery( XW_UtilCtxt* uc, UtilQueryID id, XWStreamCtxt* stream )
{
CursesAppGlobals* globals;
char* question;
char* answers[3];
short numAnswers = 0;
XP_Bool freeMe = XP_FALSE;
XP_U16 result;
switch( id ) {
case QUERY_COMMIT_TURN:
question = strFromStream( stream );
freeMe = XP_TRUE;
answers[numAnswers++] = "Cancel";
answers[numAnswers++] = "Ok";
break;
case QUERY_COMMIT_TRADE:
question = "Commit trade?";
answers[numAnswers++] = "Cancel";
answers[numAnswers++] = "Ok";
break;
case QUERY_ROBOT_MOVE:
case QUERY_ROBOT_TRADE:
question = strFromStream( stream );
freeMe = XP_TRUE;
answers[numAnswers++] = "Ok";
break;
default:
XP_ASSERT( 0 );
return 0;
}
globals = (CursesAppGlobals*)uc->closure;
result = cursesask( globals, question, numAnswers,
answers[0], answers[1], answers[2] );
if ( freeMe ) {
free( question );
}
return result;
} /* curses_util_userQuery */
static void
curses_util_askBlankFace(XW_UtilCtxt* uc, DictionaryCtxt* dict,
XP_UCHAR* buf )
{
buf[0] = 'A';
buf[1] = '\0';
} /* curses_util_askBlankFace */
static void
curses_util_trayHiddenChange( XW_UtilCtxt* uc, XW_TrayVisState state )
{
/* nothing to do if we don't have a scrollbar */
} /* curses_util_trayHiddenChange */
static void
cursesShowFinalScores( CursesAppGlobals* globals )
{
XWStreamCtxt* stream;
XP_UCHAR* text;
stream = mem_stream_make( MPPARM(globals->cGlobals.params->util->mpool)
globals->cGlobals.params->vtMgr,
globals, CHANNEL_NONE, NULL );
server_writeFinalScores( globals->cGlobals.game.server, stream );
text = strFromStream( stream );
(void)cursesask( globals, text, 1, "Ok" );
free( text );
} /* cursesShowFinalScores */
static void
curses_util_notifyGameOver( XW_UtilCtxt* uc )
{
CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure;
board_draw( globals->cGlobals.game.board );
/* game belongs in cGlobals... */
if ( globals->cGlobals.params->printHistory ) {
catGameHistory( &globals->cGlobals );
}
if ( globals->cGlobals.params->quitAfter ) {
globals->timeToExit = XP_TRUE;
} else if ( globals->cGlobals.params->undoWhenDone ) {
server_handleUndo( globals->cGlobals.game.server );
} else {
cursesShowFinalScores( globals );
}
} /* curses_util_notifyGameOver */
static XP_Bool
curses_util_hiliteCell( XW_UtilCtxt* uc, XP_U16 col, XP_U16 row )
{
return XP_TRUE;
} /* curses_util_hiliteCell */
static XP_Bool
curses_util_engineProgressCallback( XW_UtilCtxt* uc )
{
return XP_TRUE;
} /* curses_util_engineProgressCallback */
static void
curses_util_setTimer( XW_UtilCtxt* uc, XWTimerReason why )
{
XP_ASSERT( 0 ); /* no pen-down events..... */
} /* curses_util_setTimer */
static void
curses_util_requestTime( XW_UtilCtxt* uc )
{
/* I've created a pipe whose read-only end is plugged into the array of
fds that my event loop polls so that I can write to it to simulate
post-event on a more familiar system. It works, so no complaints! */
CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure;
(void)write( globals->timepipe[1], "!", 1 );
} /* curses_util_requestTime */
static void
initCurses( CursesAppGlobals* globals )
{
WINDOW* mainWin;
WINDOW* menuWin;
WINDOW* boardWin;
int x, y;
/* ncurses man page says most apps want this sequence */
mainWin = initscr();
cbreak();
noecho();
nonl();
intrflush(stdscr, FALSE);
keypad(stdscr, TRUE);
getmaxyx(mainWin, y, x);
globals->statusLine = y - MENU_WINDOW_HEIGHT - 1;
menuWin = newwin( MENU_WINDOW_HEIGHT, x, y-MENU_WINDOW_HEIGHT, 0 );
nodelay(menuWin, 1); /* don't block on getch */
boardWin = newwin( MAX_ROWS+1, x, 0, 0 );
/* leaveok( boardWin, 1 ); */
/* leaveok( menuWin, 1 ); */
globals->menuWin = menuWin;
globals->boardWin = boardWin;
globals->mainWin = mainWin;
} /* initCurses */
#if 0
static void
showStatus( CursesAppGlobals* globals )
{
char* str;
switch ( globals->state ) {
case XW_SERVER_WAITING_CLIENT_SIGNON:
str = "Waiting for client[s] to connnect";
break;
case XW_SERVER_READY_TO_PLAY:
str = "It's somebody's move";
break;
default:
str = "unknown state";
}
standout();
mvaddstr( globals->statusLine, 0, str );
/* clrtoeol(); */
standend();
refresh();
} /* showStatus */
#endif
typedef XP_Bool (*CursesMenuHandler)(CursesAppGlobals* globals);
typedef struct MenuList {
CursesMenuHandler handler;
char* desc;
char* keyDesc;
char key;
} MenuList;
static XP_Bool
handleQuit( CursesAppGlobals* globals )
{
globals->timeToExit = XP_TRUE;
return XP_TRUE;
} /* handleQuit */
static XP_Bool
handleTab( CursesAppGlobals* globals )
{
globals->doDraw = board_handleKey( globals->cGlobals.game.board,
XP_FOCUSCHANGE_KEY );
changeFocus( globals );
return XP_TRUE;
} /* handleTab */
static XP_Bool
handleRet( CursesAppGlobals* globals )
{
globals->doDraw = board_handleKey( globals->cGlobals.game.board,
XP_RETURN_KEY );
return XP_TRUE;
} /* handleRet */
static XP_Bool
handleHint( CursesAppGlobals* globals )
{
XP_Bool redo;
globals->doDraw = board_requestHint( globals->cGlobals.game.board, &redo );
return XP_TRUE;
} /* handleHint */
static XP_Bool
handleCommit( CursesAppGlobals* globals )
{
globals->doDraw = board_commitTurn( globals->cGlobals.game.board );
return XP_TRUE;
} /* handleCommit */
static XP_Bool
handleJuggle( CursesAppGlobals* globals )
{
globals->doDraw = board_juggleTray( globals->cGlobals.game.board );
return XP_TRUE;
} /* handleJuggle */
static XP_Bool
handleHide( CursesAppGlobals* globals )
{
XW_TrayVisState curState =
board_getTrayVisState( globals->cGlobals.game.board );
if ( curState == TRAY_REVEALED ) {
globals->doDraw = board_hideTray( globals->cGlobals.game.board );
} else {
globals->doDraw = board_showTray( globals->cGlobals.game.board );
}
return XP_TRUE;
} /* handleJuggle */
static XP_Bool
handleFlip( CursesAppGlobals* globals )
{
globals->doDraw = board_flip( globals->cGlobals.game.board );
return XP_TRUE;
} /* handleFlip */
static XP_Bool
handleToggleValues( CursesAppGlobals* globals )
{
globals->doDraw = board_toggle_showValues( globals->cGlobals.game.board );
return XP_TRUE;
} /* handleToggleValues */
static XP_Bool
handleBackspace( CursesAppGlobals* globals )
{
globals->doDraw = board_handleKey( globals->cGlobals.game.board,
XP_CURSOR_KEY_DEL );
return XP_TRUE;
} /* handleBackspace */
static XP_Bool
handleUndo( CursesAppGlobals* globals )
{
globals->doDraw = server_handleUndo( globals->cGlobals.game.server );
return XP_TRUE;
} /* handleUndo */
static XP_Bool
handleReplace( CursesAppGlobals* globals )
{
globals->doDraw = board_replaceTiles( globals->cGlobals.game.board );
return XP_TRUE;
} /* handleReplace */
MenuList sharedMenuList[] = {
{ handleQuit, "Quit", "Q", 'Q' },
{ handleTab, "Change focus", "<tab>", '\t' },
{ handleRet, "Click/tap", "<ret>", '\r' },
{ handleHint, "Hint", "?", '?' },
{ handleCommit, "Commit move", "C", 'C' },
{ handleFlip, "Flip", "F", 'F' },
{ handleToggleValues, "Show values", "V", 'V' },
{ handleBackspace, "Remove from board", "<del>", 8 },
{ handleUndo, "Undo prev", "U", 'U' },
{ handleReplace, "uNdo cur", "N", 'N' },
{ NULL, NULL, NULL, '\0'}
};
static XP_Bool
handleLeft( CursesAppGlobals* globals )
{
globals->doDraw = board_handleKey( globals->cGlobals.game.board,
XP_CURSOR_KEY_LEFT );
return XP_TRUE;
} /* handleLeft */
static XP_Bool
handleDivLeft( CursesAppGlobals* globals )
{
globals->doDraw = board_moveDivider( globals->cGlobals.game.board,
XP_FALSE );
return XP_TRUE;
} /* handleDivLeft */
static XP_Bool
handleDivRight( CursesAppGlobals* globals )
{
globals->doDraw = board_moveDivider( globals->cGlobals.game.board,
XP_TRUE );
return XP_TRUE;
} /* handleDivRight */
static XP_Bool
handleRight( CursesAppGlobals* globals )
{
globals->doDraw = board_handleKey( globals->cGlobals.game.board,
XP_CURSOR_KEY_RIGHT );
return XP_TRUE;
} /* handleRight */
static XP_Bool
handleUp( CursesAppGlobals* globals )
{
globals->doDraw = board_handleKey( globals->cGlobals.game.board,
XP_CURSOR_KEY_UP );
return XP_TRUE;
} /* handleUp */
static XP_Bool
handleDown( CursesAppGlobals* globals )
{
globals->doDraw = board_handleKey( globals->cGlobals.game.board,
XP_CURSOR_KEY_DOWN );
return XP_TRUE;
} /* handleDown */
MenuList boardMenuList[] = {
{ handleLeft, "Left", "H", 'H' },
{ handleRight, "Right", "L", 'L' },
{ handleUp, "Up", "J", 'J' },
{ handleDown, "Down", "K", 'K' },
{ NULL, NULL, NULL, '\0'}
};
MenuList scoreMenuList[] = {
{ handleUp, "Up", "J", 'J' },
{ handleDown, "Down", "K", 'K' },
{ NULL, NULL, NULL, '\0'}
};
MenuList trayMenuList[] = {
{ handleLeft, "Left", "H", 'H' },
{ handleRight, "Right", "L", 'L' },
{ handleDivLeft, "Div left", "{", '{' },
{ handleDivRight, "Div right", "}", '}' },
{ handleJuggle, "Juggle", "J", 'J' },
{ handleHide, "[un]hIde", "I", 'I' },
{ NULL, NULL, NULL, '\0'}
};
static void
figureMaxes( MenuList* mList, short maxLines, short* maxKeyP, short* maxCmdP )
{
short i;
*maxKeyP = *maxCmdP = 0;
for ( i = 0; i < maxLines && mList->handler != NULL; ++i ) {
short keyLen = strlen(mList->keyDesc);
short cmdLen = strlen(mList->desc);
*maxKeyP = XP_MAX( *maxKeyP, keyLen );
*maxCmdP= XP_MAX( *maxCmdP, cmdLen );
++mList;
}
} /* figureMaxes */
static void
drawMenuFromList( CursesAppGlobals* globals, MenuList* menuList )
{
short i;
short maxKey = 0, maxCmd = 0;
short line = 0, col;
short nLines;
int winMaxY, winMaxX;
WINDOW* win = globals->menuWin;
XP_Bool done = XP_FALSE;
getmaxyx( win, winMaxY, winMaxX );
nLines = globals->nLinesMenu;
if ( nLines == 0 ) {
nLines = 1;
}
for ( ; !done; ++nLines ) {
MenuList* entry = sharedMenuList;
XP_Bool isShared = XP_TRUE;
wclear( win );
maxKey = maxCmd = 0;
for ( line = 0, col = -2, i = 0; ; ++entry, ++line ) {
char* key;
if ( entry->handler == NULL ) {
if ( !isShared ) {
done = XP_TRUE;
break;
} else {
isShared = XP_FALSE;
entry = menuList;
XP_ASSERT( !!entry->handler );
}
}
if ( line % nLines == 0 ) {
line = 0;
col += maxKey + maxCmd + 2;
figureMaxes( entry, nLines, &maxKey, &maxCmd );
if ( (col + maxCmd + strlen(entry->keyDesc)) >= winMaxX ) {
break;
}
}
key = entry->keyDesc;
wstandout( win );
mvwaddstr( win, line, col+maxKey-strlen(key), key );
wstandend( win );
mvwaddstr( win, line, col+maxKey+1, entry->desc );
}
}
globals->nLinesMenu = nLines - 1;
wrefresh( win );
} /* drawMenuFromList */
static void
SIGWINCH_handler( int signal )
{
int x, y;
assert( signal == SIGWINCH );
endwin();
/* (*globals.drawMenu)( &globals ); */
getmaxyx( stdscr, y, x );
wresize( globals.mainWin, y-MENU_WINDOW_HEIGHT, x );
board_draw( globals.cGlobals.game.board );
} /* SIGWINCH_handler */
static void
cursesListenOnSocket( CursesAppGlobals* globals, int newSock,
XWStreamCtxt* stream )
{
XP_ASSERT( globals->fdCount+1 < FD_MAX );
XP_WARNF( "setting fd[%d] to %d", globals->fdCount, newSock );
globals->fdArray[globals->fdCount].fd = newSock;
globals->fdArray[globals->fdCount].events = POLLIN;
globals->streams[globals->fdCount] = stream;
++globals->fdCount;
XP_LOGF( "listenOnSocket: there are now %d sources to poll",
globals->fdCount );
} /* cursesListenOnSocket */
/*
* Ok, so this doesn't block yet....
*/
static XP_Bool
blocking_gotEvent( CursesAppGlobals* globals, int* ch )
{
XP_Bool result = XP_FALSE;
int numEvents;
short fdIndex;
XP_Bool redraw = XP_FALSE;
numEvents = poll( globals->fdArray, globals->fdCount, INFINITE_TIMEOUT );
if ( numEvents > 0 ) {
/* stdin first */
if ( (globals->fdArray[FD_STDIN].revents & POLLIN) != 0 ) {
int evtCh = fgetc(stdin);
*ch = evtCh;
result = XP_TRUE;
--numEvents;
}
if ( (globals->fdArray[FD_STDIN].revents & ~POLLIN ) ) {
XP_LOGF( "some other events set on stdin" );
}
if ( (globals->fdArray[FD_TIMEEVT].revents & POLLIN) != 0 ) {
char ch;
/* XP_DEBUGF( "curses got a USER EVENT\n" ); */
(void)read(globals->fdArray[FD_TIMEEVT].fd, &ch, 1 );
}
fdIndex = FD_FIRSTSOCKET;
if ( numEvents > 0 &&
(globals->fdArray[fdIndex].revents & POLLIN) != 0 ) {
int flen, nBytes;
XP_U8 buf[256];
struct sockaddr_in addr_sock;
--numEvents;
flen = sizeof(addr_sock);
nBytes = recvfrom( globals->fdArray[fdIndex].fd,
buf, sizeof(buf), 0,
(struct sockaddr *)&addr_sock, &flen );
if ( nBytes == -1 ) {
XP_WARNF( "recvfrom: errno=%d\n", errno );
} else {
XWStreamCtxt* inboundS;
XP_Bool redraw = XP_FALSE;
XP_STATUSF( "recvfrom=>%d", nBytes );
inboundS = stream_from_msgbuf( &globals->cGlobals,
buf, nBytes );
if ( !!inboundS ) {
CommsAddrRec addrRec;
XP_MEMSET( &addrRec, 0, sizeof(addrRec) );
addrRec.conType = COMMS_CONN_IP;
addrRec.u.ip.ipAddr = ntohl(addr_sock.sin_addr.s_addr);
XP_LOGF( "captured incomming ip address: 0x%lx",
addrRec.u.ip.ipAddr );
if ( comms_checkIncommingStream(globals->cGlobals.game.comms,
inboundS,
&addrRec ) ) {
XP_LOGF( "comms read port: %d",
addrRec.u.ip.port );
redraw = server_receiveMessage( globals->cGlobals.game.server,
inboundS );
}
stream_destroy( inboundS );
}
/* if there's something to draw resulting from the message, we
need to give the main loop time to reflect that on the
screen before giving the server another shot. So just call
the idle proc. */
if ( redraw ) {
curses_util_requestTime(globals->cGlobals.params->util);
}
}
++fdIndex;
}
redraw = server_do( globals->cGlobals.game.server ) || redraw;
if ( redraw ) {
/* messages change a lot */
board_invalAll( globals->cGlobals.game.board );
board_draw( globals->cGlobals.game.board );
}
}
return result;
} /* blocking_gotEvent */
static void
changeFocus( CursesAppGlobals* globals )
{
BoardObjectType focussed =
board_getFocusOwner( globals->cGlobals.game.board );
if ( focussed == OBJ_TRAY ) {
globals->menuList = trayMenuList;
drawMenuFromList( globals, trayMenuList );
} else if ( focussed == OBJ_BOARD ) {
globals->menuList = boardMenuList;
drawMenuFromList( globals, boardMenuList );
} else if ( focussed == OBJ_SCORE ) {
globals->menuList = scoreMenuList;
drawMenuFromList( globals, scoreMenuList );
} else {
XP_ASSERT(0);
}
} /* changeFocus */
#if 0
static void
initClientSocket( CursesAppGlobals* globals, char* serverName )
{
struct hostent* hostinfo;
hostinfo = gethostbyname( serverName );
if ( !hostinfo ) {
userError( globals, "unable to get host info for %s\n", serverName );
} else {
char* hostName = inet_ntoa( *(struct in_addr*)hostinfo->h_addr );
XP_LOGF( "gethostbyname returned %s", hostName );
globals->csInfo.client.serverAddr = inet_addr(hostName);
XP_LOGF( "inet_addr returned %lu",
globals->csInfo.client.serverAddr );
}
} /* initClientSocket */
#endif
static VTableMgr*
curses_util_getVTManager(XW_UtilCtxt* uc)
{
CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure;
return globals->cGlobals.params->vtMgr;
} /* linux_util_getVTManager */
static XP_Bool
curses_util_askPassword( XW_UtilCtxt* uc, const XP_UCHAR* name,
XP_UCHAR* buf, XP_U16* len )
{
XP_WARNF( "curses_util_askPassword not implemented" );
return XP_FALSE;
} /* curses_util_askPassword */
static void
curses_util_yOffsetChange( XW_UtilCtxt* uc, XP_U16 oldOffset, XP_U16 newOffset )
{
if ( oldOffset != newOffset ) {
XP_WARNF( "curses_util_yOffsetChange(%d,%d) not implemented",
oldOffset, newOffset );
}
} /* curses_util_yOffsetChange */
static XP_Bool
curses_util_warnIllegalWord( XW_UtilCtxt* uc, BadWordInfo* bwi, XP_U16 player,
XP_Bool turnLost )
{
XP_WARNF( "curses_util_warnIllegalWord not implemented" );
return XP_FALSE;
} /* curses_util_warnIllegalWord */
static void
cursesSendOnClose( XWStreamCtxt* stream, void* closure )
{
XP_S16 result;
CursesAppGlobals* globals = (CursesAppGlobals*)closure;
XP_LOGF( "cursesSendOnClose called" );
result = comms_send( globals->cGlobals.game.comms, COMMS_CONN_IP, stream );
} /* cursesSendOnClose */
static XWStreamCtxt*
curses_util_makeStreamFromAddr(XW_UtilCtxt* uc, XP_PlayerAddr channelNo )
{
CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure;
LaunchParams* params = globals->cGlobals.params;
XWStreamCtxt* stream = mem_stream_make( MPPARM(uc->mpool)
params->vtMgr,
uc->closure, channelNo,
cursesSendOnClose );
return stream;
} /* curses_util_makeStreamFromAddr */
static void
setupCursesUtilCallbacks( CursesAppGlobals* globals, XW_UtilCtxt* util )
{
util->vtable->m_util_userError = curses_util_userError;
util->vtable->m_util_getVTManager = curses_util_getVTManager;
util->vtable->m_util_askPassword = curses_util_askPassword;
util->vtable->m_util_yOffsetChange = curses_util_yOffsetChange;
util->vtable->m_util_warnIllegalWord = curses_util_warnIllegalWord;
util->vtable->m_util_makeStreamFromAddr = curses_util_makeStreamFromAddr;
util->vtable->m_util_userQuery = curses_util_userQuery;
util->vtable->m_util_askBlankFace = curses_util_askBlankFace;
util->vtable->m_util_trayHiddenChange = curses_util_trayHiddenChange;
util->vtable->m_util_notifyGameOver = curses_util_notifyGameOver;
util->vtable->m_util_hiliteCell = curses_util_hiliteCell;
util->vtable->m_util_engineProgressCallback =
curses_util_engineProgressCallback;
util->vtable->m_util_setTimer = curses_util_setTimer;
util->vtable->m_util_requestTime = curses_util_requestTime;
util->vtable->m_util_listenPortChange = curses_util_listenPortChange;
util->vtable->m_util_requestTime = curses_util_requestTime;
util->closure = globals;
} /* setupCursesUtilCallbacks */
static void
sendOnClose( XWStreamCtxt* stream, void* closure )
{
CursesAppGlobals* globals = closure;
XP_LOGF( "curses sendOnClose called" );
XP_ASSERT( !!globals->cGlobals.game.comms );
comms_send( globals->cGlobals.game.comms, COMMS_CONN_IP, stream );
} /* sendOnClose */
static XP_Bool
handleKeyEvent( CursesAppGlobals* globals, MenuList* list, char ch )
{
while ( list->handler != NULL ) {
if ( list->key == ch ) {
if ( (*list->handler)(globals) ) {
return XP_TRUE;
}
}
++list;
}
return XP_FALSE;
} /* handleKeyEvent */
static XP_Bool
passKeyToBoard( CursesAppGlobals* globals, char ch )
{
XP_Bool handled = ch >= 'a' && ch <= 'z';
if ( handled ) {
ch += 'A' - 'a';
globals->doDraw = board_handleKey( globals->cGlobals.game.board, ch );
}
XP_LOGF( "passKeyToBoard: handled=%d on %x (%c)", handled, ch, ch );
return handled;
} /* passKeyToBoard */
void
cursesmain( XP_Bool isServer, LaunchParams* params )
{
int piperesult;
int sock;
DictionaryCtxt* dict;
CommsAddrRec addr;
memset( &globals, 0, sizeof(globals) );
globals.amServer = isServer;
globals.cGlobals.params = params;
globals.cp.showBoardArrow = XP_TRUE;
globals.cp.showRobotScores = params->showRobotScores;
dict = params->dict;
setupCursesUtilCallbacks( &globals, params->util );
globals.cGlobals.defaultServerName = params->info.clientInfo.serverName;
cursesListenOnSocket( &globals, 0, NULL ); /* stdin */
piperesult = pipe( globals.timepipe );
XP_ASSERT( piperesult == 0 );
cursesListenOnSocket( &globals, globals.timepipe[0], NULL ); /* reader pipe */
sock = initListenerSocket( params->defaultListenPort );
cursesListenOnSocket( &globals, sock, NULL );
signal( SIGWINCH, SIGWINCH_handler );
initCurses( &globals );
globals.draw = (CursesDrawCtx*)cursesDrawCtxtMake( globals.boardWin );
game_makeNewGame( MEMPOOL &globals.cGlobals.game, &params->gi,
params->util, (DrawCtx*)globals.draw,
&globals.cp, linux_udp_send, &globals );
addr.conType = COMMS_CONN_IP;
addr.u.ip.ipAddr = 0; /* ??? */
addr.u.ip.port = params->defaultSendPort;
comms_setAddr( globals.cGlobals.game.comms,
&addr, params->defaultListenPort );
model_setDictionary( globals.cGlobals.game.model, params->dict );
board_setPos( globals.cGlobals.game.board, 1, 1, XP_FALSE );
board_setScale( globals.cGlobals.game.board, 1, 1 );
board_setScoreboardLoc( globals.cGlobals.game.board, 20, 1, 50,
5, /*4 players + rem*/ XP_FALSE );
board_setTrayLoc( globals.cGlobals.game.board, 25, 8, 3, 4, 1 );
/* no divider -- yet */
/* board_setTrayVisible( globals.board, XP_TRUE, XP_FALSE ); */
board_invalAll( globals.cGlobals.game.board );
/* send any events that need to get off before the event loop begins */
if ( !isServer ) {
if ( 1 /* stream_open( params->info.clientInfo.stream ) */) {
server_initClientConnection( globals.cGlobals.game.server,
mem_stream_make( MEMPOOL
params->vtMgr,
&globals,
(XP_PlayerAddr)0,
sendOnClose ) );
#if 0
cursesListenOnSocket(
&globals, NULL, /* replaces below */
/* linux_getStreamSocket( params->info.clientInfo.stream ), */
params->info.clientInfo.stream );
#endif
} else {
cursesUserError( &globals, "Unable to open connection to server");
exit( 0 );
}
}
server_do( globals.cGlobals.game.server );
globals.menuList = boardMenuList;
drawMenuFromList( &globals, boardMenuList );
board_draw( globals.cGlobals.game.board );
while ( !globals.timeToExit ) {
int ch;
if ( blocking_gotEvent( &globals, &ch )
&& (handleKeyEvent( &globals, globals.menuList, ch )
|| handleKeyEvent( &globals, sharedMenuList, ch )
|| passKeyToBoard( &globals, ch ) ) ) {
if ( globals.doDraw ) {
board_draw( globals.cGlobals.game.board );
globals.doDraw = XP_FALSE;
}
}
}
endwin();
} /* cursesmain */
#endif /* PLATFORM_NCURSES */

119
xwords4/linux/cursesmain.h Normal file
View file

@ -0,0 +1,119 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 1997-2000 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 _CURSESMAIN_H_
#define _CURSESMAIN_H_
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/unistd.h>
#include <sys/poll.h>
#include <ncurses.h>
#include "draw.h"
#include "main.h"
#include "board.h"
#include "model.h"
#include "dictnry.h"
#include "xwstream.h"
#include "comms.h"
#include "server.h"
#include "xwstate.h"
#include "util.h"
/* #include "compipe.h" */
typedef struct CursesDrawCtx {
DrawCtxVTable* vtable;
WINDOW* boardWin;
/* WINDOW* trayWin; */
} CursesDrawCtx;
typedef struct CursesAppGlobals CursesAppGlobals;
typedef XP_Bool (*EventFunc)(CursesAppGlobals* globals, int ch);
/* typedef void (*MenuDrawer)(CursesAppGlobals* globals); */
#define FD_MAX 6
#define FD_STDIN 0
#define FD_TIMEEVT 1
#define FD_FIRSTSOCKET 2
struct CursesAppGlobals {
CommonGlobals cGlobals;
CursesDrawCtx* draw;
DictionaryCtxt* dictionary;
EngineCtxt* engine;
CommonPrefs cp;
XP_Bool amServer; /* this process acting as server */
WINDOW* mainWin;
WINDOW* menuWin;
WINDOW* boardWin;
XP_Bool timeToExit;
XP_Bool doDraw;
struct MenuList* menuList;
XP_U16 nLinesMenu;
union {
struct {
XWStreamCtxt* stream; /* how we can reach the server */
} client;
struct {
int serverSocket;
XP_Bool socketOpen;
} server;
} csInfo;
short statusLine;
XWGameState state;
struct sockaddr_in listenerSockAddr;
short fdCount;
struct pollfd fdArray[FD_MAX]; /* one for stdio, one for listening socket */
XWStreamCtxt* streams[FD_MAX]; /* [0], for stdin, will be null.... */
int timepipe[2]; /* for reading/writing "user events" */
};
DrawCtx* cursesDrawCtxtMake( WINDOW* boardWin );
/* Ports: Client and server pick a port at startup on which they'll listen.
* If both are to be on the same device using localhost as their ip address,
* then they need to be listening on different ports. Server finds out what
* port client is listening on from the return address of the first message
* client sends -- I think. (I'm not sure that when I create a socket to use
* to SEND to the server that I specify the port on which I'm listening.
* Maybe I need to include that in a platform-specific part of the connect
* message.... Clearly there will need to be such a thing.
*/
void cursesmain( XP_Bool isServer, LaunchParams* params );
#endif

169
xwords4/linux/filestream.c Normal file
View file

@ -0,0 +1,169 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 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.
*/
#if 0
#include <stdio.h>
#include "xwstream.h"
typedef struct LinuxFileStreamCtxt {
StreamCtxVTable* vtable;
FILE* file;
} LinuxFileStreamCtxt;
static void make_vtable( LinuxFileStreamCtxt* stream );
XWStreamCtxt*
linux_make_fileStream( char* fileName, XP_Bool forWriting )
{
LinuxFileStreamCtxt* result = malloc( sizeof(*result) );
XP_MEMSET( result, 0, sizeof(*result) );
make_vtable( result );
result->file = fopen( fileName, forWriting? "w":"r" );
XP_ASSERT( result->file );
return (XWStreamCtxt*)result;
} /* linux_make_fileStream */
static void
linux_file_stream_getBytes( XWStreamCtxt* p_sctx, void* where,
XP_U16 count )
{
LinuxFileStreamCtxt* stream = (LinuxFileStreamCtxt*)p_sctx;
XP_ASSERT( !!stream->file );
fread( where, count, 1, stream->file );
} /* linux_file_stream_getBytes */
static XP_U8
linux_file_stream_getU8( XWStreamCtxt* p_sctx )
{
XP_U8 result;
linux_file_stream_getBytes( p_sctx, &result, sizeof(result) );
return result;
} /* linux_file_stream_getU8 */
static XP_U16
linux_file_stream_getU16( XWStreamCtxt* p_sctx )
{
XP_U16 result;
linux_file_stream_getBytes( p_sctx, &result, sizeof(result) );
return result;
} /* linux_file_stream_getU16 */
static XP_U32
linux_file_stream_getU32( XWStreamCtxt* p_sctx )
{
XP_U32 result;
linux_file_stream_getBytes( p_sctx, &result, sizeof(result) );
return result;
} /* linux_file_stream_getU32 */
static void
linux_file_stream_putBytes( XWStreamCtxt* p_sctx, void* where,
XP_U16 count )
{
LinuxFileStreamCtxt* stream = (LinuxFileStreamCtxt*)p_sctx;
size_t written;
XP_ASSERT( !!stream->file );
written = fwrite( where, count, 1, stream->file );
XP_ASSERT( written != 0 );
} /* linux_file_stream_putBytes */
static void
linux_file_stream_putU8( XWStreamCtxt* p_sctx, XP_U8 data )
{
linux_file_stream_putBytes( p_sctx, &data, sizeof(data) );
} /* linux_file_stream_putU8 */
static void
linux_file_stream_putU16( XWStreamCtxt* p_sctx, XP_U16 data )
{
linux_file_stream_putBytes( p_sctx, &data, sizeof(data) );
} /* linux_common_stream_putUI16 */
static void
linux_file_stream_putU32( XWStreamCtxt* p_sctx, XP_U32 data )
{
linux_file_stream_putBytes( p_sctx, &data, sizeof(data) );
} /* linux_file_stream_putUI32 */
static void
linux_file_stream_open( XWStreamCtxt* p_sctx )
{
LinuxFileStreamCtxt* stream = (LinuxFileStreamCtxt*)p_sctx;
XP_ASSERT( !!stream->file );
rewind( stream->file );
} /* linux_file_stream_open */
static void
linux_file_stream_close( XWStreamCtxt* p_sctx )
{
LinuxFileStreamCtxt* stream = (LinuxFileStreamCtxt*)p_sctx;
XP_ASSERT( !!stream->file );
fclose( stream->file );
stream->file = NULL;
} /* linux_file_stream_close */
static void
linux_file_stream_destroy( XWStreamCtxt* p_sctx )
{
LinuxFileStreamCtxt* stream;
stream = (LinuxFileStreamCtxt*)p_sctx;
if ( !!stream->file ) {
stream_close( stream );
}
free( p_sctx->vtable );
free( stream );
} /* linux_file_stream_destroy */
static void
make_vtable( LinuxFileStreamCtxt* stream )
{
XP_ASSERT( !stream->vtable );
stream->vtable = malloc( sizeof(*stream->vtable) );
SET_VTABLE_ENTRY( stream, stream_getU8, linux_file );
SET_VTABLE_ENTRY( stream, stream_getBytes, linux_file );
SET_VTABLE_ENTRY( stream, stream_getU16, linux_file );
SET_VTABLE_ENTRY( stream, stream_getU32, linux_file );
SET_VTABLE_ENTRY( stream, stream_putU8, linux_file );
SET_VTABLE_ENTRY( stream, stream_putBytes, linux_file );
SET_VTABLE_ENTRY( stream, stream_putU16, linux_file );
SET_VTABLE_ENTRY( stream, stream_putU32, linux_file );
SET_VTABLE_ENTRY( stream, stream_destroy, linux_file );
SET_VTABLE_ENTRY( stream, stream_open, linux_file );
SET_VTABLE_ENTRY( stream, stream_close, linux_file );
/* PENDING(ehouse) These are part of some subclass, not of stream
overall */
/* SET_VTABLE_ENTRY( stream, stream_getSize, linux_file ); */
/* SET_VTABLE_ENTRY( stream, stream_getAddress, linux_file ); */
/* SET_VTABLE_ENTRY( stream, stream_setAddress, linux_file ); */
} /* make_vtable */
#endif

View file

@ -0,0 +1,26 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 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 _FILESTREAM_H_
#define _FILESTREAM_H_
XWStreamCtxt* linux_make_fileStream( char* fileName, XP_Bool forWriting );
#endif

14
xwords4/linux/flip.xpm Normal file
View file

@ -0,0 +1,14 @@
/* XPM */
static char * flip_xpm[] = {
"8 8 3 1",
" c None",
". c #000000",
"+ c #FFFFFF",
"........",
".+......",
".++.....",
".+++....",
".++++...",
".+++++..",
".++++++.",
"........"};

83
xwords4/linux/gtkask.c Normal file
View file

@ -0,0 +1,83 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 2000 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 PLATFORM_GTK
#include <stdarg.h>
#include "gtkask.h"
static void
button_event( GtkWidget* widget, void* closure )
{
gboolean* whichSet = (gboolean*)closure;
*whichSet = 1;
gtk_main_quit();
}
gint
gtkask( GtkAppGlobals* globals, gchar *message, gint numButtons,
char* button1, ... )
{
GtkWidget* dialog;
GtkWidget* label;
GtkWidget* button;
short i;
gboolean* results = g_malloc( numButtons * sizeof(results[0]) );
char** butList = &button1;
/* Create the widgets */
dialog = gtk_dialog_new();
gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
label = gtk_label_new( message );
for ( i = 0; i < numButtons; ++i ) {
button = gtk_button_new_with_label( *butList );
results[i] = 0;
gtk_signal_connect( GTK_OBJECT( button ), "clicked",
GTK_SIGNAL_FUNC(button_event), &results[i] );
gtk_container_add( GTK_CONTAINER( GTK_DIALOG(dialog)->action_area),
button );
++butList;
}
/* Add the label, and show everything we've added to the dialog. */
gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),
label);
gtk_widget_show_all (dialog);
/* returns when button handler calls gtk_main_quit */
gtk_main();
gtk_widget_destroy( dialog );
for ( i = 0; i < numButtons; ++i ) {
if ( results[i] ) {
break;
}
}
g_free( results );
return i;
} /* gtkask */
#endif

32
xwords4/linux/gtkask.h Normal file
View file

@ -0,0 +1,32 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 2000 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 PLATFORM_GTK
#ifndef _GTKASK_H_
#define _GTKASK_H_
#include "gtkmain.h"
gint gtkask( GtkAppGlobals* globals, gchar *message, gint numButtons,
char* button1, ... );
#endif
#endif /* PLATFORM_GTK */

913
xwords4/linux/gtkdraw.c Normal file
View file

@ -0,0 +1,913 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4;-*- */
/*
* Copyright 1997-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.
*/
#ifdef PLATFORM_GTK
#include <stdlib.h>
#include <stdio.h>
#include "gtkmain.h"
#include "draw.h"
#include "board.h"
#include "linuxmain.h"
/* static GdkGC* newGCForColor( GdkWindow* window, XP_Color* newC ); */
static void
insetRect( XP_Rect* r, short i )
{
r->top += i;
r->left += i;
i *= 2;
r->width -= i;
r->height -= i;
} /* insetRect */
//#define DRAW_WHAT(dc) ((dc)->pixmap)
#define DRAW_WHAT(dc) ((dc)->widget->window)
static void
eraseRect(GtkDrawCtx* dctx, XP_Rect* rect )
{
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->widget->style->white_gc,
TRUE, rect->left, rect->top,
rect->width, rect->height );
} /* eraseRect */
static void
frameRect( GtkDrawCtx* dctx, XP_Rect* rect )
{
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawGC, FALSE, rect->left, rect->top,
rect->width, rect->height );
} /* frameRect */
#ifdef DRAW_WITH_PRIMITIVES
static void
gtk_prim_draw_setClip( DrawCtx* p_dctx, XP_Rect* newClip, XP_Rect* oldClip)
{
} /* gtk_prim_draw_setClip */
static void
gtk_prim_draw_frameRect( DrawCtx* p_dctx, XP_Rect* rect )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
frameRect( dctx, rect );
} /* gtk_prim_draw_frameRect */
static void
gtk_prim_draw_invertRect( DrawCtx* p_dctx, XP_Rect* rect )
{
/* not sure you can do this on GTK!! */
} /* gtk_prim_draw_invertRect */
static void
gtk_prim_draw_clearRect( DrawCtx* p_dctx, XP_Rect* rect )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
eraseRect( dctx, rect );
} /* gtk_prim_draw_clearRect */
static void
gtk_prim_draw_drawString( DrawCtx* p_dctx, XP_UCHAR* str,
XP_U16 x, XP_U16 y )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_U16 fontHeight = 10; /* FIX ME */
gdk_draw_string( DRAW_WHAT(dctx), dctx->gdkFont, dctx->drawGC,
x, y + fontHeight, str );
} /* gtk_prim_draw_drawString */
static void
gtk_prim_draw_drawBitmap( DrawCtx* p_dctx, XP_Bitmap bm,
XP_U16 x, XP_U16 y )
{
} /* gtk_prim_draw_drawBitmap */
static void
gtk_prim_draw_measureText( DrawCtx* p_dctx, XP_UCHAR* str,
XP_U16* widthP, XP_U16* heightP)
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
gint len = strlen(str);
gint width = gdk_text_measure( dctx->gdkFont, str, len );
*widthP = width;
*heightP = 12; /* ??? :-) */
} /* gtk_prim_draw_measureText */
#endif /* DRAW_WITH_PRIMITIVES */
static void
drawBitmapFromLBS( GtkDrawCtx* dctx, XP_Bitmap bm, XP_Rect* rect )
{
GdkPixmap* pm;
LinuxBMStruct* lbs = (LinuxBMStruct*)bm;
gint x, y;
XP_U8* bp;
XP_U16 i;
XP_S16 nBytes;
XP_U16 nCols, nRows;
nCols = lbs->nCols;
nRows = lbs->nRows;
bp = (XP_U8*)(lbs + 1); /* point to the bitmap data */
nBytes = lbs->nBytes;
pm = gdk_pixmap_new( DRAW_WHAT(dctx), nCols, nRows, -1 );
gdk_draw_rectangle( pm, dctx->widget->style->white_gc, TRUE,
0, 0, nCols, nRows );
x = 0;
y = 0;
while ( nBytes-- ) {
XP_U8 byte = *bp++;
for ( i = 0; i < 8; ++i ) {
XP_Bool draw = ((byte & 0x80) != 0);
if ( draw ) {
gdk_draw_point( pm, dctx->widget->style->black_gc, x, y );
}
byte <<= 1;
if ( ++x == nCols ) {
x = 0;
if ( ++y == nRows ) {
break;
}
}
}
}
XP_ASSERT( nBytes == -1 ); /* else we're out of sync */
gdk_draw_pixmap( DRAW_WHAT(dctx),
dctx->drawGC,
(GdkDrawable*)pm, 0, 0,
rect->left+2,
rect->top+2,
lbs->nCols,
lbs->nRows );
gdk_pixmap_unref( pm );
} /* drawBitmapFromLBS */
#if 0
static void
gtk_draw_destroyCtxt( DrawCtx* p_dctx )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
GtkAllocation* alloc = &dctx->widget->allocation;
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->widget->style->white_gc,
TRUE,
0, 0, alloc->width, alloc->height );
} /* draw_setup */
#endif
static void
gtk_draw_boardBegin( DrawCtx* p_dctx, XP_Rect* rect, XP_Bool hasfocus )
{
GdkRectangle gdkrect;
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
gdk_gc_set_foreground( dctx->drawGC, &dctx->black );
gdkrect = *(GdkRectangle*)rect;
++gdkrect.width;
++gdkrect.height;
gdk_gc_set_clip_rectangle( dctx->drawGC, &gdkrect );
/* gdk_draw_rectangle( DRAW_WHAT(dctx), */
/* dctx->drawGC, FALSE, */
/* rect->left, rect->top, rect->width, rect->height ); */
} /* draw_finish */
static void
gtk_draw_boardFinished( DrawCtx* p_dctx )
{
// GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
} /* draw_finished */
static XP_Bool
gtk_draw_drawCell( DrawCtx* p_dctx, XP_Rect* rect, XP_UCHAR* letter,
XP_Bitmap bitmap, XP_S16 owner, XWBonusType bonus,
XP_Bool isBlank, XP_Bool highlight, XP_Bool isStar )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_Rect rectInset = *rect;
XP_Bool showGrid = dctx->globals->gridOn;
eraseRect( dctx, rect );
insetRect( &rectInset, 1 );
gdk_gc_set_foreground( dctx->drawGC, &dctx->black );
if ( showGrid ) {
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawGC,
FALSE,
rect->left, rect->top, rect->width,
rect->height );
}
/* draw the bonus colors only if we're not putting a "tile" there */
if ( !!letter ) {
if ( *letter == LETTER_NONE && bonus != BONUS_NONE ) {
XP_ASSERT( bonus <= 4 );
gdk_gc_set_foreground( dctx->drawGC, &dctx->bonusColors[bonus-1] );
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawGC,
TRUE,
rectInset.left, rectInset.top,
rectInset.width+1, rectInset.height+1 );
} else {
gint len = strlen(letter);
gint width = gdk_text_measure( dctx->gdkFont, letter, len );
gint x = rect->left;
x += (rect->width - width) / 2;
if ( highlight ) {
gdk_gc_set_foreground( dctx->drawGC, &dctx->red );
} else {
gdk_gc_set_foreground( dctx->drawGC,
owner >= 0? &dctx->playerColors[owner]:
&dctx->black );
}
gdk_draw_text( DRAW_WHAT(dctx), dctx->gdkFont, dctx->drawGC,
x, rect->top+rect->height-1, letter, len );
if ( isBlank ) {
gdk_draw_arc( DRAW_WHAT(dctx), dctx->drawGC,
0, /* filled */
rect->left, /* x */
rect->top, /* y */
rect->width,/*width, */
rect->height,/*width, */
0, 360*64 );
}
}
} else if ( !!bitmap ) {
drawBitmapFromLBS( dctx, bitmap, rect );
}
if ( isStar ) {
letter = "*";
gdk_gc_set_foreground( dctx->drawGC, &dctx->black );
gdk_draw_text( DRAW_WHAT(dctx), dctx->gdkFont, dctx->drawGC,
rect->left+3, rect->top+rect->height-1,
letter, 1 );
}
return XP_TRUE;
} /* gtk_draw_drawCell */
static void
gtk_draw_invertCell( DrawCtx* p_dctx, XP_Rect* rect )
{
/* GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx; */
/* (void)gtk_draw_drawMiniWindow( p_dctx, "f", rect); */
/* GdkGCValues values; */
/* gdk_gc_get_values( dctx->drawGC, &values ); */
/* gdk_gc_set_function( dctx->drawGC, GDK_INVERT ); */
/* gdk_gc_set_clip_rectangle( dctx->drawGC, (GdkRectangle*)rect ); */
/* gdk_draw_rectangle( DRAW_WHAT(dctx), dctx->drawGC, */
/* TRUE, rect->left, rect->top, */
/* rect->width, rect->height ); */
/* gdk_gc_set_function( dctx->drawGC, values.function ); */
} /* gtk_draw_invertCell */
static void
gtk_draw_trayBegin( DrawCtx* p_dctx, XP_Rect* rect, XP_U16 owner,
XP_Bool hasfocus )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_Rect clip = *rect;
insetRect( &clip, -1 );
gdk_gc_set_clip_rectangle( dctx->drawGC, (GdkRectangle*)&clip );
/* eraseRect( dctx, rect ); */
} /* gtk_draw_trayBegin */
static void
gtk_draw_drawTile( DrawCtx* p_dctx, XP_Rect* rect, XP_UCHAR* textP,
XP_Bitmap bitmap, XP_S16 val, XP_Bool highlighted )
{
unsigned char numbuf[3];
gint len;
gint width;
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_Rect insetR = *rect;
gdk_gc_set_foreground( dctx->drawGC, &dctx->black );
/* insetRect( &insetR, 1 ); */ /* to draw inside the thing */
/* ++insetR.left; */
/* --insetR.width; */
eraseRect( dctx, &insetR );
if ( val < 0 ) {
} else {
insetRect( &insetR, 1 );
if ( !!textP ) {
if ( *textP != LETTER_NONE ) { /* blank */
gdk_draw_text( DRAW_WHAT(dctx), dctx->gdkTrayFont, dctx->drawGC,
insetR.left + 2,
insetR.top + dctx->trayFontHeight,
textP, 1 );
}
} else if ( !!bitmap ) {
drawBitmapFromLBS( dctx, bitmap, &insetR );
}
sprintf( numbuf, "%d", val );
len = strlen( numbuf );
width = gdk_text_measure( dctx->gdkFont, numbuf, len );
gdk_draw_text( DRAW_WHAT(dctx), dctx->gdkFont, dctx->drawGC,
insetR.left+insetR.width - width - 1,
insetR.top + insetR.height - 2,
numbuf, len );
/* frame the tile */
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawGC,
FALSE,
insetR.left, insetR.top, insetR.width,
insetR.height );
if ( highlighted ) {
insetRect( &insetR, 1 );
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawGC,
FALSE, insetR.left, insetR.top,
insetR.width, insetR.height);
}
}
} /* gtk_draw_drawTile */
static void
gtk_draw_drawTileBack( DrawCtx* p_dctx, XP_Rect* rect )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_Rect r = *rect;
insetRect( &r, 1 );
gdk_gc_set_foreground( dctx->drawGC, &dctx->black );
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawGC, TRUE,
r.left, r.top, r.width, r.height );
} /* gtk_draw_drawTileBack */
static void
gtk_draw_drawTrayDivider( DrawCtx* p_dctx, XP_Rect* rect, XP_Bool selected )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_Rect r = *rect;
eraseRect( dctx, &r );
++r.left;
r.width -= selected? 2:1;
if ( selected ) {
--r.height;
}
gdk_gc_set_foreground( dctx->drawGC, &dctx->black );
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawGC,
!selected,
r.left, r.top, r.width, r.height);
} /* gtk_draw_drawTrayDivider */
#if 0
static void
gtk_draw_frameBoard( DrawCtx* p_dctx, XP_Rect* rect )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
gdk_gc_set_foreground( dctx->drawGC, &dctx->black );
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawGC, FALSE,
rect->left, rect->top, rect->width, rect->height );
} /* gtk_draw_frameBoard */
static void
gtk_draw_frameTray( DrawCtx* p_dctx, XP_Rect* rect )
{
// GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
} /* gtk_draw_frameBoard */
#endif
static void
gtk_draw_clearRect( DrawCtx* p_dctx, XP_Rect* rectP )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_Rect rect = *rectP;
++rect.width;
++rect.top;
eraseRect( dctx, &rect );
} /* gtk_draw_clearRect */
static void
gtk_draw_drawBoardArrow( DrawCtx* p_dctx, XP_Rect* rectP,
XWBonusType cursorBonus, XP_Bool vertical )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
/* XP_Rect rect = *rectP; */
char curs = vertical? '|':'-';
gdk_gc_set_foreground( dctx->drawGC, &dctx->black );
gdk_draw_text( DRAW_WHAT(dctx), dctx->gdkFont, dctx->drawGC,
rectP->left+3, rectP->top+rectP->height-1,
&curs, 1 );
} /* gtk_draw_drawBoardCursor */
static void
gtk_draw_scoreBegin( DrawCtx* p_dctx, XP_Rect* rect, XP_U16 numPlayers,
XP_Bool hasfocus )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
gdk_gc_set_clip_rectangle( dctx->drawGC, (GdkRectangle*)rect );
eraseRect( dctx, rect );
} /* gtk_draw_scoreBegin */
static void
gtkDrawDrawRemText( DrawCtx* p_dctx, XP_Rect* r, XP_U16 nTilesLeft,
XP_U16* widthP, XP_U16* heightP )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
GtkAppGlobals* globals = dctx->globals;
XP_Bool isVertical = globals->cGlobals.params->verticalScore;
char buf[10];
char* bufp = buf;
XP_U16 height, width;
XP_U16 left = r->left;
XP_U16 top = r->top;
XP_Bool draw = !widthP;
gdk_gc_set_foreground( dctx->drawGC, &dctx->black );
sprintf( buf, "rem:%d", nTilesLeft );
width = 2 + gdk_text_measure( dctx->gdkFont, buf, strlen(buf) );
if ( isVertical ) {
height = 12;
left += 2;
if ( width > r->width ) {
if ( draw ) {
gdk_draw_string( DRAW_WHAT(dctx), dctx->gdkFont, dctx->drawGC,
left + 2, top + height, "rem:" );
}
bufp += 4;
top += 12;
}
} else {
height = r->height;
}
if ( draw ) {
gdk_draw_string( DRAW_WHAT(dctx), dctx->gdkFont, dctx->drawGC,
left, top + height, bufp );
} else {
*widthP = width;
*heightP = height;
}
} /* gtkDrawDrawRemText */
static void
gtk_draw_measureRemText( DrawCtx* p_dctx, XP_Rect* r, XP_S16 nTilesLeft,
XP_U16* width, XP_U16* height )
{
gtkDrawDrawRemText( p_dctx, r, nTilesLeft, width, height );
} /* gtk_draw_measureRemText */
static void
gtk_draw_drawRemText( DrawCtx* p_dctx, XP_Rect* rInner, XP_Rect* rOuter,
XP_S16 nTilesLeft )
{
gtkDrawDrawRemText( p_dctx, rInner, nTilesLeft, NULL, NULL );
} /* gtk_draw_drawRemText */
static void
widthAndText( char* buf, GdkFont* font, DrawScoreInfo* dsi,
XP_U16* widthP, XP_U16* heightP )
{
XP_S16 score = dsi->score;
XP_U16 nTilesLeft = dsi->nTilesLeft;
XP_Bool isTurn = dsi->isTurn;
char* borders = "";
if ( isTurn ) {
borders = "*";
}
sprintf( buf, "%s%.3d", borders, score );
if ( nTilesLeft < MAX_TRAY_TILES ) {
char nbuf[10];
sprintf( nbuf, ":%d", nTilesLeft );
(void)strcat( buf, nbuf );
}
(void)strcat( buf, borders );
if ( !!widthP ) {
*widthP = gdk_string_measure( font, buf );
*heightP = HOR_SCORE_HEIGHT; /* a wild-ass guess */
}
} /* widthAndText */
static void
gtk_draw_measureScoreText( DrawCtx* p_dctx, XP_Rect* r,
DrawScoreInfo* dsi,
XP_U16* width, XP_U16* height )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
char buf[20];
GdkFont* font = /* dsi->selected? dctx->gdkBoldFont : */dctx->gdkFont;
widthAndText( buf, font, dsi, width, height );
} /* gtk_draw_measureScoreText */
static void
gtk_draw_score_drawPlayer( DrawCtx* p_dctx, XP_S16 playerNum,
XP_Rect* rInner, XP_Rect* rOuter,
DrawScoreInfo* dsi )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
char scoreBuf[20];
XP_U16 x;
GdkFont* font = /* dsi->selected? dctx->gdkBoldFont : */dctx->gdkFont;
widthAndText( scoreBuf, font, dsi, NULL, NULL );
x = rInner->left;// + ((rect->width - width) /2);
gdk_gc_set_foreground( dctx->drawGC,
playerNum >= 0? &dctx->playerColors[playerNum]:
&dctx->black
/*selected? &dctx->red:&dctx->black*/ );
if ( dsi->selected ) {
gdk_draw_rectangle( DRAW_WHAT(dctx), dctx->drawGC,
TRUE, rOuter->left, rOuter->top,
rOuter->width, rOuter->height );
eraseRect( dctx, rInner );
}
gdk_draw_string( DRAW_WHAT(dctx), font, dctx->drawGC,
x, rInner->top + rInner->height, scoreBuf );
} /* gtk_draw_score_drawPlayer */
static void
gtk_draw_score_pendingScore( DrawCtx* p_dctx, XP_Rect* rect, XP_S16 score,
XP_U16 playerNum )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
char buf[5];
XP_U16 left;
XP_Rect localR;
if ( score >= 0 ) {
sprintf( buf, "%.3d", score );
} else {
strcpy( buf, "???" );
}
gdk_gc_set_foreground( dctx->drawGC, &dctx->black );
gdk_gc_set_clip_rectangle( dctx->drawGC, (GdkRectangle*)rect );
localR = *rect;
rect = &localR;
insetRect( rect, 1 );
eraseRect( dctx, rect );
left = rect->left + 1;
gdk_draw_string( DRAW_WHAT(dctx), dctx->gdkFont, dctx->drawGC,
left, rect->top + (rect->height/2), "Pts:" );
gdk_draw_string( DRAW_WHAT(dctx), dctx->gdkFont, dctx->drawGC,
left, rect->top + rect->height, buf );
} /* gtk_draw_score_pendingScore */
static void
gtk_draw_scoreFinished( DrawCtx* p_dctx )
{
/* GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx; */
} /* gtk_draw_scoreFinished */
static void
gtkFormatTimerText( XP_UCHAR* buf, XP_S16 secondsLeft )
{
XP_U16 minutes, seconds;
if ( secondsLeft < 0 ) {
*buf++ = '-';
secondsLeft *= -1;
}
minutes = secondsLeft / 60;
seconds = secondsLeft % 60;
sprintf( buf, "% 1d:%02d", minutes, seconds );
} /* gtkFormatTimerText */
static void
gtk_draw_drawTimer( DrawCtx* p_dctx, XP_Rect* rInner, XP_Rect* rOuter,
XP_U16 player, XP_S16 secondsLeft )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_UCHAR buf[10];
gtkFormatTimerText( buf, secondsLeft );
gdk_gc_set_clip_rectangle( dctx->drawGC, (GdkRectangle*)rInner );
eraseRect( dctx, rInner );
gdk_draw_string( DRAW_WHAT(dctx), dctx->gdkFont, dctx->drawGC,
rInner->left, rInner->top + rInner->height, buf );
} /* gtk_draw_drawTimer */
#define MINI_LINE_HT 12
#define MINI_V_PADDING 6
#define MINI_H_PADDING 8
static unsigned char*
gtk_draw_getMiniWText( DrawCtx* p_dctx, XWMiniTextType textHint )
{
/* GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx; */
unsigned char* str;
switch( textHint ) {
case BONUS_DOUBLE_LETTER:
str = "Double letter"; break;
case BONUS_DOUBLE_WORD:
str = "Double word"; break;
case BONUS_TRIPLE_LETTER:
str = "Triple letter"; break;
case BONUS_TRIPLE_WORD:
str = "Triple word"; break;
case INTRADE_MW_TEXT:
str = "Trading tiles;\nclick D when done"; break;
default:
XP_ASSERT( XP_FALSE );
}
return str;
} /* gtk_draw_getMiniWText */
static void
gtk_draw_measureMiniWText( DrawCtx* p_dctx, unsigned char* str,
XP_U16* widthP, XP_U16* heightP )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_U16 height, maxWidth;
for ( height = MINI_V_PADDING, maxWidth = 0; ; ) {
unsigned char* nextStr = strstr( str, "\n" );
XP_U16 len = nextStr==NULL? strlen(str): nextStr - str;
XP_U16 width = gdk_text_measure( dctx->gdkFont, str, len );
maxWidth = XP_MAX( maxWidth, width );
height += MINI_LINE_HT;
if ( nextStr == NULL ) {
break;
}
str = nextStr+1; /* skip '\n' */
}
*widthP = maxWidth + MINI_H_PADDING;
*heightP = height;
} /* gtk_draw_measureMiniWText */
static void
gtk_draw_drawMiniWindow( DrawCtx* p_dctx, unsigned char* text, XP_Rect* rect,
void** closureP )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_Rect localR = *rect;
gdk_gc_set_foreground( dctx->drawGC, &dctx->black );
gdk_gc_set_clip_rectangle( dctx->drawGC, (GdkRectangle*)&localR );
/* play some skanky games to get the shadow drawn under and to the
right... */
eraseRect( dctx, &localR );
insetRect( &localR, 1 );
--localR.width;
--localR.height;
frameRect( dctx, &localR );
--localR.top;
--localR.left;
eraseRect( dctx, &localR );
frameRect( dctx, &localR );
for ( ; ; ) { /* draw up to the '\n' each time */
unsigned char* nextStr = strstr( text, "\n" );
XP_U16 len, width, left;
if ( nextStr == NULL ) {
len = strlen(text);
} else {
len = nextStr - text;
}
localR.top += MINI_LINE_HT;
width = gdk_text_measure( dctx->gdkFont, text, len );
left = localR.left + ((localR.width - width) / 2);
gdk_draw_text( DRAW_WHAT(dctx), dctx->gdkFont, dctx->drawGC,
left, localR.top, text, len );
if ( nextStr == NULL ) {
break;
}
text = nextStr+1; /* skip the CR */
}
} /* gtk_draw_drawMiniWindow */
static void
gtk_draw_eraseMiniWindow( DrawCtx* p_dctx, XP_Rect* rect, XP_Bool lastTime,
void** closure, XP_Bool* invalUnder )
{
/* GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx; */
*invalUnder = XP_TRUE;
} /* gtk_draw_eraseMiniWindow */
#define SET_GDK_COLOR( c, r, g, b ) { \
c.red = (r); \
c.green = (g); \
c.blue = (b); \
}
static void
draw_doNothing( DrawCtx* dctx, ... )
{
} /* draw_doNothing */
static void
allocAndSet( GdkColormap* map, GdkColor* color, unsigned short red,
unsigned short green, unsigned short blue )
{
gboolean success;
color->red = red;
color->green = green;
color->blue = blue;
success = gdk_color_alloc( map, color );
if ( !success ) {
XP_WARNF( "unable to alloc color" );
}
} /* allocAndSet */
DrawCtx*
gtkDrawCtxtMake( GtkWidget *widget, GtkAppGlobals* globals )
{
GtkDrawCtx* dctx = g_malloc( sizeof(GtkDrawCtx) );
GdkFont* font;
short i;
dctx->vtable = g_malloc( sizeof(*(((GtkDrawCtx*)dctx)->vtable)) );
for ( i = 0; i < sizeof(*dctx->vtable)/4; ++i ) {
((void**)(dctx->vtable))[i] = draw_doNothing;
}
SET_VTABLE_ENTRY( dctx->vtable, draw_clearRect, gtk );
#ifdef DRAW_WITH_PRIMITIVES
SET_VTABLE_ENTRY( dctx->vtable, draw_setClip, gtk_prim );
SET_VTABLE_ENTRY( dctx->vtable, draw_frameRect, gtk_prim );
SET_VTABLE_ENTRY( dctx->vtable, draw_invertRect, gtk_prim );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawString, gtk_prim );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawBitmap, gtk_prim );
SET_VTABLE_ENTRY( dctx->vtable, draw_measureText, gtk_prim );
InitDrawDefaults( dctx->vtable );
#else
SET_VTABLE_ENTRY( dctx->vtable, draw_boardBegin, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawCell, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_invertCell, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_boardFinished, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_trayBegin, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawTile, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawTileBack, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawTrayDivider, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawBoardArrow, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_scoreBegin, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_measureRemText, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawRemText, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_measureScoreText, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_score_drawPlayer, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_score_pendingScore, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_scoreFinished, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawTimer, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_getMiniWText, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_measureMiniWText, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawMiniWindow, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_eraseMiniWindow, gtk );
#endif
/* SET_VTABLE_ENTRY( dctx, draw_frameBoard, gtk_ ); */
/* SET_VTABLE_ENTRY( dctx, draw_frameTray, gtk_ ); */
dctx->widget = widget;
dctx->globals = globals;
/* font = gdk_font_load( "-*-new century schoolbook-medium-r-normal-" */
/* "-14-100-100-100-p-82-iso8859-1" ); */
font = gdk_font_load( "-adobe-new century schoolbook-medium-r-normal-"
"-10-100-75-75-p-60-iso8859-1" );
if ( font == NULL ) {
font = gdk_font_load( "-misc-fixed-medium-r-*-*-*-140-*-*-*-*-*-*" );
}
dctx->gdkFont = font;
font = gdk_font_load( "-adobe-new century schoolbook-bold-r-normal-"
"-10-100-75-75-p-66-iso8859-1" );
dctx->gdkBoldFont = font? font:dctx->gdkFont;
/* font = gdk_font_load( "-*-new century schoolbook-bold-i-normal-" */
/* "-24-240-75-75-p-148-iso8859-1" ); */
font = gdk_font_load( "-adobe-new century schoolbook-bold-r-normal-"
"-12-120-75-75-p-77-iso8859-1" );
if ( font == NULL ) {
font = dctx->gdkFont;
}
dctx->gdkTrayFont = font;
dctx->drawGC = gdk_gc_new( widget->window );
dctx->trayFontHeight = 13;
dctx->black.red = 0x0000;
dctx->black.pixel = 0x0000;
dctx->black.green = 0x0000;
dctx->black.blue = 0x0000;
dctx->white.red = 0xFFFF;
dctx->white.pixel = 0x0000;
dctx->white.green = 0xFFFF;
dctx->white.blue = 0xFFFF;
{
GdkColormap* map = gdk_colormap_get_system();
allocAndSet( map, &dctx->bonusColors[0], 0xFFFF, 0xAFFF, 0xAFFF );
allocAndSet( map, &dctx->bonusColors[1], 0xAFFF, 0xFFFF, 0xAFFF );
allocAndSet( map, &dctx->bonusColors[2], 0xAFFF, 0xAFFF, 0xFFFF );
allocAndSet( map, &dctx->bonusColors[3], 0xFFFF, 0xAFFF, 0xFFFF );
allocAndSet( map, &dctx->playerColors[0], 0x0000, 0x0000, 0xAFFF );
allocAndSet( map, &dctx->playerColors[1], 0xAFFF, 0x0000, 0x0000 );
allocAndSet( map, &dctx->playerColors[2], 0x0000, 0xAFFF, 0x0000 );
allocAndSet( map, &dctx->playerColors[3], 0xAFFF, 0x0000, 0xAFFF );
allocAndSet( map, &dctx->red, 0xFFFF, 0x0000, 0x0000 );
}
return (DrawCtx*)dctx;
} /* gtk_drawctxt_init */
#endif /* PLATFORM_GTK */

27
xwords4/linux/gtkdraw.h Normal file
View file

@ -0,0 +1,27 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; compile-command: "make debug"; -*- */
/*
* Copyright 1997 - 2000 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 _GTKDRAW_H_
#define _GTKDRAW_H_
#include "draw.h"
DrawCtx* gtkDrawCtxtMake( GtkWidget *widget, GtkAppGlobals* globals );
#endif

View file

@ -0,0 +1,108 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 2000 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 PLATFORM_GTK
#include <stdarg.h>
#include "gtkask.h"
static void
button_event( GtkWidget* widget, void* closure )
{
XP_Bool* whichSet = (XP_Bool*)closure;
*whichSet = 1;
gtk_main_quit();
}
#define BUTTONS_PER_ROW 13
void
gtkletterask( DictionaryCtxt* dict, char* resultBuf )
{
GtkWidget* dialog;
GtkWidget* label;
Tile tile, nonBlanks;
XP_Bool results[32]; /* MAX NUM FACES */
unsigned char buf[4];
GtkWidget* vbox;
GtkWidget* hbox = NULL;
XP_U16 numFaces = dict_numTileFaces( dict );
Tile blankFace = dict_getBlankTile( dict );
XP_MEMSET( results, 0, sizeof(results) );
XP_ASSERT( numFaces > 0 );
vbox = gtk_vbox_new( FALSE, 0 );
for ( nonBlanks = tile = 0; tile < numFaces; ++tile ) {
GtkWidget* button;
if ( tile == blankFace ) {
continue;
}
if ( nonBlanks % BUTTONS_PER_ROW == 0 ) {
hbox = gtk_hbox_new( FALSE, 0 );
}
dict_tilesToString( dict, &nonBlanks, 1, buf );
button = gtk_button_new_with_label( buf );
gtk_box_pack_start( GTK_BOX(hbox), button, FALSE, TRUE, 0 );
gtk_signal_connect( GTK_OBJECT(button), "clicked", button_event,
&results[nonBlanks] );
gtk_widget_show( button );
if ( tile+1 == numFaces || (nonBlanks % BUTTONS_PER_ROW == 0) ) {
gtk_widget_show( hbox );
gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, TRUE, 0 );
}
++nonBlanks;
}
gtk_widget_show( vbox );
/* Create the widgets */
dialog = gtk_dialog_new();
gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
label = gtk_label_new( "Choose a letter for your blank." );
gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),
label);
gtk_container_add( GTK_CONTAINER( GTK_DIALOG(dialog)->action_area), vbox);
gtk_widget_show_all( dialog );
gtk_main();
gtk_widget_destroy( dialog );
for ( nonBlanks = tile = 0; tile < numFaces; ++tile ) {
if ( tile == blankFace ) {
continue;
} else if ( results[nonBlanks] ) {
break;
}
++nonBlanks;
}
dict_tilesToString( dict, &tile, 1, resultBuf );
} /* gtkletterask */
#endif /* PLATFORM_GTK */

View file

@ -0,0 +1,31 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 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.
*/
#ifdef PLATFORM_GTK
#ifndef _GTKLETTERASK_H_
#define _GTKLETTERASK_H_
#include "gtkmain.h"
void gtkletterask( DictionaryCtxt* dict, char* buf );
#endif /* _GTKLETTERASK_H_ */
#endif /* PLATFORM_GTK */

1537
xwords4/linux/gtkmain.c Normal file

File diff suppressed because it is too large Load diff

121
xwords4/linux/gtkmain.h Normal file
View file

@ -0,0 +1,121 @@
/* Copyright 1997 - 2002 by Eric House (fixin@peak.org) (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 _GTKMAIN_H_
#define _GTKMAIN_H_
#ifdef PLATFORM_GTK
#include <gtk/gtk.h>
#include <sys/time.h>
#include "draw.h"
#include "main.h"
#include "game.h"
#include "dictnry.h"
typedef struct GtkDrawCtx {
DrawCtxVTable* vtable;
/* GdkDrawable* pixmap; */
GtkWidget* widget;
struct GtkAppGlobals* globals;
GdkGC* drawGC;
GdkColor black;
GdkColor white;
GdkColor red; /* for pending tiles */
GdkColor bonusColors[4];
GdkColor playerColors[MAX_NUM_PLAYERS];
GdkFont *gdkFont;
GdkFont *gdkBoldFont;
GdkFont *gdkTrayFont;
gint trayFontHeight;
} GtkDrawCtx;
typedef struct ClientStreamRec {
XWStreamCtxt* stream;
guint key;
int sock;
} ClientStreamRec;
typedef struct GtkAppGlobals {
CommonGlobals cGlobals;
GtkWidget* window;
GtkDrawCtx* draw;
EngineCtxt* engine;
guint idleID;
struct timeval penTv; /* for timer */
XP_U32 penTimerInterval;
struct timeval scoreTv; /* for timer */
XP_U32 scoreTimerInterval;
GtkAdjustment* adjustment;
ClientStreamRec clientRecs[MAX_NUM_PLAYERS];
guint listenSockKey; /* save return from gtk_input_add_full */
CommonPrefs cp;
XP_Bool gridOn;
XP_Bool dropIncommingMsgs;
} GtkAppGlobals;
/* DictionaryCtxt* gtk_dictionary_make(); */
int gtkmain( XP_Bool isServer, LaunchParams* params, int argc, char *argv[] );
#define NUM_COLS 15
#define NUM_ROWS 15
#define MIN_SCALE 12 /* was 14 */
#define MIN_TRAY_SCALEH 24
#define MIN_TRAY_SCALEV MIN_TRAY_SCALEH
#define GTK_TRAYPAD_WIDTH 2
#define TOP_MARGIN 0 /* was 2 */
#define BOARD_LEFT_MARGIN 2
#define TRAY_LEFT_MARGIN 2
#define SCORE_BOARD_PADDING 0
#define HOR_SCORE_LEFT (BOARD_LEFT_MARGIN)
#define HOR_SCORE_HEIGHT 8
#define TIMER_HEIGHT HOR_SCORE_HEIGHT
#define HOR_SCORE_TOP (TOP_MARGIN)
#define TIMER_PAD 10
#define VERT_SCORE_TOP (TIMER_HEIGHT + TIMER_PAD)
#define VERT_SCORE_HEIGHT ((MIN_SCALE*MAX_COLS) - TIMER_HEIGHT - TIMER_PAD)
#define TIMER_WIDTH 40
#define TIMER_TOP HOR_SCORE_TOP
#define HOR_SCORE_WIDTH ((MIN_SCALE*MAX_COLS)-TIMER_PAD)
#define VERT_SCORE_WIDTH 30
#define BOARD_TOP (SCORE_TOP + SCORE_HEIGHT + SCORE_BOARD_PADDING )
#define BOARD_LEFT (BOARD_LEFT_MARGIN)
#define TRAY_LEFT TRAY_LEFT_MARGIN
#define GTK_DIVIDER_WIDTH 5
#define BOTTOM_MARGIN TOP_MARGIN
#define RIGHT_MARGIN BOARD_LEFT_MARGIN
#endif /* PLATFORM_GTK */
#endif

436
xwords4/linux/gtknewgame.c Normal file
View file

@ -0,0 +1,436 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 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.
*/
#ifdef PLATFORM_GTK
#include <stdarg.h>
#include "gtknewgame.h"
#include "strutils.h"
#define MAX_SIZE_CHOICES 10
/* make the appropriate set of entries sensitive or not
*/
typedef struct ItemNumPair {
GtkWidget* item;
short index;
gboolean found;
} ItemNumPair;
void
countBeforeSame( GtkWidget *widget, gpointer data )
{
ItemNumPair* pair = (ItemNumPair*)data;
if ( !pair->found ) {
if ( pair->item == widget ) {
pair->found = TRUE;
} else {
++pair->index;
}
}
} /* countBeforeSame */
static void
setChildrenSensitivity( GtkWidget* hbox, gboolean enabling )
{
gtk_widget_set_sensitive( hbox, enabling );
} /* setChildrenSensitivity */
static void
nplayers_menu_select( GtkWidget* item, GtkNewGameState* state )
{
short prevNPlayers = state->nPlayers;
short newNPlayers;
GtkWidget* parent = item->parent;
ItemNumPair pair;
short high, low;
gboolean enabling;
pair.item = item;
pair.index = 0;
pair.found = FALSE;
gtk_container_foreach( GTK_CONTAINER(parent), countBeforeSame, &pair );
newNPlayers = pair.index + 1;
low = XP_MIN( newNPlayers, prevNPlayers );
high = XP_MAX( newNPlayers, prevNPlayers );
enabling = newNPlayers > prevNPlayers;
/* now loop through all the hboxes */
while ( low < high ) {
setChildrenSensitivity( state->playerEntries[low], enabling );
++low;
}
state->nPlayers = newNPlayers;
} /* nplayers_menu_select */
static void
size_menu_select( GtkWidget* item, GtkNewGameState* state )
{
ItemNumPair pair;
pair.item = item;
pair.index = 0;
pair.found = FALSE;
gtk_container_foreach( GTK_CONTAINER(item->parent), countBeforeSame, &pair );
XP_DEBUGF( "changing nCols from %d to %d\n", state->nCols,
MAX_COLS - pair.index );
state->nCols = MAX_COLS - pair.index;
} /* size_menu_select */
typedef struct LoadPair {
LocalPlayer* info;
XP_U16 counter;
MPSLOT
} LoadPair;
static void
loadCopyValues( GtkWidget* item, gpointer data )
{
LoadPair* lp = (LoadPair*)data;
LocalPlayer* player = lp->info;
char* entryText;
switch( lp->counter ) {
case 0: /* labels */
case 2:
break;
case 1: /* name field */
entryText = gtk_entry_get_text( GTK_ENTRY(item) );
player->name = copyString( MPPARM(lp->mpool) entryText );
break;
case 3: /* passwd field */
entryText = gtk_entry_get_text( GTK_ENTRY(item) );
player->password = copyString( MPPARM(lp->mpool) entryText );
break;
case 4: /* is Robot */
player->isRobot = GTK_WIDGET_STATE( item ) == GTK_STATE_ACTIVE;
break;
case 5: /* is local */
player->isLocal = GTK_WIDGET_STATE( item ) == GTK_STATE_ACTIVE;
break;
default:
XP_ASSERT( 0 );
}
++lp->counter;
} /* loadCopyValues */
static void
handle_ok( GtkWidget* widget, void* closure )
{
GtkNewGameState* state = (GtkNewGameState*)closure;
CurGameInfo* gi = &state->globals->cGlobals.params->gi;
short i;
LoadPair lp;
MPASSIGN( lp.mpool, state->globals->cGlobals.params->util->mpool );
gi->nPlayers = state->nPlayers;
gi->boardSize = state->nCols; /* they're the same for now */
for ( i = 0; i < state->nPlayers; ++i ) {
LocalPlayer* player = &gi->players[i];
GtkWidget* hbox = state->playerEntries[i];
lp.info = player;
lp.counter = 0;
/* Read values out of the items in the hbox, which are, in order, the
name entry, passwd entry, isLocal box and isRobot box */
gtk_container_foreach( GTK_CONTAINER(hbox), loadCopyValues, &lp );
}
state->cancelled = XP_FALSE;
gtk_main_quit();
}
static void
handle_cancel( GtkWidget* widget, void* closure )
{
GtkNewGameState* state = (GtkNewGameState*)closure;
state->cancelled = XP_TRUE;
gtk_main_quit();
}
static void
handle_revert( GtkWidget* widget, void* closure )
{
GtkNewGameState* state = (GtkNewGameState*)closure;
state->revert = TRUE;
gtk_main_quit();
} /* handle_revert */
GtkWidget*
make_menu_item( gchar* name, GtkSignalFunc func, gpointer data )
{
GtkWidget* item;
item = gtk_menu_item_new_with_label( name );
gtk_signal_connect( GTK_OBJECT(item), "activate", func, data );
gtk_widget_show( item );
return item;
} /* make_menu_item */
static GtkWidget*
makeButton( char* text, GtkSignalFunc func, gpointer data )
{
GtkWidget* button = gtk_button_new_with_label( text );
gtk_signal_connect( GTK_OBJECT(button), "clicked", func, data );
gtk_widget_show( button );
return button;
} /* makeButton */
static GtkWidget*
makeNewGameDialog( GtkNewGameState* state )
{
GtkWidget* dialog;
GtkWidget* vbox;
GtkWidget* hbox;
GtkWidget* item;
GtkWidget* nPlayersMenu;
GtkWidget* boardSizeMenu;
GtkWidget* opt;
CurGameInfo* gi;
short i;
dialog = gtk_dialog_new();
gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
vbox = gtk_vbox_new( FALSE, 0 );
hbox = gtk_hbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(hbox), gtk_label_new("Number of players"),
FALSE, TRUE, 0 );
opt = gtk_option_menu_new();
nPlayersMenu = gtk_menu_new();
gi = &state->globals->cGlobals.params->gi;
state->nPlayers = gi->nPlayers;
for ( i = 0; i < MAX_NUM_PLAYERS; ++i ) {
char buf[2];
snprintf( buf, 2, "%d", i+1 );
item = make_menu_item( buf, GTK_SIGNAL_FUNC(nplayers_menu_select),
state );
gtk_menu_append( GTK_MENU(nPlayersMenu), item );
if ( i+1 == state->nPlayers ) {
gtk_menu_set_active( GTK_MENU(nPlayersMenu), i );
}
}
gtk_option_menu_set_menu( GTK_OPTION_MENU(opt), nPlayersMenu );
gtk_widget_show( opt );
gtk_box_pack_start( GTK_BOX(hbox), opt, FALSE, TRUE, 0 );
gtk_widget_show( hbox );
gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, TRUE, 0 );
for ( i = 0; i < MAX_NUM_PLAYERS; ++i ) {
GtkWidget* label;
GtkWidget* nameField = gtk_entry_new();
GtkWidget* passwdField = gtk_entry_new_with_max_length( 6 );
GtkWidget* robotCheck = gtk_check_button_new_with_label( "robot" );
GtkWidget* localCheck = gtk_check_button_new_with_label( "is local" );
hbox = gtk_hbox_new( FALSE, 0 );
state->playerEntries[i] = hbox;
label = gtk_label_new("name:");
gtk_box_pack_start( GTK_BOX(hbox), label, FALSE, TRUE, 0 );
gtk_widget_show( label );
gtk_box_pack_start( GTK_BOX(hbox), nameField, FALSE, TRUE, 0 );
gtk_widget_show( nameField );
label = gtk_label_new("passwd:");
gtk_box_pack_start( GTK_BOX(hbox), label, FALSE, TRUE, 0 );
gtk_widget_show( label );
gtk_box_pack_start( GTK_BOX(hbox), passwdField, FALSE, TRUE, 0 );
gtk_widget_show( passwdField );
gtk_box_pack_start( GTK_BOX(hbox), robotCheck, FALSE, TRUE, 0 );
gtk_widget_show( robotCheck );
gtk_box_pack_start( GTK_BOX(hbox), localCheck, FALSE, TRUE, 0 );
gtk_widget_show( localCheck );
if ( i < state->nPlayers ) {
XP_Bool isSet;
gtk_entry_set_text(
GTK_ENTRY(nameField),
gi->players[i].name );
isSet = gi->players[i].isRobot;
gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON(robotCheck),
isSet );
XP_DEBUGF( "isRobot set to %d\n", isSet );
isSet = gi->players[i].isLocal;
gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON(localCheck),
isSet );
XP_DEBUGF( "isLocal set to %d\n", isSet );
} else {
char buf[10];
snprintf( buf, sizeof(buf), "Player %d", i+1 );
gtk_entry_set_text( GTK_ENTRY(nameField), buf );
gtk_widget_set_sensitive( hbox, FALSE );
gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON(localCheck),
XP_TRUE );
}
gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, TRUE, 0 );
gtk_widget_show( hbox );
}
/* board size choices */
hbox = gtk_hbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(hbox), gtk_label_new("Board size"),
FALSE, TRUE, 0 );
opt = gtk_option_menu_new();
boardSizeMenu = gtk_menu_new();
state->nCols = gi->boardSize;
for ( i = 0; i < MAX_SIZE_CHOICES; ++i ) {
char buf[10];
XP_U16 siz = MAX_COLS - i;
snprintf( buf, sizeof(buf), "%dx%d", siz, siz );
item = make_menu_item( buf, GTK_SIGNAL_FUNC(size_menu_select),
state );
gtk_menu_append( GTK_MENU(boardSizeMenu), item );
if ( siz == state->nCols ) {
gtk_menu_set_active( GTK_MENU(boardSizeMenu), i );
}
}
gtk_option_menu_set_menu( GTK_OPTION_MENU(opt), boardSizeMenu );
gtk_widget_show( opt );
gtk_box_pack_start( GTK_BOX(hbox), opt, FALSE, TRUE, 0 );
gtk_box_pack_start( GTK_BOX(hbox), gtk_label_new("Dictionary: "),
FALSE, TRUE, 0 );
XP_ASSERT( gi->dictName );
gtk_box_pack_start( GTK_BOX(hbox),
gtk_label_new(gi->dictName),
FALSE, TRUE, 0 );
gtk_widget_show( hbox );
gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, TRUE, 0 );
/* buttons at the bottom */
hbox = gtk_hbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(hbox),
makeButton( "Ok", handle_ok, state ),
FALSE, TRUE, 0 );
gtk_box_pack_start( GTK_BOX(hbox),
makeButton( "Revert", handle_revert, state ),
FALSE, TRUE, 0 );
gtk_box_pack_start( GTK_BOX(hbox),
makeButton( "Cancel", handle_cancel, state ),
FALSE, TRUE, 0 );
gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, TRUE, 0 );
gtk_widget_show( vbox );
gtk_container_add( GTK_CONTAINER( GTK_DIALOG(dialog)->action_area), vbox);
gtk_widget_show_all (dialog);
return dialog;
} /* makeNewGameDialog */
gboolean
newGameDialog( GtkAppGlobals* globals/* , GtkGameInfo* gameInfo */ )
{
GtkNewGameState state;
state.globals = globals;
/* returns when button handler calls gtk_main_quit */
do {
GtkWidget* dialog = makeNewGameDialog( &state );
state.revert = FALSE;
gtk_main();
gtk_widget_destroy( dialog );
} while ( state.revert );
return !state.cancelled;
} /* newGameDialog */
#if 0
gint
gtkask( GtkAppGlobals* globals, gchar *message, gint numButtons,
char* button1, ... )
{
GtkWidget* dialog;
GtkWidget* label;
GtkWidget* button;
short i;
gboolean* results = g_malloc( numButtons * sizeof(results[0]) );
char** butList = &button1;
/* Create the widgets */
dialog = gtk_dialog_new();
gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
label = gtk_label_new( message );
for ( i = 0; i < numButtons; ++i ) {
button = gtk_button_new_with_label( *butList );
results[i] = 0;
gtk_signal_connect( GTK_OBJECT( button ), "clicked",
GTK_SIGNAL_FUNC(button_event), &results[i] );
gtk_container_add( GTK_CONTAINER( GTK_DIALOG(dialog)->action_area),
button );
++butList;
}
/* Add the label, and show everything we've added to the dialog. */
gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),
label);
gtk_widget_show_all (dialog);
/* returns when button handler calls gtk_main_quit */
gtk_main();
gtk_widget_destroy( dialog );
for ( i = 0; i < numButtons; ++i ) {
if ( results[i] ) {
break;
}
}
g_free( results );
return i;
} /* gtkask */
#endif
#endif /* PLATFORM_GTK */

View file

@ -0,0 +1,40 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 2000 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 PLATFORM_GTK
#ifndef _GTKNEWGAME_H_
#define _GTKNEWGAME_H_
#include "gtkmain.h"
typedef struct GtkNewGameState {
GtkAppGlobals* globals;
GtkWidget* playerEntries[MAX_NUM_PLAYERS];
gboolean revert;
gboolean cancelled;
short nPlayers;
short nCols;
} GtkNewGameState;
gboolean newGameDialog( GtkAppGlobals* globals/* , GtkGameInfo* gameInfo */ );
#endif /* _GTKNEWGAME_H_ */
#endif /* PLATFORM_GTK */

View file

@ -0,0 +1,94 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 2000 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 PLATFORM_GTK
#include "gtkpasswdask.h"
#include <gtk/gtk.h>
static void
button_event( GtkWidget* widget, void* closure )
{
XP_Bool* okptr = (XP_Bool*)closure;
*okptr = XP_TRUE;
gtk_main_quit();
} /* ok_button_event */
XP_Bool
gtkpasswdask( const char* name, char* outbuf, XP_U16* buflen )
{
XP_Bool ok = XP_FALSE;
XP_Bool ignore;
char buf[64];
short i;
GtkWidget* entry;
GtkWidget* vbox;
GtkWidget* hbox;
GtkWidget* dialog;
GtkWidget* label;
char* labels[] = { "Ok", "Cancel" };
XP_Bool* boolps[] = { &ok, &ignore };
dialog = gtk_dialog_new();
gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
snprintf( buf, sizeof(buf), "Password for player \"%s\"", name );
label = gtk_label_new( buf );
gtk_container_add( GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),
label );
/* we need a text field and two buttons as well */
vbox = gtk_vbox_new(FALSE, 0);
entry = gtk_entry_new();
gtk_widget_show( entry );
gtk_box_pack_start( GTK_BOX(vbox), entry, FALSE, TRUE, 0 );
hbox = gtk_hbox_new(FALSE, 0);
for ( i = 0; i < 2; ++i ) {
GtkWidget* button = gtk_button_new_with_label( labels[i] );
gtk_signal_connect( GTK_OBJECT(button), "clicked", button_event,
boolps[i] );
gtk_box_pack_start( GTK_BOX(hbox), button, FALSE, TRUE, 0 );
gtk_widget_show( button );
}
gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, TRUE, 0 );
gtk_container_add( GTK_CONTAINER( GTK_DIALOG(dialog)->action_area), vbox);
gtk_widget_show_all( dialog );
gtk_main();
if ( ok ) {
char* text = gtk_entry_get_text( GTK_ENTRY(entry) );
strncpy( outbuf, text, *buflen );
*buflen = strlen(outbuf);
}
gtk_widget_destroy( dialog );
return ok;
} /* gtkpasswdask */
#endif /* PLATFORM_GTK */

View file

@ -0,0 +1,31 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 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.
*/
#ifdef PLATFORM_GTK
#ifndef _GTKPASSWDASK_H_
#define _GTKPASSWDASK_H_
#include "comtypes.h"
XP_Bool gtkpasswdask( const char* name, char* buf, XP_U16* len );
#endif /* _GTKPASSWDASK_H_ */
#endif /* PLATFORM_GTK */

17
xwords4/linux/hint.xpm Normal file
View file

@ -0,0 +1,17 @@
/* XPM */
static char * hint_xpm[] = {
"8 11 3 1",
" c None",
". c #FFFFFF",
"+ c #000000",
".++++++.",
"+++..+++",
"++.++.++",
"+.++++.+",
"+.++++.+",
"++.++.++",
"++.++.++",
"+++..+++",
"+++..+++",
"+++..+++",
".++++++."};

14
xwords4/linux/juggle.xpm Normal file
View file

@ -0,0 +1,14 @@
/* XPM */
static char * juggle_xpm[] = {
"8 8 3 1",
" c None",
". c #000000",
"+ c #FFFFFF",
"........",
"...+++..",
"....+...",
"....+...",
"....+...",
"..+.+...",
"...+....",
"........"};

372
xwords4/linux/linuxdict.c Normal file
View file

@ -0,0 +1,372 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4;-*- */
/*
* Copyright 1997-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 CLIENT_ONLY /* there's an else in the middle!!! */
#include <stdio.h>
#include <stdlib.h>
/* #include <prc.h> */
#include "comtypes.h"
#include "dictnryp.h"
#include "linuxmain.h"
typedef struct DictStart {
XP_U32 numNodes;
/* XP_U32 indexStart; */
array_edge* array;
} DictStart;
typedef struct LinuxDictionaryCtxt {
DictionaryCtxt super;
/* prc_t* pt; */
/* DictStart* starts; */
/* XP_U16 numStarts; */
} LinuxDictionaryCtxt;
/************************ Prototypes ***********************/
static XP_Bool initFromDictFile( LinuxDictionaryCtxt* dctx, char* fileName );
static void linux_dictionary_destroy( DictionaryCtxt* dict );
/*****************************************************************************
*
****************************************************************************/
DictionaryCtxt*
linux_dictionary_make( MPFORMAL char* dictFileName )
{
LinuxDictionaryCtxt* result =
(LinuxDictionaryCtxt*)XP_MALLOC(mpool, sizeof(*result));
XP_MEMSET( result, 0, sizeof(*result) );
MPASSIGN(result->super.mpool, mpool);
if ( !!dictFileName ) {
XP_Bool success = initFromDictFile( result, dictFileName );
if ( success ) {
result->super.destructor = linux_dictionary_destroy;
setBlankTile( &result->super );
} else {
XP_FREE( mpool, result );
result = NULL;
}
}
return (DictionaryCtxt*)result;
} /* gtk_dictionary_make */
static XP_U16
countSpecials( LinuxDictionaryCtxt* ctxt )
{
XP_U16 result = 0;
XP_U16 i;
for ( i = 0; i < ctxt->super.nFaces; ++i ) {
if ( IS_SPECIAL(ctxt->super.faces16[i] ) ) {
++result;
}
}
return result;
} /* countSpecials */
static XP_Bitmap
skipBitmap( LinuxDictionaryCtxt* ctxt, FILE* dictF )
{
XP_U8 nCols, nRows, nBytes;
LinuxBMStruct* lbs = NULL;
(void)fread( &nCols, sizeof(nCols), 1, dictF );
if ( nCols > 0 ) {
(void)fread( &nRows, sizeof(nRows), 1, dictF );
nBytes = ((nRows * nCols) + 7) / 8;
lbs = XP_MALLOC( ctxt->super.mpool, sizeof(*lbs) + nBytes );
lbs->nRows = nRows;
lbs->nCols = nCols;
lbs->nBytes = nBytes;
(void)fread( lbs + 1, nBytes, 1, dictF );
}
return lbs;
} /* skipBitmap */
static void
skipBitmaps( LinuxDictionaryCtxt* ctxt, FILE* dictF )
{
XP_U16 nSpecials;
XP_UCHAR* text;
XP_UCHAR** texts;
SpecialBitmaps* bitmaps;
Tile tile;
nSpecials = countSpecials( ctxt );
texts = (XP_UCHAR**)XP_MALLOC( ctxt->super.mpool,
nSpecials * sizeof(*texts) );
bitmaps = (SpecialBitmaps*)XP_MALLOC( ctxt->super.mpool,
nSpecials * sizeof(*bitmaps) );
for ( tile = 0; tile < ctxt->super.nFaces; ++tile ) {
XP_CHAR16 face = ctxt->super.faces16[(short)tile];
if ( IS_SPECIAL(face) ) {
XP_U8 txtlen;
XP_ASSERT( face < nSpecials );
/* get the string */
(void)fread( &txtlen, sizeof(txtlen), 1, dictF );
text = (XP_UCHAR*)XP_MALLOC(ctxt->super.mpool, txtlen+1);
(void)fread( text, txtlen, 1, dictF );
text[txtlen] = '\0';
texts[face] = text;
XP_DEBUGF( "skipping bitmaps for %s", texts[face] );
bitmaps[face].largeBM = skipBitmap( ctxt, dictF );
bitmaps[face].smallBM = skipBitmap( ctxt, dictF );
}
}
ctxt->super.chars = texts;
ctxt->super.bitmaps = bitmaps;
} /* skipBitmaps */
static XP_Bool
initFromDictFile( LinuxDictionaryCtxt* dctx, char* fileName )
{
XP_Bool formatOk = XP_TRUE;
XP_U8 numFaces;
long curPos, dictLength;
XP_U32 topOffset;
FILE* dictF = fopen( fileName, "r" );
unsigned short xloc;
XP_U16 flags;
XP_U16 facesSize;
XP_ASSERT( dictF );
(void)fread( &flags, sizeof(flags), 1, dictF );
flags = ntohs(flags);
XP_DEBUGF( "flags=0x%x", flags );
#ifdef NODE_CAN_4
if ( flags == 0x0001 ) {
dctx->super.nodeSize = 3;
dctx->super.charSize = 1;
} else if ( flags == 0x0002 ) {
dctx->super.nodeSize = 3;
dctx->super.charSize = 2;
} else if ( flags == 0x0003 ) {
dctx->super.nodeSize = 4;
dctx->super.charSize = 2;
} else {
/* case I don't know how to deal with */
formatOk = XP_FALSE;
XP_ASSERT(0);
}
#else
XP_ASSERT( flags == 0x0001 );
#endif
if ( formatOk ) {
(void)fread( &numFaces, sizeof(numFaces), 1, dictF );
dctx->super.nFaces = numFaces;
dctx->super.countsAndValues = XP_MALLOC( dctx->super.mpool,
numFaces*2 );
facesSize = numFaces * sizeof(dctx->super.faces16[0]);
dctx->super.faces16 = XP_MALLOC( dctx->super.mpool, facesSize );
XP_MEMSET( dctx->super.faces16, 0, facesSize );
fread( dctx->super.faces16, numFaces * dctx->super.charSize,
1, dictF );
if ( dctx->super.charSize == sizeof(dctx->super.faces16[0]) ) {
/* fix endianness */
XP_U16 i;
for ( i = 0; i < numFaces; ++i ) {
XP_CHAR16 tmp = dctx->super.faces16[i];
dctx->super.faces16[i] = ntohs(tmp);
}
} else {
XP_UCHAR* src = ((XP_UCHAR*)(dctx->super.faces16)) + numFaces;
XP_CHAR16* dest = dctx->super.faces16 + numFaces;
while ( src-- <= (XP_UCHAR*)(dest--) ) {
*dest = (XP_CHAR16)*src;
}
}
fread( &xloc, 2, 1, dictF ); /* read in (dump) the xloc header for
now */
fread( dctx->super.countsAndValues, numFaces*2, 1, dictF );
skipBitmaps( dctx, dictF );
curPos = ftell( dictF );
fseek( dictF, 0L, SEEK_END );
dictLength = ftell( dictF ) - curPos;
fseek( dictF, curPos, SEEK_SET );
if ( dictLength > 0 ) {
fread( &topOffset, sizeof(topOffset), 1, dictF );
/* it's in big-endian order */
topOffset = ntohl(topOffset);
dictLength -= sizeof(topOffset); /* first four bytes are offset */
}
if ( dictLength > 0 ) {
#ifdef DEBUG
dctx->super.numEdges = dictLength / 3;
#endif
#ifdef NODE_CAN_4
XP_ASSERT( (dictLength % dctx->super.nodeSize) == 0 );
#else
XP_ASSERT( (dictLength % 3) == 0 );
#endif
dctx->super.base = (array_edge*)XP_MALLOC( dctx->super.mpool,
dictLength );
XP_ASSERT( !!dctx->super.base );
fread( dctx->super.base, dictLength, 1, dictF );
dctx->super.topEdge = dctx->super.base + topOffset;
} else {
dctx->super.base = NULL;
dctx->super.topEdge = NULL;
}
}
fclose( dictF );
return formatOk;
} /* initFromDictFile */
static void
freeSpecials( LinuxDictionaryCtxt* ctxt )
{
XP_U16 nSpecials = 0;
XP_U16 i;
for ( i = 0; i < ctxt->super.nFaces; ++i ) {
if ( IS_SPECIAL(ctxt->super.faces16[i]) ) {
if ( !!ctxt->super.bitmaps ) {
XP_Bitmap* bmp = ctxt->super.bitmaps[nSpecials].largeBM;
if ( !!bmp ) {
XP_FREE( ctxt->super.mpool, bmp );
}
bmp = ctxt->super.bitmaps[nSpecials].smallBM;
if ( !!bmp ) {
XP_FREE( ctxt->super.mpool, bmp );
}
}
if ( !!ctxt->super.chars && !!ctxt->super.chars[nSpecials]) {
XP_FREE( ctxt->super.mpool, ctxt->super.chars[nSpecials] );
}
++nSpecials;
}
}
if ( !!ctxt->super.bitmaps ) {
XP_FREE( ctxt->super.mpool, ctxt->super.bitmaps );
}
if ( !!ctxt->super.chars ) {
XP_FREE( ctxt->super.mpool, ctxt->super.chars );
}
} /* freeSpecials */
static void
linux_dictionary_destroy( DictionaryCtxt* dict )
{
LinuxDictionaryCtxt* ctxt = (LinuxDictionaryCtxt*)dict;
freeSpecials( ctxt );
if ( !!dict->topEdge ) {
XP_FREE( dict->mpool, dict->topEdge );
}
XP_FREE( dict->mpool, ctxt->super.countsAndValues );
XP_FREE( dict->mpool, ctxt->super.faces16 );
XP_FREE( dict->mpool, ctxt );
} /* linux_dictionary_destroy */
#else /* CLIENT_ONLY *IS* defined */
/* initFromDictFile:
* This guy reads in from a prc file, and probably hasn't worked in a year.
*/
#define RECS_BEFORE_DAWG 3 /* a hack */
static XP_Bool
initFromDictFile( LinuxDictionaryCtxt* dctx, char* fileName )
{
short i;
unsigned short* dataP;
unsigned nRecs;
prc_record_t* prect;
prc_t* pt = prcopen( fileName, PRC_OPEN_READ );
dctx->pt = pt; /* remember so we can close it later */
nRecs = prcgetnrecords( pt );
/* record 0 holds a struct whose 5th byte is the record num of the first
dawg record. 1 and 2 hold tile data. Let's assume 3 is the first dawg
record for now. */
prect = prcgetrecord( pt, 1 );
dctx->super.numFaces = prect->datalen; /* one char per byte */
dctx->super.faces = malloc( prect->datalen );
memcpy( dctx->super.faces, prect->data, prect->datalen );
dctx->super.counts = malloc( dctx->super.numFaces );
dctx->super.values = malloc( dctx->super.numFaces );
prect = prcgetrecord( pt, 2 );
dataP = (unsigned short*)prect->data + 1; /* skip the xloc header */
for ( i = 0; i < dctx->super.numFaces; ++i ) {
unsigned short byt = *dataP++;
dctx->super.values[i] = byt >> 8;
dctx->super.counts[i] = byt & 0xFF;
if ( dctx->super.values[i] == 0 ) {
dctx->super.counts[i] = 4; /* 4 blanks :-) */
}
}
dctx->numStarts = nRecs - RECS_BEFORE_DAWG;
dctx->starts = XP_MALLOC( dctx->numStarts * sizeof(*dctx->starts) );
for ( i = 0/* , offset = 0 */; i < dctx->numStarts; ++i ) {
prect = prcgetrecord( pt, i + RECS_BEFORE_DAWG );
dctx->starts[i].numNodes = prect->datalen / 3;
dctx->starts[i].array = (array_edge*)prect->data;
XP_ASSERT( (prect->datalen % 3) == 0 );
}
} /* initFromDictFile */
void
linux_dictionary_destroy( DictionaryCtxt* dict )
{
LinuxDictionaryCtxt* ctxt = (LinuxDictionaryCtxt*)dict;
prcclose( ctxt->pt );
}
#endif /* CLIENT_ONLY */

762
xwords4/linux/linuxmain.c Normal file
View file

@ -0,0 +1,762 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 2000 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 <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <netdb.h> /* gethostbyname */
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <time.h>
#include "linuxmain.h"
#include "main.h"
#ifdef PLATFORM_NCURSES
# include "cursesmain.h"
#endif
#ifdef PLATFORM_GTK
# include "gtkmain.h"
#endif
#include "model.h"
#include "util.h"
#include "strutils.h"
/* #include "commgr.h" */
/* #include "compipe.h" */
#include "memstream.h"
#include "LocalizedStrIncludes.h"
#define DEFAULT_SEND_PORT 4999
#define DEFAULT_LISTEN_PORT 4998
#ifdef DEBUG
void
linux_debugf( char* format, ... )
{
char buf[1000];
va_list ap;
// time_t tim;
struct tm* timp;
struct timeval tv;
struct timezone tz;
gettimeofday( &tv, &tz );
timp = localtime( &tv.tv_sec );
sprintf( buf, "%d:%d:%d: ", timp->tm_hour, timp->tm_min, timp->tm_sec );
va_start(ap, format);
vsprintf(buf+strlen(buf), format, ap);
va_end(ap);
fprintf( stderr, buf );
fprintf( stderr, "\n" );
}
#endif
void
catOnClose( XWStreamCtxt* stream, void* closure )
{
XP_U16 nBytes;
char* buffer;
XP_LOGF( "catOnClose" );
nBytes = stream_getSize( stream );
buffer = malloc( nBytes + 1 );
stream_getBytes( stream, buffer, nBytes );
buffer[nBytes] = '\0';
fprintf( stderr, buffer );
free( buffer );
} /* catOnClose */
void
catGameHistory( CommonGlobals* cGlobals )
{
if ( !!cGlobals->game.model ) {
XP_Bool gameOver = server_getGameIsOver( cGlobals->game.server );
XWStreamCtxt* stream =
mem_stream_make( MPPARM(cGlobals->params->util->mpool)
cGlobals->params->vtMgr,
NULL, CHANNEL_NONE, catOnClose );
model_writeGameHistory( cGlobals->game.model, stream,
cGlobals->game.server, gameOver );
stream_putU8( stream, '\n' );
stream_destroy( stream );
}
} /* catGameHistory */
XP_UCHAR*
strFromStream( XWStreamCtxt* stream )
{
XP_U16 len = stream_getSize( stream );
XP_UCHAR* buf = (XP_UCHAR*)malloc( len + 1 );
stream_getBytes( stream, buf, len );
buf[len] = '\0';
return buf;
} /* strFromStream */
XP_UCHAR*
linux_getErrString( UtilErrID id )
{
char* message = NULL;
switch( id ) {
case ERR_TILES_NOT_IN_LINE:
message = "All tiles played must be in a line.";
break;
case ERR_NO_EMPTIES_IN_TURN:
message = "Empty squares cannot separate tiles played.";
break;
case ERR_TWO_TILES_FIRST_MOVE:
message = "Must play two or more pieces on the first move.";
break;
case ERR_TILES_MUST_CONTACT:
message = "New pieces must contact others already in place (or "
"the middle square on the first move).";
break;
case ERR_NOT_YOUR_TURN:
message = "You can't do that; it's not your turn!";
break;
case ERR_NO_PEEK_ROBOT_TILES:
message = "No peeking at the robot's tiles!";
break;
case ERR_NO_PEEK_REMOTE_TILES:
message = "No peeking at remote players' tiles!";
break;
case ERR_REG_UNEXPECTED_USER:
message = "Refused attempt to register unexpected user[s].";
break;
case ERR_SERVER_DICT_WINS:
message = "Conflict between Host and Guest dictionaries; Host wins.";
XP_WARNF( "GTK may have problems here." );
break;
default:
message = "<unrecognized error code reported>";
}
return message;
} /* linux_getErrString */
static void
usage( char* appName, char* msg )
{
if ( msg != NULL ) {
fprintf( stderr, "Error: %s\n\n", msg );
}
fprintf( stderr, "usage: %s \n"
#if defined PLATFORM_GTK && defined PLATFORM_NCURSES
"\t [-g] # gtk (default)\n"
"\t [-u] # ncurses (for dumb terminal)\n"
#endif
#if defined PLATFORM_GTK
"\t [-o] # tray overlaps board (like small screen)\n"
"\t [-k] # ask for parameters via \"new games\" dlg\n"
#endif
"\t [-f file] # use this file to save/load game\n"
"\t [-q] # quit when game over (useful for robot-only)\n"
"\t [-S] # slow robot down \n"
"\t [-i] # print game history when game over\n"
"\t [-U] # call 'Undo' after game ends\n"
"\t [-r name]* # same-process robot\n"
"\t [-n name]* # same-process player (no network used)\n"
"\t [-w pwd]* # passwd for matching local player\n"
"\t [-v] # put scoreboard in vertical mode\n"
"\t [-m] # make the robot duMb (smart is default)\n"
"\t [-c] # explain robot scores after each move\n"
"\t\t # (max of four players total, local and remote)\n"
"\t [-b boardSize] # number of columns and rows\n"
"\t [-l listen_port] # port for inet connection (4999 default)\n"
"\t [-e random_seed] \n"
"\t [-t initial_minutes_on_timer] \n"
"# --------------- choose client or server ----------\n"
"\t -s # be the server\n"
"\t -d xwd_file # provides tile counts & values\n"
"\t\t # list each player as local or remote\n"
"\t [-N]* # remote client (listen for connection)\n"
"# --------------- OR client-only ----------\n"
"\t [-a server_addr] # be a client (on port spec'd above)\n"
"\t [-p client_port] # must != server's port if on same device"
"" " (default localhost)\n"
"\nexample: \n"
"\tserver: ./xwords -d dict.xwd -s -r Eric -N\n"
"\tclient: ./xwords -d dict.xwd -r Kati -p 4999 -l 6000\n"
, appName );
exit(1);
}
XP_S16
linux_udp_send( XP_U8* buf, XP_U16 buflen, CommsAddrRec* addrRec,
void* closure )
{
CommonGlobals* globals = (CommonGlobals*)closure;
struct sockaddr_in to_sock;
struct hostent* host;
XP_S16 result;
int sock;
/* XP_U8* msg; */
XP_LOGF( "linux_udp_send" );
/* make a local copy of the address to send to */
sock = socket( AF_INET, SOCK_DGRAM, 0 );
if ( sock == -1 ) {
XP_DEBUGF( "socket returned -1\n" );
return -1;
}
if ( !!addrRec ) {
XP_ASSERT( addrRec->conType == COMMS_CONN_IP );
XP_MEMSET( &to_sock, 0, sizeof(to_sock) );
XP_STATUSF( "target IP = 0x%lx", addrRec->u.ip.ipAddr );
to_sock.sin_addr.s_addr = htonl(addrRec->u.ip.ipAddr);
to_sock.sin_port = htons(addrRec->u.ip.port);
XP_STATUSF( "0: sending to port %d(0x%x)",
addrRec->u.ip.port, addrRec->u.ip.port );
} else {
to_sock.sin_port = htons(globals->params->defaultSendPort);
XP_STATUSF( "1: sending to port %d",
globals->params->defaultSendPort );
if (( host = gethostbyname(globals->defaultServerName) ) == NULL ) {
XP_WARNF( "gethostbyname returned -1\n" );
return -1;
} else {
XP_WARNF( "gethostbyname for %s worked",
globals->defaultServerName );
}
memcpy( &(to_sock.sin_addr.s_addr), host->h_addr_list[0],
sizeof(struct in_addr));
}
to_sock.sin_family = AF_INET;
/* msg = malloc( buflen ); */
/* XP_MEMCPY( msg, buf, buflen ); */
errno = 0;
XP_STATUSF( "checking: errno=%d", (short)errno );
result = sendto( sock, buf, buflen, 0,
(struct sockaddr *)&to_sock, sizeof(to_sock) );
XP_STATUSF( "foo: sendto returned %d of %d (err=%d)",
result, buflen, errno );
close(sock);
/* free( msg ); */
return result;
} /* linux_udp_send */
int
initListenerSocket( int port )
{
int newSocket;
int result;
struct sockaddr_in sockAddr;
XP_DEBUGF( "opening socket to listen on port %d", port );
newSocket = socket( AF_INET, DGRAM_TYPE, 0 );
XP_DEBUGF( "socket returned %d", newSocket );
sockAddr.sin_family = AF_INET;
sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
sockAddr.sin_port = htons(port);
result = bind( newSocket, (struct sockaddr*)&sockAddr, sizeof(sockAddr) );
XP_LOGF( "bind on port %d returned %d; errno=%d",
port, result, errno );
/* result = listen( newSocket, 5 ); */
/* XP_DEBUGF( "listen returned %d; errno=%d\n", result, errno ); */
return newSocket;
} /* initListenerSocket */
/* Create a stream for the incomming message buffer, and read in any
information specific to our platform's comms layer (return address, say)
*/
XWStreamCtxt*
stream_from_msgbuf( CommonGlobals* globals, char* bufPtr, XP_U16 nBytes )
{
XWStreamCtxt* result;
result = mem_stream_make( MPPARM(globals->params->util->mpool)
globals->params->vtMgr,
globals, CHANNEL_NONE, NULL );
stream_putBytes( result, bufPtr, nBytes );
return result;
} /* stream_from_msgbuf */
#if 0
static void
streamClosed( XWStreamCtxt* stream, XP_PlayerAddr addr, void* closure )
{
fprintf( stderr, "streamClosed called\n" );
} /* streamClosed */
static XWStreamCtxt*
linux_util_makeStreamFromAddr( XW_UtilCtxt* uctx, XP_U16 channelNo )
{
#if 1
/* XWStreamCtxt* stream = linux_mem_stream_make( uctx->closure, channelNo, */
/* sendOnClose, NULL ); */
#else
struct sockaddr* returnAddr = (struct sockaddr*)addr;
int newSocket;
int result;
newSocket = socket( AF_INET, DGRAM_TYPE, 0 );
fprintf( stderr, "linux_util_makeStreamFromAddr: made socket %d\n",
newSocket );
/* #define EADDRINUSE 98 */
result = bind( newSocket, (struct sockaddr*)returnAddr, addrLen );
fprintf( stderr, "bind returned %d; errno=%d\n", result, errno );
return linux_make_socketStream( newSocket );
#endif
} /* linux_util_makeStreamFromAddr */
#endif
static DictionaryCtxt*
linux_util_makeEmptyDict( XW_UtilCtxt* uctx )
{
XP_DEBUGF( "linux_util_makeEmptyDict called\n" );
return linux_dictionary_make( MPPARM(uctx->mpool) NULL );
} /* linux_util_makeEmptyDict */
#define EM BONUS_NONE
#define DL BONUS_DOUBLE_LETTER
#define DW BONUS_DOUBLE_WORD
#define TL BONUS_TRIPLE_LETTER
#define TW BONUS_TRIPLE_WORD
static XWBonusType
linux_util_getSquareBonus( XW_UtilCtxt* uc, ModelCtxt* model,
XP_U16 col, XP_U16 row )
{
XP_U16 index;
/* This must be static or won't compile under multilink (for Palm).
Fix! */
static char scrabbleBoard[8*8] = {
TW,EM,EM,DL,EM,EM,EM,TW,
EM,DW,EM,EM,EM,TL,EM,EM,
EM,EM,DW,EM,EM,EM,DL,EM,
DL,EM,EM,DW,EM,EM,EM,DL,
EM,EM,EM,EM,DW,EM,EM,EM,
EM,TL,EM,EM,EM,TL,EM,EM,
EM,EM,DL,EM,EM,EM,DL,EM,
TW,EM,EM,DL,EM,EM,EM,DW,
}; /* scrabbleBoard */
if ( col > 7 ) col = 14 - col;
if ( row > 7 ) row = 14 - row;
index = (row*8) + col;
if ( index >= 8*8 ) {
return (XWBonusType)EM;
} else {
return (XWBonusType)scrabbleBoard[index];
}
} /* linux_util_getSquareBonus */
static XP_U32
linux_util_getCurSeconds( XW_UtilCtxt* uc )
{
return (XP_U32)time(NULL);//tv.tv_sec;
} /* gtk_util_getCurSeconds */
static XP_UCHAR*
linux_util_getUserString( XW_UtilCtxt* uc, XP_U16 code )
{
switch( code ) {
case STRD_REMAINING_TILES_ADD:
return "+ %d [all remaining tiles]";
case STRD_UNUSED_TILES_SUB:
return "- %d [unused tiles]";
case STR_COMMIT_CONFIRM:
return "Are you sure you want to commit the current move?\n";
case STRD_TURN_SCORE:
return "Score for turn: %d\n";
case STR_BONUS_ALL:
return "Bonus for using all tiles: 50\n";
case STR_NONLOCAL_NAME:
return "%s (remote)";
case STRD_TIME_PENALTY_SUB:
return " - %d [time]";
/* added.... */
case STRD_CUMULATIVE_SCORE:
return "Cumulative score: %d\n";
case STRS_TRAY_AT_START:
return "Tray at start: %s\n";
case STRS_MOVE_DOWN:
return "move (from %s down)\n";
case STRS_MOVE_ACROSS:
return "move (from %s across)\n";
case STRS_NEW_TILES:
return "New tiles: %s\n";
case STRSS_TRADED_FOR:
return "Traded %s for %s.";
case STR_PASS:
return "pass\n";
case STR_PHONY_REJECTED:
return "Illegal word in move; turn lost!\n";
case STRD_ROBOT_TRADED:
return "%d tiles traded this turn.";
case STR_ROBOT_MOVED:
return "The robot moved:\n";
case STR_REMOTE_MOVED:
return "Remote player moved:\n";
default:
return "unknown code to linux_util_getUserString";
}
} /* linux_util_getUserString */
static unsigned int
defaultRandomSeed()
{
/* use kernel device rather than time() so can run multiple times/second
without getting the same results. */
unsigned int rs;
FILE* rfile = fopen( "/dev/urandom", "ro" );
fread( &rs, sizeof(rs), 1, rfile );
fclose( rfile );
return rs;
} /* defaultRandomSeed */
int
main( int argc, char** argv )
{
XP_Bool useGTK, useCurses;
int opt;
int totalPlayerCount = 0;
XP_Bool isServer = XP_FALSE;
char* listenPortNumString = NULL;
char* sendPortNumString = NULL;
char* serverName = "localhost";
unsigned int seed = defaultRandomSeed();
LaunchParams mainParams;
XP_U16 robotCount = 0;
#ifdef DEBUG
{
int i;
for ( i = 0; i < argc; ++i ) {
XP_LOGF( argv[i] );
}
}
#endif
memset( &mainParams, 0, sizeof(mainParams) );
mainParams.util = malloc( sizeof(*mainParams.util) );
XP_MEMSET( mainParams.util, 0, sizeof(*mainParams.util) );
#ifdef MEM_DEBUG
mainParams.util->mpool = mpool_make();
#endif
mainParams.vtMgr = make_vtablemgr(MPPARM_NOCOMMA(mainParams.util->mpool));
/* fprintf( stdout, "press <RET> to start\n" ); */
/* (void)fgetc( stdin ); */
/* defaults */
mainParams.defaultListenPort = DEFAULT_LISTEN_PORT;
mainParams.defaultSendPort = DEFAULT_SEND_PORT;
mainParams.trayOverlaps = XP_FALSE;
mainParams.gi.boardSize = 15;
mainParams.quitAfter = XP_FALSE;
mainParams.sleepOnAnchor = XP_FALSE;
mainParams.printHistory = XP_FALSE;
mainParams.undoWhenDone = XP_FALSE;
mainParams.gi.timerEnabled = XP_FALSE;
mainParams.gi.robotSmartness = SMART_ROBOT;
/* serverName = mainParams.info.clientInfo.serverName = "localhost"; */
#if defined PLATFORM_GTK
useGTK = 1;
useCurses = 0;
#else /* curses is the default if GTK isn't available */
useGTK = 0;
useCurses = 1;
#endif
do {
short index;
opt = getopt( argc, argv, "?"
#if defined PLATFORM_GTK && defined PLATFORM_NCURSES
"gu"
#endif
#if defined PLATFORM_GTK
"o"
#endif
"kKf:l:n:Nsd:a:p:e:r:b:qw:Sit:Umvc" );
switch( opt ) {
case 'h':
case '?':
usage(argv[0], NULL);
break;
case 'c':
mainParams.showRobotScores = XP_TRUE;
break;
case 'd':
mainParams.gi.dictName = copyString( MPPARM(mainParams.util->mpool)
optarg );
break;
case 'e':
seed = atoi(optarg);
break;
case 'f':
mainParams.fileName = optarg;
break;
case 'i':
mainParams.printHistory = 1;
break;
case 'K':
mainParams.skipWarnings = 1;
break;
case 'w':
mainParams.gi.players[mainParams.nLocalPlayers-1].password
= optarg;
break;
case 'm': /* dumb robot */
mainParams.gi.robotSmartness = DUMB_ROBOT;
break;
case 'n':
index = mainParams.gi.nPlayers++;
++mainParams.nLocalPlayers;
mainParams.gi.players[index].isRobot = XP_FALSE;
mainParams.gi.players[index].isLocal = XP_TRUE;
mainParams.gi.players[index].name =
copyString(MPPARM(mainParams.util->mpool) optarg);
break;
case 'N':
index = mainParams.gi.nPlayers++;
mainParams.gi.players[index].isLocal = XP_FALSE;
++mainParams.info.serverInfo.nRemotePlayers;
break;
case 'l':
listenPortNumString = optarg;
break;
case 'p':
sendPortNumString = optarg;
break;
case 'r':
++robotCount;
index = mainParams.gi.nPlayers++;
++mainParams.nLocalPlayers;
mainParams.gi.players[index].isRobot = XP_TRUE;
mainParams.gi.players[index].isLocal = XP_TRUE;
mainParams.gi.players[index].name =
copyString(MPPARM(mainParams.util->mpool) optarg);
break;
case 's':
isServer = XP_TRUE;
break;
case 'S':
mainParams.sleepOnAnchor = XP_TRUE;
break;
case 't':
mainParams.gi.gameSeconds = atoi(optarg) * 60;
mainParams.gi.timerEnabled = XP_TRUE;
break;
case 'U':
mainParams.undoWhenDone = XP_TRUE;
break;
case 'a':
/* mainParams.info.clientInfo.serverName = */
serverName = optarg;
break;
case 'q':
mainParams.quitAfter = XP_TRUE;
break;
case 'b':
mainParams.gi.boardSize = atoi(optarg);
break;
case 'v':
mainParams.verticalScore = XP_TRUE;
break;
#if defined PLATFORM_GTK && defined PLATFORM_NCURSES
case 'g':
useGTK = 1;
break;
case 'u':
useCurses = 1;
useGTK = 0;
break;
#endif
#if defined PLATFORM_GTK
case 'o':
mainParams.trayOverlaps = XP_TRUE;
break;
case 'k':
mainParams.askNewGame = XP_TRUE;
break;
#endif
}
} while ( opt != -1 );
XP_ASSERT( mainParams.gi.nPlayers == mainParams.nLocalPlayers
+ mainParams.info.serverInfo.nRemotePlayers );
if ( isServer ) {
if ( mainParams.info.serverInfo.nRemotePlayers == 0 ) {
mainParams.gi.serverRole = SERVER_STANDALONE;
} else {
mainParams.gi.serverRole = SERVER_ISSERVER;
}
} else {
mainParams.gi.serverRole = SERVER_ISCLIENT;
}
/* convert strings to whatever */
if ( listenPortNumString ) {
mainParams.defaultListenPort = atoi( listenPortNumString );
}
if ( sendPortNumString != NULL ) {
mainParams.defaultSendPort = atoi( sendPortNumString );
}
/* sanity checks */
totalPlayerCount = mainParams.nLocalPlayers
+ mainParams.info.serverInfo.nRemotePlayers;
if ( !mainParams.fileName ) {
if ( (totalPlayerCount < 1) ||
(totalPlayerCount > MAX_NUM_PLAYERS) ) {
usage( argv[0], "Need between 1 and 4 players" );
}
}
if ( !!mainParams.gi.dictName ) {
mainParams.dict = linux_dictionary_make(
MPPARM(mainParams.util->mpool) mainParams.gi.dictName );
XP_ASSERT( !!mainParams.dict );
} else if ( isServer ) {
#ifdef STUBBED_DICT
mainParams.dict = make_stubbed_dict(
MPPARM_NOCOMMA(mainParams.util->mpool) );
XP_WARNF( "no dictionary provided: using English stub dict\n" );
#else
usage( argv[0], "Server needs a dictionary" );
#endif
} else if ( robotCount > 0 ) {
usage( argv[0], "Client can't have robots without a dictionary" );
}
if ( !isServer ) {
if ( mainParams.info.serverInfo.nRemotePlayers > 0 ) {
usage( argv[0], "Client can't have remote players" );
}
}
/* setup sockets and any other stuff not specific to GTK or ncurses */
if ( isServer ) {
} else {
/* struct hostent* hostinfo; */
/* hostinfo = gethostbyname( serverName ); */
/* if ( !hostinfo ) { */
/* fprintf( stderr, "unable to get host info for %s\n", serverName ); */
/* exit( 0 ); */
/* } else { */
/* char* hostName = inet_ntoa( *(struct in_addr*)hostinfo->h_addr ); */
/* unsigned long int serverAddr; */
/* fprintf( stderr, "gethostbyname returned %s\n", hostName ); */
/* serverAddr = inet_addr(hostName); */
/* fprintf( stderr, "inet_addr returned %lu\n", serverAddr ); */
/* mainParams.info.clientInfo.stream = */
/* linux_make_outbound_socketStream( serverAddr, serverPort ); */
/* if ( !stream_open( mainParams.info.clientInfo.stream ) { */
/* fprintf( stderr, "ERROR: unable to connect to server\n" ); */
/* exit(0); */
/* } */
/* } */
mainParams.info.clientInfo.serverName = serverName;
}
/* mainParams.pipe = linuxCommPipeCtxtMake( isServer ); */
mainParams.util->vtable = malloc( sizeof(UtilVtable) );
/* mainParams.util->vtable->m_util_makeStreamFromAddr = */
/* linux_util_makeStreamFromAddr; */
mainParams.util->gameInfo = &mainParams.gi;
mainParams.util->vtable->m_util_makeEmptyDict =
linux_util_makeEmptyDict;
mainParams.util->vtable->m_util_getSquareBonus =
linux_util_getSquareBonus;
mainParams.util->vtable->m_util_getCurSeconds =
linux_util_getCurSeconds;
mainParams.util->vtable->m_util_getUserString =
linux_util_getUserString;
srandom( seed ); /* init linux random number generator */
XP_LOGF( "seeded srandom with %d", seed );
if ( isServer ) {
if ( mainParams.info.serverInfo.nRemotePlayers == 0 ) {
mainParams.serverRole = SERVER_STANDALONE;
} else {
mainParams.serverRole = SERVER_ISSERVER;
}
} else {
mainParams.serverRole = SERVER_ISCLIENT;
}
if ( mainParams.nLocalPlayers > 0 || !!mainParams.fileName) {
if ( useCurses ) {
#if defined PLATFORM_NCURSES
cursesmain( isServer, &mainParams );
#endif
} else {
#if defined PLATFORM_GTK
gtkmain( isServer, &mainParams, argc, argv );
#endif
}
} else {
/* run server as faceless process? */
}
dict_destroy( mainParams.dict );
return 0;
} /* main */

50
xwords4/linux/linuxmain.h Normal file
View file

@ -0,0 +1,50 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; compile-command: "make -k";-*- */
/*
* Copyright 1997-2000 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 _LINUXMAIN_H_
#define _LINUXMAIN_H_
#include "main.h"
#include "dictnry.h"
#include "mempool.h"
#include "comms.h"
#include "memstream.h"
/* #include "compipe.h" */
extern int errno;
typedef struct LinuxBMStruct {
XP_U8 nCols;
XP_U8 nRows;
XP_U8 nBytes;
} LinuxBMStruct;
DictionaryCtxt* linux_dictionary_make( MPFORMAL char* dictFileName );
int initListenerSocket( int port );
XP_S16 linux_udp_send( XP_U8* buf, XP_U16 buflen, CommsAddrRec* addrRec,
void* closure );
XWStreamCtxt* stream_from_msgbuf( CommonGlobals* globals, char* bufPtr,
XP_U16 nBytes );
XP_UCHAR* linux_getErrString( UtilErrID id );
XP_UCHAR* strFromStream( XWStreamCtxt* stream );
void catGameHistory( CommonGlobals* cGlobals );
void catOnClose( XWStreamCtxt* stream, void* closure );
#endif

View file

@ -0,0 +1,34 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 2000 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 "linuxserver.h"
ServerCtxt*
linux_make_server( char* dictName, XP_U16 totalPlayerCount )
{
return NULL;
} /* linux_make_server */
ServerCtxt*
linux_make_serverProxy( char* serverAddrStr, XP_U16 totalPlayerCount )
{
return NULL;
} /* linux_make_serverProxy */

View file

@ -0,0 +1,31 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 2000 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 _LINUXSERVER_H_
#define _LINUXSERVER_H_
#include "server.h"
ServerCtxt* linux_make_server( char* dictName, XP_U16 totalPlayerCount );
ServerCtxt* linux_make_serverProxy( char* serverAddrStr,
XP_U16 totalPlayerCount );
#endif

81
xwords4/linux/main.h Normal file
View file

@ -0,0 +1,81 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 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 _MAIN_H_
#define _MAIN_H_
#include "comtypes.h"
#include "util.h"
#include "game.h"
#include "vtabmgr.h"
typedef struct ServerInfo {
XP_U16 nRemotePlayers;
/* CommPipeCtxt* pipe; */
} ServerInfo;
typedef struct ClientInfo {
char* serverName; /* still need this? */
} ClientInfo;
typedef struct LinuxUtilCtxt {
UtilVtable* vtable;
} LinuxUtilCtxt;
typedef struct LaunchParams {
/* CommPipeCtxt* pipe; */
XW_UtilCtxt* util;
DictionaryCtxt* dict;
CurGameInfo gi;
char* fileName;
VTableMgr* vtMgr;
XP_U16 nLocalPlayers;
XP_Bool trayOverlaps; /* probably only interesting for GTK case */
XP_Bool askNewGame;
XP_Bool quitAfter;
XP_Bool sleepOnAnchor;
XP_Bool printHistory;
XP_Bool undoWhenDone;
XP_Bool verticalScore;
// XP_Bool mainParams;
XP_Bool skipWarnings;
XP_Bool showRobotScores;
Connectedness serverRole;
union {
ServerInfo serverInfo;
ClientInfo clientInfo;
} info;
short defaultSendPort;
short defaultListenPort;
} LaunchParams;
typedef struct CommonGlobals {
LaunchParams* params;
XWGame game;
/* UDP comms stuff */
char* defaultServerName;
} CommonGlobals;
#endif

View file

@ -0,0 +1,15 @@
#!/bin/bash
# I use this thing this way: playnum.sh 10 2>&1 | ./wordlens.pl
NUM=$1
echo "NUM=$NUM"
while :; do
../linux/xwords -u -s -r Eric -d ../linux/dicts/OSPD2to15.xwd -q -i
NUM=$(( NUM - 1 ));
if (( $NUM <= 0 )); then exit 0; fi
done

View file

@ -0,0 +1,23 @@
#!/usr/bin/perl
use strict;
my @counts;
my $nGames = 0;
while ( <> ) {
chomp;
if ( m|^([A-Z]+) \[| ) {
my $len = length($1);
++$counts[$len];
} elsif ( m|^1:1| ) {
++$nGames;
}
}
print "****** out of $nGames games: *****\n";
print "length num played\n";
for ( my $i = 2; $i <= 15; ++$i ) {
printf( "%3d %8d\n", $i, $counts[$i] );
}

18
xwords4/linux/value.xpm Normal file
View file

@ -0,0 +1,18 @@
/* XPM */
static char * value_xpm[] = {
"8 10 5 1",
" c None",
". c #0F0F0F",
"+ c #2F2F2F",
"@ c #000000",
"# c #FFFFFF",
".+@@@@@@",
"+##@@@#@",
"@.#@@#@@",
"@###@#@@",
"@@@@#@@@",
"@@@#@#@@",
"@@#@#@#@",
"@@#@###@",
"@#@@#@#@",
"@@@@@@@@"};

113
xwords4/linux/xptypes.h Normal file
View file

@ -0,0 +1,113 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 1997 - 2000 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 _XPTYPES_H_
#define _XPTYPES_H_
#include <stdlib.h>
#include <stdio.h>
#include <string.h> /* memset */
#include <assert.h> /* memset */
#include <unistd.h>
#include <netinet/in.h>
#ifdef PLATFORM_GTK
# include <glib.h>
# include <gdk/gdk.h>
# include <gtk/gtk.h>
#endif
#define XP_TRUE ((XP_Bool)(1==1))
#define XP_FALSE ((XP_Bool)(1==0))
typedef unsigned char XP_U8;
typedef signed char XP_S8;
typedef unsigned char XP_UCHAR;
typedef unsigned short XP_U16;
typedef signed short XP_S16;
typedef unsigned long XP_U32;
typedef signed long XP_S32;
typedef signed short XP_FontCode; /* not sure how I'm using this yet */
typedef unsigned char XP_Bool;
#ifdef PLATFORM_GTK
typedef guint32 XP_Time;
#else
typedef unsigned long XP_Time;
#endif
#define XP_CR "\n"
#define XP_STATUSF XP_DEBUGF
#define XP_LOGF XP_DEBUGF
#ifdef DEBUG
extern void linux_debugf(char*, ...);
#define XP_DEBUGF linux_debugf
#else
extern void p_ignore(char*, ...);
#define XP_DEBUGF if(0)p_ignore
#endif
#define XP_WARNF XP_DEBUGF
#ifdef MEM_DEBUG
# define XP_PLATMALLOC(nbytes) malloc(nbytes)
# define XP_PLATREALLOC(p,s) realloc((p),(s))
# define XP_PLATFREE(p) free(p)
#else
# define XP_MALLOC(pool,nbytes) malloc(nbytes)
# define XP_REALLOC(pool,p,s) realloc((p),(s))
# define XP_FREE(pool,p) free(p)
#endif
#define XP_MEMSET(src, val, nbytes) memset( (src), (val), (nbytes) )
#define XP_MEMCPY(d,s,l) memcpy((d),(s),(l))
#define XP_MEMCMP( a1, a2, l ) memcmp((a1),(a2),(l))
#define XP_STRLEN(s) strlen(s)
#define XP_STRNCMP(s1,s2,len) strncmp((s1),(s2),(len))
#define XP_STRCMP(s1,s2) strcmp((s1),(s2))
#define XP_RANDOM() random()
#define XP_SNPRINTF snprintf
#define XP_MIN(a,b) ((a)<(b)?(a):(b))
#define XP_MAX(a,b) ((a)>(b)?(a):(b))
#ifdef DEBUG
# define XP_ASSERT(b) assert(b)
#else
# define XP_ASSERT(b)
#endif
#define DGRAM_TYPE SOCK_DGRAM
/* #define DGRAM_TYPE SOCK_STREAM */
#define XP_NTOHL(l) ntohl(l)
#define XP_NTOHS(s) ntohs(s)
#define XP_HTONL(l) htonl(l)
#define XP_HTONS(s) htons(s)
#endif