mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-18 22:26:30 +01:00
first checkin
This commit is contained in:
parent
c505d9a0f7
commit
5776da0b93
80 changed files with 22330 additions and 0 deletions
5
xwords4/common/Makefile
Normal file
5
xwords4/common/Makefile
Normal file
|
@ -0,0 +1,5 @@
|
|||
# -*- mode: Makefile; -*-
|
||||
|
||||
clean:
|
||||
rm -rf $(PLATFORM)/*.o
|
||||
|
2625
xwords4/common/board.c
Normal file
2625
xwords4/common/board.c
Normal file
File diff suppressed because it is too large
Load diff
163
xwords4/common/board.h
Normal file
163
xwords4/common/board.h
Normal 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
191
xwords4/common/boardp.h
Normal 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
54
xwords4/common/commmgr.h
Normal 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
719
xwords4/common/comms.c
Normal 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
93
xwords4/common/comms.h
Normal 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
111
xwords4/common/comtypes.h
Normal 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
70
xwords4/common/config.mk
Normal 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
75
xwords4/common/dawg.h
Normal 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
482
xwords4/common/dictnry.c
Normal 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
154
xwords4/common/dictnry.h
Normal 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
36
xwords4/common/dictnryp.h
Normal 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
370
xwords4/common/draw.c
Normal 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
242
xwords4/common/draw.h
Normal 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
1105
xwords4/common/engine.c
Normal file
File diff suppressed because it is too large
Load diff
61
xwords4/common/engine.h
Normal file
61
xwords4/common/engine.h
Normal 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
425
xwords4/common/game.c
Normal 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
99
xwords4/common/game.h
Normal 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
244
xwords4/common/mempool.c
Normal 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
52
xwords4/common/mempool.h
Normal 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
416
xwords4/common/memstream.c
Normal 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
|
46
xwords4/common/memstream.h
Normal file
46
xwords4/common/memstream.h
Normal 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
1488
xwords4/common/model.c
Normal file
File diff suppressed because it is too large
Load diff
238
xwords4/common/model.h
Normal file
238
xwords4/common/model.h
Normal 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
78
xwords4/common/modelp.h
Normal 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
351
xwords4/common/movestak.c
Normal 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
94
xwords4/common/movestak.h
Normal 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
828
xwords4/common/mscore.c
Normal 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
233
xwords4/common/pool.c
Normal 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
43
xwords4/common/pool.h
Normal 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
31
xwords4/common/rules.mk
Normal 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
2270
xwords4/common/server.c
Normal file
File diff suppressed because it is too large
Load diff
124
xwords4/common/server.h
Normal file
124
xwords4/common/server.h
Normal 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
61
xwords4/common/states.h
Normal 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
165
xwords4/common/strutils.c
Normal 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
52
xwords4/common/strutils.h
Normal 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
659
xwords4/common/tray.c
Normal 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, ÷r );
|
||||
onDivider = rectContainsPt( ÷r, 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, ÷r );
|
||||
draw_drawTrayDivider( board->draw, ÷r,
|
||||
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
199
xwords4/common/util.h
Normal 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
33
xwords4/common/virtuals.h
Normal 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
75
xwords4/common/vtabmgr.c
Normal 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
50
xwords4/common/vtabmgr.h
Normal 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
53
xwords4/common/xwproto.h
Normal 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
29
xwords4/common/xwstate.h
Normal 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
136
xwords4/common/xwstream.h
Normal 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_ */
|
51
xwords4/linux/LocalizedStrIncludes.h
Normal file
51
xwords4/linux/LocalizedStrIncludes.h
Normal 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
120
xwords4/linux/Makefile
Normal 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
52
xwords4/linux/README.txt
Normal 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
189
xwords4/linux/cursesask.c
Normal 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
29
xwords4/linux/cursesask.h
Normal 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
500
xwords4/linux/cursesdraw.c
Normal 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
950
xwords4/linux/cursesmain.c
Normal 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, ¶ms->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
119
xwords4/linux/cursesmain.h
Normal 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
169
xwords4/linux/filestream.c
Normal 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
|
||||
|
26
xwords4/linux/filestream.h
Normal file
26
xwords4/linux/filestream.h
Normal 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
14
xwords4/linux/flip.xpm
Normal 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
83
xwords4/linux/gtkask.c
Normal 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
32
xwords4/linux/gtkask.h
Normal 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
913
xwords4/linux/gtkdraw.c
Normal 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
27
xwords4/linux/gtkdraw.h
Normal 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
|
108
xwords4/linux/gtkletterask.c
Normal file
108
xwords4/linux/gtkletterask.c
Normal 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 */
|
31
xwords4/linux/gtkletterask.h
Normal file
31
xwords4/linux/gtkletterask.h
Normal 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
1537
xwords4/linux/gtkmain.c
Normal file
File diff suppressed because it is too large
Load diff
121
xwords4/linux/gtkmain.h
Normal file
121
xwords4/linux/gtkmain.h
Normal 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
436
xwords4/linux/gtknewgame.c
Normal 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 */
|
40
xwords4/linux/gtknewgame.h
Normal file
40
xwords4/linux/gtknewgame.h
Normal 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 */
|
94
xwords4/linux/gtkpasswdask.c
Normal file
94
xwords4/linux/gtkpasswdask.c
Normal 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 */
|
31
xwords4/linux/gtkpasswdask.h
Normal file
31
xwords4/linux/gtkpasswdask.h
Normal 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
17
xwords4/linux/hint.xpm
Normal 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
14
xwords4/linux/juggle.xpm
Normal 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
372
xwords4/linux/linuxdict.c
Normal 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
762
xwords4/linux/linuxmain.c
Normal 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
50
xwords4/linux/linuxmain.h
Normal 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
|
34
xwords4/linux/linuxserver.c
Normal file
34
xwords4/linux/linuxserver.c
Normal 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 */
|
31
xwords4/linux/linuxserver.h
Normal file
31
xwords4/linux/linuxserver.h
Normal 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
81
xwords4/linux/main.h
Normal 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
|
15
xwords4/linux/scripts/playnum.sh
Executable file
15
xwords4/linux/scripts/playnum.sh
Executable 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
|
23
xwords4/linux/scripts/wordlens.pl
Executable file
23
xwords4/linux/scripts/wordlens.pl
Executable 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
18
xwords4/linux/value.xpm
Normal 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
113
xwords4/linux/xptypes.h
Normal 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
|
||||
|
Loading…
Reference in a new issue