make curses app more like the rest

Lots of changes adding a games-list view to the app from which you
create new games, open and delete existing ones, etc. There's still
plenty that's unimplemented, but it's already more useful for testing
and development. Which is the point.
This commit is contained in:
Eric House 2020-01-24 09:05:16 -08:00
parent 9f0324f8ae
commit 598be04bef
42 changed files with 3437 additions and 2179 deletions

View file

@ -643,15 +643,14 @@ CommsCtxt*
comms_makeFromStream( MPFORMAL XWStreamCtxt* stream, XW_UtilCtxt* util, comms_makeFromStream( MPFORMAL XWStreamCtxt* stream, XW_UtilCtxt* util,
const TransportProcs* procs, XP_U16 forceChannel ) const TransportProcs* procs, XP_U16 forceChannel )
{ {
XP_Bool isServer; XP_U16 nPlayersHere, nPlayersTotal;
XP_U16 nAddrRecs, nPlayersHere, nPlayersTotal;
AddressRecord** prevsAddrNext; AddressRecord** prevsAddrNext;
MsgQueueElem** prevsQueueNext; MsgQueueElem** prevsQueueNext;
XP_U16 version = stream_getVersion( stream ); XP_U16 version = stream_getVersion( stream );
CommsAddrRec addr; CommsAddrRec addr;
short ii; short ii;
isServer = stream_getU8( stream ); XP_Bool isServer = stream_getU8( stream );
addrFromStream( &addr, stream ); addrFromStream( &addr, stream );
if ( version >= STREAM_VERS_DEVIDS if ( version >= STREAM_VERS_DEVIDS
@ -692,7 +691,7 @@ comms_makeFromStream( MPFORMAL XWStreamCtxt* stream, XW_UtilCtxt* util,
comms->queueLen = stream_getU8( stream ); comms->queueLen = stream_getU8( stream );
nAddrRecs = stream_getU8( stream ); XP_U16 nAddrRecs = stream_getU8( stream );
prevsAddrNext = &comms->recs; prevsAddrNext = &comms->recs;
for ( ii = 0; ii < nAddrRecs; ++ii ) { for ( ii = 0; ii < nAddrRecs; ++ii ) {
AddressRecord* rec = (AddressRecord*)XP_CALLOC( mpool, sizeof(*rec)); AddressRecord* rec = (AddressRecord*)XP_CALLOC( mpool, sizeof(*rec));
@ -1206,13 +1205,15 @@ makeElemWithID( CommsCtxt* comms, MsgID msgID, AddressRecord* rec,
XP_U16 XP_U16
comms_getChannelSeed( CommsCtxt* comms ) comms_getChannelSeed( CommsCtxt* comms )
{ {
while ( 0 == (comms->channelSeed & ~CHANNEL_MASK) ) { XP_U16 result = !!comms ? comms->channelSeed : 0;
comms->channelSeed = XP_RANDOM() & ~CHANNEL_MASK; while ( !!comms && 0 == (result & ~CHANNEL_MASK) ) {
comms->channelSeed |= comms->forceChannel; result = XP_RANDOM() & ~CHANNEL_MASK;
CNO_FMT( cbuf, comms->channelSeed ); result |= comms->forceChannel;
XP_LOGF( "%s: made seed: %s(%d)", __func__, cbuf, comms->channelSeed ); CNO_FMT( cbuf, result );
XP_LOGF( "%s: made seed: %s(%d)", __func__, cbuf, result );
comms->channelSeed = result;
} }
return comms->channelSeed; return result;
} }
/* Send a message using the sequentially next MsgID. Save the message so /* Send a message using the sequentially next MsgID. Save the message so
@ -1687,16 +1688,16 @@ got_connect_cmd( CommsCtxt* comms, XWStreamCtxt* stream,
set_relay_state( comms, reconnected ? COMMS_RELAYSTATE_RECONNECTED set_relay_state( comms, reconnected ? COMMS_RELAYSTATE_RECONNECTED
: COMMS_RELAYSTATE_CONNECTED ); : COMMS_RELAYSTATE_CONNECTED );
XWHostID myHostID = stream_getU8( stream ); XWHostID myHostID = stream_getU8( stream );
XP_LOGF( "%s: changing rr.myHostID from %x to %x", __func__,
comms->rr.myHostID, myHostID );
if ( comms->rr.myHostID != myHostID ) { if ( comms->rr.myHostID != myHostID ) {
XP_LOGF( "%s: changing rr.myHostID from %x to %x", __func__,
comms->rr.myHostID, myHostID );
comms->rr.myHostID = myHostID; comms->rr.myHostID = myHostID;
} }
isServer = HOST_ID_SERVER == comms->rr.myHostID; isServer = HOST_ID_SERVER == comms->rr.myHostID;
if ( isServer != comms->isServer ) { if ( isServer != comms->isServer ) {
XP_LOGF( "%s: becoming a server", __func__ ); XP_LOGF( "%s: becoming%s a server", __func__, isServer ? "" : " NOT" );
comms->isServer = isServer; comms->isServer = isServer;
util_setIsServer( comms->util, comms->isServer ); util_setIsServer( comms->util, comms->isServer );

View file

@ -53,7 +53,7 @@ load( XW_DUtilCtxt* dutil )
if ( 0 < stream_getSize( stream ) ) { if ( 0 < stream_getSize( stream ) ) {
state->devCount = stream_getU16( stream ); state->devCount = stream_getU16( stream );
++state->devCount; /* for testing until something's there */ ++state->devCount; /* for testing until something's there */
XP_LOGF( "%s(): read devCount: %d", __func__, state->devCount ); /* XP_LOGF( "%s(): read devCount: %d", __func__, state->devCount ); */
} else { } else {
XP_LOGF( "%s(): empty stream!!", __func__ ); XP_LOGF( "%s(): empty stream!!", __func__ );
} }

View file

@ -388,6 +388,13 @@ game_getState( const XWGame* game, GameStateInfo* gsi )
comms_countPendingPackets(game->comms) : 0; comms_countPendingPackets(game->comms) : 0;
} }
XP_Bool
game_getIsServer( const XWGame* game )
{
XP_Bool result = comms_getIsServer( game->comms );
return result;
}
void void
game_dispose( XWGame* game ) game_dispose( XWGame* game )
{ {
@ -642,7 +649,6 @@ gi_writeToStream( XWStreamCtxt* stream, const CurGameInfo* gi )
stream_putBits( stream, NPLAYERS_NBITS, gi->nPlayers ); stream_putBits( stream, NPLAYERS_NBITS, gi->nPlayers );
stream_putBits( stream, nColsNBits, gi->boardSize ); stream_putBits( stream, nColsNBits, gi->boardSize );
stream_putBits( stream, 2, gi->serverRole ); stream_putBits( stream, 2, gi->serverRole );
/* XP_LOGF( "%s: wrote serverRole of %d", __func__, gi->serverRole ); */
stream_putBits( stream, 1, gi->hintsNotAllowed ); stream_putBits( stream, 1, gi->hintsNotAllowed );
stream_putBits( stream, 2, gi->phoniesAction ); stream_putBits( stream, 2, gi->phoniesAction );
stream_putBits( stream, 1, gi->timerEnabled ); stream_putBits( stream, 1, gi->timerEnabled );

View file

@ -89,6 +89,7 @@ XP_Bool game_receiveMessage( XWGame* game, XWStreamCtxt* stream,
void game_dispose( XWGame* game ); void game_dispose( XWGame* game );
void game_getState( const XWGame* game, GameStateInfo* gsi ); void game_getState( const XWGame* game, GameStateInfo* gsi );
XP_Bool game_getIsServer( const XWGame* game );
void gi_initPlayerInfo( MPFORMAL CurGameInfo* gi, void gi_initPlayerInfo( MPFORMAL CurGameInfo* gi,
const XP_UCHAR* nameTemplate ); const XP_UCHAR* nameTemplate );

View file

@ -238,7 +238,9 @@ syncPlayers( ServerCtxt* server )
static XP_Bool static XP_Bool
amServer( const ServerCtxt* server ) amServer( const ServerCtxt* server )
{ {
return SERVER_ISSERVER == server->vol.gi->serverRole; XP_Bool result = SERVER_ISSERVER == server->vol.gi->serverRole;
// LOG_RETURNF( "%d (seed=%d)", result, comms_getChannelSeed( server->vol.comms ) );
return result;
} }
static void static void
@ -867,7 +869,6 @@ makeRobotMove( ServerCtxt* server )
XP_Bool result = XP_FALSE; XP_Bool result = XP_FALSE;
XP_Bool searchComplete; XP_Bool searchComplete;
XP_S16 turn; XP_S16 turn;
const TrayTileSet* tileSet;
MoveInfo newMove = {0}; MoveInfo newMove = {0};
ModelCtxt* model = server->vol.model; ModelCtxt* model = server->vol.model;
CurGameInfo* gi = server->vol.gi; CurGameInfo* gi = server->vol.gi;
@ -899,7 +900,7 @@ makeRobotMove( ServerCtxt* server )
model_resetCurrentTurn( model, turn ); model_resetCurrentTurn( model, turn );
if ( !forceTrade ) { if ( !forceTrade ) {
tileSet = model_getPlayerTiles( model, turn ); const TrayTileSet* tileSet = model_getPlayerTiles( model, turn );
#ifdef XWFEATURE_BONUSALL #ifdef XWFEATURE_BONUSALL
XP_U16 allTilesBonus = server_figureFinishBonus( server, turn ); XP_U16 allTilesBonus = server_figureFinishBonus( server, turn );
#endif #endif
@ -1143,7 +1144,7 @@ server_do( ServerCtxt* server )
} else { } else {
XP_Bool moreToDo = XP_FALSE; XP_Bool moreToDo = XP_FALSE;
server->serverDoing = XP_TRUE; server->serverDoing = XP_TRUE;
XP_LOGF( "%s(): gameState: %s", __func__, getStateStr(server->nv.gameState) );
switch( server->nv.gameState ) { switch( server->nv.gameState ) {
case XWSTATE_BEGIN: case XWSTATE_BEGIN:
if ( server->nv.pendingRegistrations == 0 ) { /* all players on if ( server->nv.pendingRegistrations == 0 ) { /* all players on
@ -2952,6 +2953,7 @@ server_receiveMessage( ServerCtxt* server, XWStreamCtxt* incoming )
XP_Bool accepted = XP_FALSE; XP_Bool accepted = XP_FALSE;
XP_Bool isServer = amServer( server ); XP_Bool isServer = amServer( server );
const XW_Proto code = readProto( server, incoming ); const XW_Proto code = readProto( server, incoming );
XP_LOGF( "%s(code=%s)", __func__, codeToStr(code) );
if ( code == XWPROTO_DEVICE_REGISTRATION ) { if ( code == XWPROTO_DEVICE_REGISTRATION ) {
accepted = isServer; accepted = isServer;

View file

@ -257,7 +257,7 @@ smsproto_prepOutbound( SMSProto* state, SMS_CMD cmd, XP_U32 gameID,
} }
static SMSMsgArray* static SMSMsgArray*
appendLocMsg( SMSProto* state, SMSMsgArray* arr, SMSMsgLoc* msg ) appendLocMsg( SMSProto* XP_UNUSED_DBG(state), SMSMsgArray* arr, SMSMsgLoc* msg )
{ {
if ( NULL == arr ) { if ( NULL == arr ) {
arr = XP_CALLOC( state->mpool, sizeof(*arr) ); arr = XP_CALLOC( state->mpool, sizeof(*arr) );
@ -273,7 +273,7 @@ appendLocMsg( SMSProto* state, SMSMsgArray* arr, SMSMsgLoc* msg )
} }
static SMSMsgArray* static SMSMsgArray*
appendNetMsg( SMSProto* state, SMSMsgArray* arr, SMSMsgNet* msg ) appendNetMsg( SMSProto* XP_UNUSED_DBG(state), SMSMsgArray* arr, SMSMsgNet* msg )
{ {
if ( NULL == arr ) { if ( NULL == arr ) {
arr = XP_CALLOC( state->mpool, sizeof(*arr) ); arr = XP_CALLOC( state->mpool, sizeof(*arr) );
@ -396,7 +396,7 @@ smsproto_freeMsgArray( SMSProto* state, SMSMsgArray* arr )
} }
static void static void
freeMsg( SMSProto* state, MsgRec** msgp ) freeMsg( SMSProto* XP_UNUSED_DBG(state), MsgRec** msgp )
{ {
XP_FREEP( state->mpool, &(*msgp)->msgNet.data ); XP_FREEP( state->mpool, &(*msgp)->msgNet.data );
XP_FREEP( state->mpool, msgp ); XP_FREEP( state->mpool, msgp );
@ -595,7 +595,7 @@ rmFromPhoneRec( SMSProto* state, int fromPhoneIndex )
} }
static void static void
freeMsgIDRec( SMSProto* state, MsgIDRec* rec, int fromPhoneIndex, int msgIDIndex ) freeMsgIDRec( SMSProto* state, MsgIDRec* XP_UNUSED_DBG(rec), int fromPhoneIndex, int msgIDIndex )
{ {
FromPhoneRec* fromPhoneRec = &state->fromPhoneRecs[fromPhoneIndex]; FromPhoneRec* fromPhoneRec = &state->fromPhoneRecs[fromPhoneIndex];
MsgIDRec* msgIDRec = &fromPhoneRec->msgIDRecs[msgIDIndex]; MsgIDRec* msgIDRec = &fromPhoneRec->msgIDRecs[msgIDIndex];

View file

@ -104,5 +104,7 @@ void smsproto_freeMsgArray( SMSProto* state, SMSMsgArray* arr );
# ifdef DEBUG # ifdef DEBUG
void smsproto_runTests( MPFORMAL XW_DUtilCtxt* dutil ); void smsproto_runTests( MPFORMAL XW_DUtilCtxt* dutil );
# else
# define smsproto_runTests( p1, p2 )
# endif # endif
#endif #endif

View file

@ -21,6 +21,7 @@ BUILD_DIR ?= .
ifeq ($(MEMDEBUG),TRUE) ifeq ($(MEMDEBUG),TRUE)
DEFINES = -DMEM_DEBUG -DDEBUG -DENABLE_LOGGING -DNUMBER_KEY_AS_INDEX DEFINES = -DMEM_DEBUG -DDEBUG -DENABLE_LOGGING -DNUMBER_KEY_AS_INDEX
DEFINES += -DCOMMS_CHECKSUM DEFINES += -DCOMMS_CHECKSUM
DEFINES += -DLOG_COMMS_MSGNOS
CFLAGS += -g $(GPROFFLAG) -Wall -Wunused-parameter -Wcast-align -Werror -O0 CFLAGS += -g $(GPROFFLAG) -Wall -Wunused-parameter -Wcast-align -Werror -O0
# DEFINES += -DDEBUG_HASHING # DEFINES += -DDEBUG_HASHING
CFLAGS += -DDEBUG_TS -rdynamic CFLAGS += -DDEBUG_TS -rdynamic
@ -144,8 +145,6 @@ DEFINES += -DCURSES_CELL_WIDTH=$(CURSES_CELL_WIDTH)
endif endif
DEFINES += $(UNICODE) DEFINES += $(UNICODE)
DEFINES += -DLOG_COMMS_MSGNOS
# Networking-related features. Only set these if STANDALONE is not set # Networking-related features. Only set these if STANDALONE is not set
ifeq ($(STANDALONE),) ifeq ($(STANDALONE),)
@ -210,16 +209,19 @@ endif
ifdef DO_CURSES ifdef DO_CURSES
CURSES_OBJS = \ CURSES_OBJS = \
$(BUILD_PLAT_DIR)/cursesmain.o \ $(BUILD_PLAT_DIR)/cursesmain.o \
$(BUILD_PLAT_DIR)/cursesboard.o \
$(BUILD_PLAT_DIR)/cursesmenu.o \
$(BUILD_PLAT_DIR)/curgamlistwin.o \
$(BUILD_PLAT_DIR)/cursesdraw.o \ $(BUILD_PLAT_DIR)/cursesdraw.o \
$(BUILD_PLAT_DIR)/cursesask.o \ $(BUILD_PLAT_DIR)/cursesask.o \
$(BUILD_PLAT_DIR)/cursesdlgutil.o \ $(BUILD_PLAT_DIR)/cursesdlgutil.o \
$(BUILD_PLAT_DIR)/cursesletterask.o $(BUILD_PLAT_DIR)/cursesletterask.o
endif endif
ifndef LIB_NO_UI ifndef LIB_NO_UI
MAIN_OBJS = $(BUILD_PLAT_DIR)/linuxmain.o MAIN_OBJS = $(BUILD_PLAT_DIR)/linuxmain.o \
$(BUILD_PLAT_DIR)/gsrcwrap.o
endif endif
OBJ = \ OBJ = \
$(BUILD_PLAT_DIR)/filestream.o \ $(BUILD_PLAT_DIR)/filestream.o \
$(BUILD_PLAT_DIR)/linuxbt.o \ $(BUILD_PLAT_DIR)/linuxbt.o \

View file

@ -0,0 +1,293 @@
/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
/*
* Copyright 2020 by Eric House (xwords@eehouse.org). All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <ncurses.h>
#include <glib.h>
#include "curgamlistwin.h"
struct CursGameList {
WINDOW* window;
int width, height;
int curSel;
int yOffset;
GSList* games;
sqlite3* pDb;
};
static void adjustCurSel( CursGameList* cgl );
CursGameList*
cgl_init( sqlite3* pDb, int width, int height )
{
CursGameList* cgl = g_malloc0( sizeof( *cgl ) );
cgl->pDb = pDb;
cgl->window = newwin( height, width, 0, 0 );
XP_LOGF( "%s(): made window with height=%d, width=%d", __func__, height, width );
cgl->width = width;
cgl->height = height;
return cgl;
}
void
cgl_destroy( CursGameList* cgl )
{
g_slist_free_full( cgl->games, g_free );
delwin( cgl->window );
g_free( cgl );
}
static void
addOne( CursGameList* cgl, sqlite3_int64 rowid )
{
GameInfo gib;
if ( getGameInfo( cgl->pDb, rowid, &gib ) ) {
GameInfo* gibp = g_malloc( sizeof(*gibp) );
*gibp = gib;
cgl->games = g_slist_append( cgl->games, gibp );
}
}
/* Load from the DB */
void
cgl_refresh( CursGameList* cgl )
{
g_slist_free_full( cgl->games, g_free );
cgl->games = NULL;
sqlite3* pDb = cgl->pDb;
GSList* games = listGames( pDb );
for ( GSList* iter = games; !!iter; iter = iter->next ) {
sqlite3_int64* rowid = (sqlite3_int64*)iter->data;
addOne( cgl, *rowid );
}
cgl_draw( cgl );
}
static GSList*
findFor( CursGameList* cgl, sqlite3_int64 rowid )
{
GSList* result = NULL;
for ( GSList* iter = cgl->games; !!iter && !result; iter = iter->next ) {
GameInfo* gib = (GameInfo*)iter->data;
if ( gib->rowid == rowid ) {
result = iter;
}
}
return result;
}
void
cgl_refreshOne( CursGameList* cgl, sqlite3_int64 rowid, bool select )
{
// Update the info. In place if it exists, otherwise creating a new list
// elem
GameInfo gib;
if ( getGameInfo( cgl->pDb, rowid, &gib ) ) {
GameInfo* found;
GSList* elem = findFor( cgl, rowid );
if ( !!elem ) {
found = (GameInfo*)elem->data;
*found = gib;
} else {
found = g_malloc( sizeof(*found) );
*found = gib;
cgl->games = g_slist_append( cgl->games, found );
}
if ( select ) {
cgl->curSel = g_slist_index( cgl->games, found );
}
adjustCurSel( cgl );
}
}
void
cgl_remove( CursGameList* cgl, sqlite3_int64 rowid )
{
GSList* elem = findFor( cgl, rowid );
if ( !!elem ) {
g_free( elem->data );
cgl->games = g_slist_delete_link( cgl->games, elem );
}
adjustCurSel( cgl );
}
void
cgl_moveSel( CursGameList* cgl, bool down )
{
int nGames = g_slist_length( cgl->games );
cgl->curSel += nGames + (down ? 1 : -1);
cgl->curSel %= nGames;
adjustCurSel( cgl );
}
static void
adjustCurSel( CursGameList* cgl )
{
XP_LOGF( "%s() start: curSel: %d; yOffset: %d", __func__, cgl->curSel, cgl->yOffset );
int nGames = g_slist_length( cgl->games );
if ( cgl->curSel >= nGames ) {
cgl->curSel = nGames - 1;
}
/* Now adjust yOffset */
int nVisRows = cgl->height - 2; /* 1 for the title and header rows */
if ( cgl->curSel - cgl->yOffset >= nVisRows ) {
cgl->yOffset = cgl->curSel - nVisRows + 1;
} else {
while ( cgl->curSel < cgl->yOffset ) {
--cgl->yOffset;
}
}
XP_LOGF( "%s() end: curSel: %d; yOffset: %d", __func__, cgl->curSel, cgl->yOffset );
cgl_draw( cgl );
}
static int
countBits( int bits )
{
int result = 0;
while ( 0 != bits ) {
++result;
bits &= bits - 1;
}
return result;
}
static const char*
codeToLang( XP_LangCode langCode )
{
const char* langName = "<\?\?\?>";
switch( langCode ) {
case 1: langName = "English"; break;
case 2: langName = "French"; break;
case 3: langName = "German"; break;
case 4: langName = "Turkish";break;
case 5: langName = "Arabic"; break;
case 6: langName = "Spanish"; break;
case 7: langName = "Swedish"; break;
case 8:langName = "Polish";; break;
case 9: langName = "Danish"; break;
case 10: langName = "Italian"; break;
case 11: langName = "Dutch"; break;
case 12: langName = "Catalan"; break;
case 13: langName = "Portuguese"; break;
case 15: langName = "Russian"; break;
case 17: langName = "Czech"; break;
case 18: langName = "Greek"; break;
case 19: langName = "Slovak"; break;
default:
XP_LOGF( "%s(): bad code %d", __func__, langCode );
break;
// XP_ASSERT(0);
}
return langName;
}
void
cgl_draw( CursGameList* cgl )
{
WINDOW* win = cgl->window;
werase( win );
const int nGames = g_slist_length( cgl->games );
/* Draw '+' at far right if scrollable */
int nBelow = nGames - (cgl->height-2) - cgl->yOffset;
XP_LOGF( "%s(): yOffset: %d; nBelow: %d", __func__, cgl->yOffset, nBelow );
if ( 0 < nBelow ) {
mvwaddstr( win, cgl->height-2, cgl->width - 1, "+" );
}
if ( 0 < cgl->yOffset ) {
mvwaddstr( win, 0, cgl->width-1, "+" );
}
const char* cols[] = {"Row", "RowID", "Lang", "Scores", "GameID", "Role", "Room",
"nTotal", "nMissing", "Seed", "nMoves", "Turn", "nPend", };
int nShown = nGames <= cgl->height - 2 ? nGames : cgl->height - 2;
char* data[nShown + 1][VSIZE(cols)];
for ( int ii = 0; ii < VSIZE(cols); ++ii ) {
data[0][ii] = g_strdup(cols[ii]);
}
int line = 1;
for ( int ii = 0; ii < nShown; ++ii ) {
const GameInfo* gi = g_slist_nth_data( cgl->games, ii + cgl->yOffset );
int col = 0;
data[line][col++] = g_strdup_printf( "%d", ii + cgl->yOffset + 1 ); /* 1-based */
data[line][col++] = g_strdup_printf( "%05lld", gi->rowid );
data[line][col++] = g_strdup( codeToLang(gi->dictLang) );
data[line][col++] = g_strdup( gi->scores );
data[line][col++] = g_strdup_printf( "%d", gi->gameID );
data[line][col++] = g_strdup_printf( "%d", gi->role );
data[line][col++] = g_strdup( gi->room );
data[line][col++] = g_strdup_printf( "%d", gi->nTotal );
data[line][col++] = g_strdup_printf( "%d", countBits(gi->nMissing) );
data[line][col++] = g_strdup_printf( "%d", gi->seed );
data[line][col++] = g_strdup_printf( "%d", gi->nMoves );
data[line][col++] = g_strdup_printf( "%d", gi->turn );
data[line][col++] = g_strdup_printf( "%d", gi->nPending );
XP_ASSERT( col == VSIZE(data[line]) );
++line;
}
int maxlen = 0;
int offset = 0;
for ( int col = 0; col < VSIZE(data[0]); ++col ) {
for ( int line = 0; line < VSIZE(data); ++line ) {
char* str = data[line][col];
int len = strlen(str);
if ( maxlen < len ) {
maxlen = len;
}
bool highlight = cgl->yOffset + line - 1 == cgl->curSel;
if ( highlight ) {
wstandout( win );
}
mvwaddstr( win, line + 1, offset, str );
if ( highlight ) {
wstandend( win );
}
g_free( str );
}
offset += maxlen + 2;
maxlen = 0;
}
char buf[cgl->width + 1];
snprintf( buf, VSIZE(buf), "%d games total", nGames );
mvwaddstr( win, 0, 0, buf );
wrefresh( win );
}
const GameInfo*
cgl_getSel( CursGameList* cgl )
{
return g_slist_nth_data( cgl->games, cgl->curSel );
}
int
cgl_getNGames( CursGameList* cgl )
{
return g_slist_length( cgl->games );
}

View file

@ -0,0 +1,43 @@
/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
/*
* Copyright 2020 by Eric House (xwords@eehouse.org). All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _CURGAMLISTWIN_H_
#define _CURGAMLISTWIN_H_
#include <stdbool.h>
#include "gamesdb.h"
typedef struct CursGameList CursGameList;
CursGameList* cgl_init( sqlite3* pDb, int width, int height );
void cgl_destroy( CursGameList* cgl );
void cgl_refresh( CursGameList* cgl );
void cgl_refreshOne( CursGameList* cgl, sqlite3_int64 rowid, bool select );
void cgl_remove( CursGameList* cgl, sqlite3_int64 rowid );
void cgl_moveSel( CursGameList* cgl, bool down );
void cgl_draw( CursGameList* cgl );
const GameInfo* cgl_getSel( CursGameList* cgl );
int cgl_getNGames( CursGameList* cgl );
#endif

View file

@ -32,11 +32,12 @@
/* Figure out how many lines there are and how wide the widest is. /* Figure out how many lines there are and how wide the widest is.
*/ */
int int
cursesask( CursesAppGlobals* globals, const char* question, short numButtons, cursesask( WINDOW* window, const char* question, short numButtons,
const char** buttons ) const char** buttons )
{ {
WINDOW* confWin; WINDOW* confWin;
int x, y, rows, row, nLines; int x, y, rows, row, nLines;
int left, top;
short newSelButton = 0; short newSelButton = 0;
short curSelButton = 1; /* force draw by being different */ short curSelButton = 1; /* force draw by being different */
short spacePerButton, num; short spacePerButton, num;
@ -45,7 +46,8 @@ cursesask( CursesAppGlobals* globals, const char* question, short numButtons,
FormatInfo fi; FormatInfo fi;
int len; int len;
getmaxyx(globals->boardWin, y, x); getmaxyx( window, y, x);
getbegyx( window, top, left );
measureAskText( question, x-2, &fi ); measureAskText( question, x-2, &fi );
len = fi.maxLen; len = fi.maxLen;
@ -62,8 +64,8 @@ cursesask( CursesAppGlobals* globals, const char* question, short numButtons,
} }
nLines = ASK_HEIGHT + rows - 1; nLines = ASK_HEIGHT + rows - 1;
confWin = newwin( nLines, len+(PAD*2), confWin = newwin( nLines, len+(PAD*2), top + ((y/2) - (nLines/2)),
(y/2) - (nLines/2), (x-len-2)/2 ); left + ((x-len-2)/2) );
keypad( confWin, TRUE ); keypad( confWin, TRUE );
wclear( confWin ); wclear( confWin );
box( confWin, '|', '-'); box( confWin, '|', '-');
@ -120,9 +122,9 @@ cursesask( CursesAppGlobals* globals, const char* question, short numButtons,
delwin( confWin ); delwin( confWin );
/* this leaves a ghost line, but I can't figure out a better way. */ /* 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 ); wtouchln( window, (y/2)-(nLines/2), ASK_HEIGHT + rows - 1, 1 );
wrefresh( globals->boardWin ); wrefresh( window );
return curSelButton; return curSelButton;
} /* ask */ } /* cursesask */
#endif /* PLATFORM_NCURSES */ #endif /* PLATFORM_NCURSES */

View file

@ -22,7 +22,7 @@
#include "cursesmain.h" #include "cursesmain.h"
int cursesask( CursesAppGlobals* globals, const char* question, int cursesask( WINDOW* window, const char* question,
short numButtons, const char** buttons ); short numButtons, const char** buttons );

1351
xwords4/linux/cursesboard.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,42 @@
/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
/*
* Copyright 2000 - 2020 by Eric House (xwords@eehouse.org). All rights
* reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _CURSESBOARD_H_
#define _CURSESBOARD_H_
#include "cursesmain.h"
typedef struct CursesAppGlobals CursesAppGlobals;
typedef struct CursesBoardState CursesBoardState;
typedef void (*OnGameSaved)( CursesAppGlobals* aGlobals, sqlite3_int64 rowid, bool isNew );
CursesBoardState* cb_init( CursesAppGlobals* aGlobals, LaunchParams* params,
CursesMenuState* menuState, OnGameSaved onGameSaved );
void cb_open( CursesBoardState* cbState, sqlite3_int64 rowid,
int width, int top, int height );
void cb_new( CursesBoardState* cbState, int width, int top, int height );
XP_U16 cb_feedBuffer( CursesBoardState* cbState, sqlite3_int64 rowid,
const XP_U8* buf, XP_U16 len, const CommsAddrRec* from );
void cb_closeAll( CursesBoardState* cbState );
#endif

View file

@ -646,4 +646,12 @@ cursesDrawCtxtMake( WINDOW* boardWin )
return (DrawCtx*)dctx; return (DrawCtx*)dctx;
} /* curses_drawctxt_init */ } /* curses_drawctxt_init */
void
cursesDrawCtxtFree( DrawCtx* pdctx )
{
CursesDrawCtx* dctx = (CursesDrawCtx*)pdctx;
free( dctx->vtable );
free( dctx );
}
#endif /* PLATFORM_NCURSES */ #endif /* PLATFORM_NCURSES */

View file

@ -47,7 +47,7 @@ sizeTextsAsButtons( XP_U16 maxLen, XP_U16 nTiles, XP_U16* textsCols,
} /* sizeTextsAsButtons */ } /* sizeTextsAsButtons */
XP_S16 XP_S16
curses_askLetter( CursesAppGlobals* globals, XP_UCHAR* query, curses_askLetter( WINDOW* window, XP_UCHAR* query,
const XP_UCHAR** texts, XP_U16 nTiles ) const XP_UCHAR** texts, XP_U16 nTiles )
{ {
XP_S16 result; XP_S16 result;
@ -70,7 +70,7 @@ curses_askLetter( CursesAppGlobals* globals, XP_UCHAR* query,
textPtrs[i] = (char*)&texts[i]; textPtrs[i] = (char*)&texts[i];
} }
getmaxyx( globals->boardWin, y, x ); getmaxyx( window, y, x );
numCtlButtons = VSIZE(ctlButtons); numCtlButtons = VSIZE(ctlButtons);
@ -209,8 +209,8 @@ curses_askLetter( CursesAppGlobals* globals, XP_UCHAR* query,
delwin( confWin ); delwin( confWin );
/* this leaves a ghost line, but I can't figure out a better way. */ /* 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 ); wtouchln( window, (y/2)-(nLines/2), ASK_HEIGHT + rows - 1, 1 );
wrefresh( globals->boardWin ); wrefresh( window );
return result; return result;
} /* curses_askLetter */ } /* curses_askLetter */

View file

@ -23,7 +23,7 @@
#include "linuxmain.h" #include "linuxmain.h"
#include "cursesmain.h" #include "cursesmain.h"
XP_S16 curses_askLetter( CursesAppGlobals* globals, XP_UCHAR* query, XP_S16 curses_askLetter( WINDOW* window, XP_UCHAR* query,
const XP_UCHAR** texts, XP_U16 nTiles ); const XP_UCHAR** texts, XP_U16 nTiles );

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */ /* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/* /*
* Copyright 1997-2000 by Eric House (xwords@eehouse.org). All rights reserved. * Copyright 1997-2020 by Eric House (xwords@eehouse.org). All rights reserved.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -39,11 +39,17 @@
#include "server.h" #include "server.h"
#include "xwstate.h" #include "xwstate.h"
#include "util.h" #include "util.h"
#include "cursesmenu.h"
#include "cursesboard.h"
/* #include "compipe.h" */ /* #include "compipe.h" */
typedef struct CursesAppGlobals CursesAppGlobals; typedef struct CursesAppGlobals CursesAppGlobals;
typedef struct CursesBoardGlobals CursesBoardGlobals;
typedef XP_Bool (*EventFunc)(CursesAppGlobals* globals, int ch); typedef XP_Bool (*EventFunc)(CursesAppGlobals* globals, int ch);
void onCursesBoardClosing( CursesAppGlobals* aGlobals, CursesBoardGlobals* bGlobals );
void onCursesGameSaved( CursesAppGlobals* aGlobals, sqlite3_int64 rowid );
/* typedef void (*MenuDrawer)(CursesAppGlobals* globals); */ /* typedef void (*MenuDrawer)(CursesAppGlobals* globals); */
#define FD_MAX 6 #define FD_MAX 6
@ -51,56 +57,10 @@ typedef XP_Bool (*EventFunc)(CursesAppGlobals* globals, int ch);
#define FD_TIMEEVT 1 #define FD_TIMEEVT 1
#define FD_FIRSTSOCKET 2 #define FD_FIRSTSOCKET 2
struct CursesAppGlobals { // typedef struct CursesBoardGlobals;
CommonGlobals cGlobals;
struct CursesDrawCtx* draw;
DictionaryCtxt* dictionary;
EngineCtxt* engine;
XP_Bool amServer; /* this process acting as server */
WINDOW* mainWin;
WINDOW* menuWin;
WINDOW* boardWin;
XP_Bool doDraw;
const struct MenuList* menuList;
XP_U16 nLinesMenu;
gchar* lastErr;
XP_U16 nChatsSent;
XP_U16 nextQueryTimeSecs;
union {
struct {
XWStreamCtxt* stream; /* how we can reach the server */
} client;
struct {
int serverSocket;
XP_Bool socketOpen;
} server;
} csInfo;
short statusLine;
XWGameState state;
CommsRelayState commsRelayState;
struct sockaddr_in listenerSockAddr;
#ifdef USE_GLIBLOOP
GMainLoop* loop;
GList* sources;
int quitpipe[2];
#else
XP_Bool timeToExit;
short fdCount;
struct pollfd fdArray[FD_MAX]; /* one for stdio, one for listening socket */
int timepipe[2]; /* for reading/writing "user events" */
#endif
};
DrawCtx* cursesDrawCtxtMake( WINDOW* boardWin ); DrawCtx* cursesDrawCtxtMake( WINDOW* boardWin );
void cursesDrawCtxtFree( DrawCtx* dctx );
/* Ports: Client and server pick a port at startup on which they'll listen. /* 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, * If both are to be on the same device using localhost as their ip address,
@ -114,5 +74,6 @@ DrawCtx* cursesDrawCtxtMake( WINDOW* boardWin );
void cursesmain( XP_Bool isServer, LaunchParams* params ); void cursesmain( XP_Bool isServer, LaunchParams* params );
bool handleQuit( void* closure, int unused_key );
#endif #endif

213
xwords4/linux/cursesmenu.c Normal file
View file

@ -0,0 +1,213 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 1997-2020 by Eric House (xwords@eehouse.org). All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <glib.h>
#include <ncurses.h>
#include "cursesmenu.h"
#include "xptypes.h"
#include "comtypes.h"
#include "cursesmain.h"
#include "linuxmain.h"
#include "gsrcwrap.h"
struct CursesMenuState {
WINDOW* menuWin;
GSList* menuLists;
};
static gboolean
handle_stdin( GIOChannel* XP_UNUSED_DBG(source), GIOCondition condition,
gpointer data )
{
if ( 0 != (G_IO_IN & condition) ) {
#ifdef DEBUG
gint fd = g_io_channel_unix_get_fd( source );
XP_ASSERT( 0 == fd );
#endif
CursesMenuState* state = (CursesMenuState*)data;
int ch = wgetch( state->menuWin );
cmenu_handleKeyEvent( state, ch );
}
return TRUE;
}
CursesMenuState*
cmenu_init( WINDOW* mainWindow )
{
CursesMenuState* result = g_malloc0( sizeof(*result) );
int width, height;
getmaxyx( mainWindow, height, width );
result->menuWin = newwin( MENU_WINDOW_HEIGHT, width,
height-MENU_WINDOW_HEIGHT, 0 );
nodelay( result->menuWin, 1 ); /* don't block on getch */
ADD_SOCKET( result, 0, handle_stdin );
return result;
}
void
cmenu_dispose( CursesMenuState* state )
{
XP_ASSERT( !!state );
g_free( state );
}
typedef struct _MenuListElem {
MenuList* list;
void* closure;
} MenuListElem;
#define PUSH_TOKEN ((MenuListElem*)-1)
void
cmenu_pop( CursesMenuState* state )
{
/* pop off the front of the list until we've popped a PUSH_TOKEN */
for ( ; ; ) {
MenuListElem* elem = state->menuLists->data;
state->menuLists = g_slist_remove_link( state->menuLists,
state->menuLists );
if ( PUSH_TOKEN == elem ) {
break;
} else {
g_free( elem );
}
}
cmenu_draw( state );
}
static void
addMenus( CursesMenuState* state, void* closure, va_list ap )
{
for ( ; ; ) {
MenuList* param = va_arg(ap, MenuList*);
if ( !param ) {
break;
}
MenuListElem* elem = g_malloc0( sizeof( *elem ) );
elem->closure = closure;
elem->list = param;
state->menuLists = g_slist_prepend( state->menuLists, elem );
}
cmenu_draw( state );
}
void
cmenu_addMenus( CursesMenuState* state, void* closure, ... )
{
va_list ap;
va_start( ap, closure );
addMenus( state, closure, ap );
va_end(ap);
}
void cmenu_push( CursesMenuState* state, void* closure, ... )
{
if ( !!state ) {
state->menuLists = g_slist_prepend( state->menuLists, PUSH_TOKEN );
va_list ap;
va_start( ap, closure );
addMenus( state, closure, ap );
va_end(ap);
}
}
void
cmenu_removeMenus( CursesMenuState* state, ... )
{
va_list ap;
va_start( ap, state );
for ( ; ; ) {
MenuList* param = va_arg(ap, MenuList*);
if ( !param ) {
break;
}
for ( GSList* iter = state->menuLists; !!iter; iter = iter->next ) {
MenuListElem* elem = iter->data;
if ( elem->list == param ) {
state->menuLists = g_slist_remove( state->menuLists, elem );
break;
}
}
}
va_end( ap );
cmenu_draw( state );
}
bool
cmenu_handleKeyEvent( CursesMenuState* state, char ch )
{
bool result = false;
for ( GSList* iter = state->menuLists; !!iter; iter = iter->next ) {
const MenuListElem* elem = iter->data;
if ( PUSH_TOKEN == elem ) {
break;
}
for ( const MenuList* list = elem->list; !!list->handler; ++list ) {
if ( list->key == ch ) {
result = (*list->handler)(elem->closure, ch);
goto done;
}
}
}
done:
return result;
}
static void
fmtMenuItem( const MenuList* item, char* buf, int maxLen )
{
snprintf( buf, maxLen, "%s %s", item->keyDesc, item->desc );
}
void
cmenu_draw( const CursesMenuState* state )
{
WINDOW* win = state->menuWin;
wclear( win );
int line = 0;
int col = 0;
int maxLen = 0;
for ( GSList* iter = state->menuLists; !!iter; iter = iter->next ) {
const MenuListElem* elem = iter->data;
if ( PUSH_TOKEN == elem ) {
break;
}
for ( MenuList* list = elem->list; !!list->handler; ++list ) {
char buf[32];
fmtMenuItem( list, buf, sizeof(buf) );
int len = strlen(buf);
if ( maxLen < len ) {
maxLen = len;
}
mvwaddstr( win, line, col, buf );
if ( ++line >= MENU_WINDOW_HEIGHT ) {
line = 0;
col += maxLen + 1;
maxLen = 0;
}
}
}
wrefresh( win );
}

View file

@ -0,0 +1,54 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 1997-2020 by Eric House (xwords@eehouse.org). All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _CURSESMENU_H_
#define _CURSESMENU_H_
#include <ncurses.h>
#ifdef CURSES_SMALL_SCREEN
# define MENU_WINDOW_HEIGHT 1
# define BOARD_OFFSET 0
#else
# define MENU_WINDOW_HEIGHT 5 /* three lines plus borders */
# define BOARD_OFFSET 1
#endif
typedef bool (*CursesMenuHandler)( void* closure, int key );
typedef struct MenuList {
CursesMenuHandler handler;
char* desc;
char* keyDesc;
char key;
} MenuList;
typedef struct CursesMenuState CursesMenuState;
CursesMenuState* cmenu_init( WINDOW* mainWindow );
void cmenu_dispose( CursesMenuState* state );
void cmenu_clearMenus( CursesMenuState* state );
void cmenu_draw( const CursesMenuState* state );
void cmenu_addMenus( CursesMenuState* state, void* closure, ... );
void cmenu_push( CursesMenuState* state, void* closure, ... );
void cmenu_pop( CursesMenuState* state );
void cmenu_removeMenus( CursesMenuState* state, ... );
bool cmenu_handleKeyEvent( CursesMenuState* state, char ch );
#endif

View file

@ -59,10 +59,14 @@ openGamesDB( const char* dbName )
",local INT(1)" ",local INT(1)"
",nmoves INT" ",nmoves INT"
",seed INT" ",seed INT"
",nPending INT"
",role INT"
",dictlang INT"
",gameid INT" ",gameid INT"
",ntotal INT(2)" ",ntotal INT(2)"
",nmissing INT(2)" ",nmissing INT(2)"
",lastMoveTime INT" ",lastMoveTime INT"
",scores TEXT"
",dupTimerExpires INT" ",dupTimerExpires INT"
")"; ")";
result = sqlite3_exec( pDb, createGamesStr, NULL, NULL, NULL ); result = sqlite3_exec( pDb, createGamesStr, NULL, NULL, NULL );
@ -161,17 +165,17 @@ void
writeToDB( XWStreamCtxt* stream, void* closure ) writeToDB( XWStreamCtxt* stream, void* closure )
{ {
CommonGlobals* cGlobals = (CommonGlobals*)closure; CommonGlobals* cGlobals = (CommonGlobals*)closure;
sqlite3_int64 selRow = cGlobals->selRow; sqlite3_int64 selRow = cGlobals->rowid;
sqlite3* pDb = cGlobals->params->pDb; sqlite3* pDb = cGlobals->params->pDb;
XP_Bool newGame = -1 == selRow; XP_Bool newGame = -1 == selRow;
selRow = writeBlobColumnStream( stream, pDb, selRow, "game" ); selRow = writeBlobColumnStream( stream, pDb, selRow, "game" );
if ( newGame ) { /* new row; need to insert blob first */ if ( newGame ) { /* new row; need to insert blob first */
cGlobals->selRow = selRow; cGlobals->rowid = selRow;
XP_LOGF( "%s(): new game at row %lld", __func__, selRow ); XP_LOGF( "%s(): new game at row %lld", __func__, selRow );
} else { } else {
assert( selRow == cGlobals->selRow ); assert( selRow == cGlobals->rowid );
} }
(*cGlobals->onSave)( cGlobals->onSaveClosure, selRow, newGame ); (*cGlobals->onSave)( cGlobals->onSaveClosure, selRow, newGame );
@ -193,8 +197,8 @@ addSnapshot( CommonGlobals* cGlobals )
cGlobals->params->vtMgr ); cGlobals->params->vtMgr );
getImage( dctx, stream ); getImage( dctx, stream );
removeSurface( dctx ); removeSurface( dctx );
cGlobals->selRow = writeBlobColumnStream( stream, cGlobals->params->pDb, cGlobals->rowid = writeBlobColumnStream( stream, cGlobals->params->pDb,
cGlobals->selRow, "snap" ); cGlobals->rowid, "snap" );
stream_destroy( stream ); stream_destroy( stream );
} }
@ -215,8 +219,11 @@ summarize( CommonGlobals* cGlobals )
XP_U32 lastMoveTime = server_getLastMoveTime( game->server ); XP_U32 lastMoveTime = server_getLastMoveTime( game->server );
XP_U16 seed = 0; XP_U16 seed = 0;
XP_S16 nMissing = 0; XP_S16 nMissing = 0;
XP_U16 nTotal = cGlobals->gi->nPlayers; XP_U16 nPending = 0;
XP_U32 gameID = cGlobals->gi->gameID; const CurGameInfo* gi = cGlobals->gi;
XP_U16 nTotal = gi->nPlayers;
XP_U32 gameID = gi->gameID;
XP_LangCode dictLang = gi->dictLang;
XP_ASSERT( 0 != gameID ); XP_ASSERT( 0 != gameID );
CommsAddrRec addr = {0}; CommsAddrRec addr = {0};
gchar* room = ""; gchar* room = "";
@ -225,6 +232,24 @@ summarize( CommonGlobals* cGlobals )
gchar connvia[128] = {0}; gchar connvia[128] = {0};
XP_UCHAR relayID[32] = {0}; XP_UCHAR relayID[32] = {0};
ScoresArray scores = {0};
if ( gameOver ) {
model_figureFinalScores( game->model, &scores, NULL );
} else {
for ( int ii = 0; ii < nTotal; ++ii ) {
scores.arr[ii] = model_getPlayerScore( game->model, ii );
}
}
gchar scoreBufs[MAX_NUM_PLAYERS][64] = {0};
gchar* arr[MAX_NUM_PLAYERS+1] = {0};
for ( int ii = 0; ii < nTotal; ++ii ) {
XP_SNPRINTF( scoreBufs[ii], VSIZE(scoreBufs[ii]), "%s: %d",
gi->players[ii].name, scores.arr[ii] );
arr[ii] = scoreBufs[ii];
}
gchar* scoresStr = g_strjoinv( "; ", arr );
XP_LOGF( "%s(): scoresStr: %s", __func__, scoresStr );
if ( !!game->comms ) { if ( !!game->comms ) {
nMissing = server_getMissingPlayers( game->server ); nMissing = server_getMissingPlayers( game->server );
comms_getAddr( game->comms, &addr ); comms_getAddr( game->comms, &addr );
@ -255,19 +280,21 @@ summarize( CommonGlobals* cGlobals )
seed = comms_getChannelSeed( game->comms ); seed = comms_getChannelSeed( game->comms );
XP_U16 len = VSIZE(relayID); XP_U16 len = VSIZE(relayID);
(void)comms_getRelayID( game->comms, relayID, &len ); (void)comms_getRelayID( game->comms, relayID, &len );
nPending = comms_countPendingPackets( game->comms );
} else { } else {
strcat( connvia, "local" ); strcat( connvia, "local" );
} }
const char* fmt = "UPDATE games " const char* fmt = "UPDATE games "
" SET room='%s', ended=%d, turn=%d, local=%d, ntotal=%d, " " SET room='%s', ended=%d, turn=%d, local=%d, ntotal=%d, "
" nmissing=%d, nmoves=%d, seed=%d, gameid=%d, connvia='%s', " " nmissing=%d, nmoves=%d, seed=%d, dictlang=%d, gameid=%d, connvia='%s', "
" relayid='%s', lastMoveTime=%d" " relayid='%s', lastMoveTime=%d, scores='%s', nPending=%d, role=%d"
" WHERE rowid=%lld"; " WHERE rowid=%lld";
XP_UCHAR buf[256]; XP_UCHAR buf[2*256];
snprintf( buf, sizeof(buf), fmt, room, gameOver?1:0, turn, isLocal?1:0, snprintf( buf, sizeof(buf), fmt, room, gameOver?1:0, turn, isLocal?1:0,
nTotal, nMissing, nMoves, seed, gameID, connvia, relayID, lastMoveTime, nTotal, nMissing, nMoves, seed, dictLang, gameID, connvia, relayID, lastMoveTime,
cGlobals->selRow ); scoresStr, nPending, gi->serverRole, cGlobals->rowid );
XP_LOGF( "query: %s", buf ); XP_LOGF( "query: %s", buf );
sqlite3_stmt* stmt = NULL; sqlite3_stmt* stmt = NULL;
int result = sqlite3_prepare_v2( cGlobals->params->pDb, buf, -1, &stmt, NULL ); int result = sqlite3_prepare_v2( cGlobals->params->pDb, buf, -1, &stmt, NULL );
@ -283,6 +310,7 @@ summarize( CommonGlobals* cGlobals )
if ( !cGlobals->params->useCurses ) { if ( !cGlobals->params->useCurses ) {
addSnapshot( cGlobals ); addSnapshot( cGlobals );
} }
g_free( scoresStr );
} }
GSList* GSList*
@ -370,7 +398,7 @@ getGameInfo( sqlite3* pDb, sqlite3_int64 rowid, GameInfo* gib )
{ {
XP_Bool success = XP_FALSE; XP_Bool success = XP_FALSE;
const char* fmt = "SELECT room, ended, turn, local, nmoves, ntotal, nmissing, " const char* fmt = "SELECT room, ended, turn, local, nmoves, ntotal, nmissing, "
"seed, connvia, gameid, lastMoveTime, relayid, snap " "dictlang, seed, connvia, gameid, lastMoveTime, relayid, scores, nPending, role, snap "
"FROM games WHERE rowid = %lld"; "FROM games WHERE rowid = %lld";
XP_UCHAR query[256]; XP_UCHAR query[256];
snprintf( query, sizeof(query), fmt, rowid ); snprintf( query, sizeof(query), fmt, rowid );
@ -384,12 +412,14 @@ getGameInfo( sqlite3* pDb, sqlite3_int64 rowid, GameInfo* gib )
int col = 0; int col = 0;
int len = sizeof(gib->room); int len = sizeof(gib->room);
getColumnText( ppStmt, col++, gib->room, &len ); getColumnText( ppStmt, col++, gib->room, &len );
gib->rowid = rowid;
gib->gameOver = 1 == sqlite3_column_int( ppStmt, col++ ); gib->gameOver = 1 == sqlite3_column_int( ppStmt, col++ );
gib->turn = sqlite3_column_int( ppStmt, col++ ); gib->turn = sqlite3_column_int( ppStmt, col++ );
gib->turnLocal = 1 == sqlite3_column_int( ppStmt, col++ ); gib->turnLocal = 1 == sqlite3_column_int( ppStmt, col++ );
gib->nMoves = sqlite3_column_int( ppStmt, col++ ); gib->nMoves = sqlite3_column_int( ppStmt, col++ );
gib->nTotal = sqlite3_column_int( ppStmt, col++ ); gib->nTotal = sqlite3_column_int( ppStmt, col++ );
gib->nMissing = sqlite3_column_int( ppStmt, col++ ); gib->nMissing = sqlite3_column_int( ppStmt, col++ );
gib->dictLang = sqlite3_column_int( ppStmt, col++ );
gib->seed = sqlite3_column_int( ppStmt, col++ ); gib->seed = sqlite3_column_int( ppStmt, col++ );
len = sizeof(gib->conn); len = sizeof(gib->conn);
getColumnText( ppStmt, col++, gib->conn, &len ); getColumnText( ppStmt, col++, gib->conn, &len );
@ -397,6 +427,10 @@ getGameInfo( sqlite3* pDb, sqlite3_int64 rowid, GameInfo* gib )
gib->lastMoveTime = sqlite3_column_int( ppStmt, col++ ); gib->lastMoveTime = sqlite3_column_int( ppStmt, col++ );
len = sizeof(gib->relayID); len = sizeof(gib->relayID);
getColumnText( ppStmt, col++, gib->relayID, &len ); getColumnText( ppStmt, col++, gib->relayID, &len );
len = sizeof(gib->scores);
getColumnText( ppStmt, col++, gib->scores, &len );
gib->nPending = sqlite3_column_int( ppStmt, col++ );
gib->role = sqlite3_column_int( ppStmt, col++ );
snprintf( gib->name, sizeof(gib->name), "Game %lld", rowid ); snprintf( gib->name, sizeof(gib->name), "Game %lld", rowid );
#ifdef PLATFORM_GTK #ifdef PLATFORM_GTK
@ -533,8 +567,11 @@ db_fetch( sqlite3* pDb, const gchar* key, gchar* buf, gint* buflen )
XP_ASSERT( !!pDb ); XP_ASSERT( !!pDb );
FetchResult result = NOT_THERE; FetchResult result = NOT_THERE;
char query[256]; char query[256];
int len = snprintf( query, sizeof(query), #ifdef DEBUG
"SELECT value from pairs where key = '%s'", key ); int len =
#endif
snprintf( query, sizeof(query),
"SELECT value from pairs where key = '%s'", key );
XP_ASSERT( len < sizeof(query) ); XP_ASSERT( len < sizeof(query) );
sqlite3_stmt *ppStmt; sqlite3_stmt *ppStmt;
int sqlResult = sqlite3_prepare_v2( pDb, query, -1, &ppStmt, NULL ); int sqlResult = sqlite3_prepare_v2( pDb, query, -1, &ppStmt, NULL );

View file

@ -28,13 +28,16 @@
#include "comtypes.h" #include "comtypes.h"
typedef struct _GameInfo { typedef struct _GameInfo {
sqlite3_int64 rowid;
XP_UCHAR name[128]; XP_UCHAR name[128];
XP_UCHAR room[128]; XP_UCHAR room[128];
XP_UCHAR conn[128]; XP_UCHAR conn[128];
XP_UCHAR scores[128];
XP_UCHAR relayID[32]; XP_UCHAR relayID[32];
#ifdef PLATFORM_GTK #ifdef PLATFORM_GTK
GdkPixbuf* snap; GdkPixbuf* snap;
#endif #endif
XP_LangCode dictLang;
XP_U32 gameID; XP_U32 gameID;
XP_S16 nMoves; XP_S16 nMoves;
XP_Bool gameOver; XP_Bool gameOver;
@ -43,7 +46,9 @@ typedef struct _GameInfo {
XP_U16 nTotal; XP_U16 nTotal;
XP_S16 nMissing; XP_S16 nMissing;
XP_U16 seed; XP_U16 seed;
XP_U16 nPending;
XP_U32 lastMoveTime; XP_U32 lastMoveTime;
XP_U16 role;
} GameInfo; } GameInfo;
sqlite3* openGamesDB( const char* dbName ); sqlite3* openGamesDB( const char* dbName );

183
xwords4/linux/gsrcwrap.c Normal file
View file

@ -0,0 +1,183 @@
/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
/*
* Copyright 2020 by Eric House (xwords@eehouse.org). All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <glib.h>
#include <time.h>
#include <stdbool.h>
#include "gsrcwrap.h"
#include "xptypes.h"
#include "comtypes.h"
#define TAG __FILE__ ": "
typedef struct _WrapperState {
union {
GSourceFunc srcProc;
GIOFunc ioProc;
} proc;
void* data;
const char* caller;
const char* procName;
struct timespec spec;
} WrapperState;
static GSList* s_idleProcs = NULL;
static void
printElapsedFor( struct timespec* first, struct timespec* second,
const char* XP_UNUSED_DBG(proc),
const char* XP_UNUSED_DBG(action) )
/* time_t tv_sec; /\* seconds *\/ */
/* long tv_nsec; /\* nanoseconds *\/ */
{
time_t secs = second->tv_sec - first->tv_sec;
long nsecs = second->tv_nsec - first->tv_nsec;
while ( nsecs < 0 ) {
++secs;
nsecs += 1000000000;
}
/* float firstSecs = (float)first->tv_sec + (((float)first->tv_nsec)/1000000000.0f); */
/* float secondSecs = (float)second->tv_sec + (((float)second->tv_nsec)/1000000000.0f); */
XP_LOGF( TAG "elapsed %s %s(): %ld.%ld", action, proc, secs, nsecs );
}
static gint
idle_wrapper( gpointer data )
{
WrapperState* state = (WrapperState*)data;
XP_LOGF( TAG "%s(): CALLED for %s", __func__, state->procName );
struct timespec callTime;
clock_gettime(CLOCK_REALTIME, &callTime);
printElapsedFor( &state->spec, &callTime, state->procName, "scheduling" );
gint result = (*state->proc.srcProc)(state->data);
struct timespec returnTime;
clock_gettime( CLOCK_REALTIME, &returnTime );
printElapsedFor( &callTime, &returnTime, state->procName, "running" );
#ifdef DEBUG
const char* procName = state->procName;
#endif
if ( 0 == result ) { /* won't be getting called again */
s_idleProcs = g_slist_remove( s_idleProcs, state );
g_free( state );
}
XP_LOGF( TAG "%s(): DONE for %s; now have %d", __func__, procName,
g_slist_length(s_idleProcs) );
return result;
}
guint
_wrapIdle( GSourceFunc function, gpointer data,
const char* procName, const char* caller )
{
XP_LOGF( TAG "%s(): installing proc %s from caller %s", __func__,
procName, caller );
WrapperState* state = g_malloc0( sizeof(*state) );
s_idleProcs = g_slist_append( s_idleProcs, state );
state->proc.srcProc = function;
state->data = data;
state->caller = caller;
state->procName = procName;
clock_gettime(CLOCK_REALTIME, &state->spec);
guint res = g_idle_add( idle_wrapper, state );
XP_LOGF( TAG "%s(): added idle for %s; now have %d", __func__,
procName, g_slist_length(s_idleProcs) );
return res;
}
#define TRY_ONE(FLAG) \
if ((condition & FLAG) == FLAG) { \
offset += snprintf( &buf[offset], len - offset, #FLAG " " ); \
}
static void
formatFlags( char* buf, size_t len, GIOCondition condition )
{
int offset = 0;
TRY_ONE(G_IO_IN);
TRY_ONE(G_IO_HUP);
TRY_ONE(G_IO_ERR);
TRY_ONE(G_IO_PRI);
}
static gboolean
watch_wrapper( GIOChannel* source, GIOCondition condition, gpointer data )
{
WrapperState* state = (WrapperState*)data;
char buf[128] = {0};
formatFlags( buf, VSIZE(buf), condition );
XP_LOGF( TAG "%s(%s): CALLED; flags: %s", __func__, state->procName, buf );
struct timespec callTime;
clock_gettime(CLOCK_REALTIME, &callTime);
bool keep = (*state->proc.ioProc)(source, condition, state->data);
struct timespec returnTime;
clock_gettime( CLOCK_REALTIME, &returnTime );
printElapsedFor( &callTime, &returnTime, state->procName, "running" );
XP_LOGF( TAG "%s(%s): DONE", __func__, state->procName );
if ( 0 == keep ) { /* won't be getting called again */
// g_source_destroy( source );
g_free( state );
}
return keep;
}
guint
_wrapWatch( gpointer data, int socket, GIOFunc ioProc,
const char* procName, const char* caller )
{
XP_LOGF( TAG "%s(): installing proc %s from caller %s", __func__,
procName, caller );
WrapperState* state = g_malloc0( sizeof(*state) );
state->proc.ioProc = ioProc;
state->data = data;
state->caller = caller;
state->procName = procName;
GIOChannel* channel = g_io_channel_unix_new( socket );
guint watch = g_io_add_watch( channel,
G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI,
watch_wrapper, state );
g_io_channel_unref( channel ); /* only main loop holds it now */
XP_LOGF( TAG "%s(): added watch for %s", __func__, procName );
return watch;
}
void
gsw_logIdles()
{
XP_LOGF( TAG "%s(): %d idles pending", __func__, g_slist_length(s_idleProcs) );
}

37
xwords4/linux/gsrcwrap.h Normal file
View file

@ -0,0 +1,37 @@
/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
/*
* Copyright 2020 by Eric House (xwords@eehouse.org). All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _GSRCWRAP_H_
#define _GSRCWRAP_H_
guint _wrapIdle( GSourceFunc function, gpointer data, const char* procName,
const char* caller );
guint _wrapWatch( gpointer data, int socket, GIOFunc func,
const char* procName, const char* caller );
void gsw_logIdles();
#define ADD_ONETIME_IDLE( PROC, CLOSURE ) \
_wrapIdle( PROC, CLOSURE, #PROC, __func__ )
#define ADD_SOCKET( CLOSURE, SOCKET, PROC ) \
_wrapWatch( CLOSURE, SOCKET, PROC, #PROC, __func__ )
#endif

View file

@ -323,10 +323,11 @@ relay_status_gtk( void* closure, CommsRelayState state )
{ {
XP_LOGF( "%s got status: %s", __func__, CommsRelayState2Str(state) ); XP_LOGF( "%s got status: %s", __func__, CommsRelayState2Str(state) );
GtkGameGlobals* globals = (GtkGameGlobals*)closure; GtkGameGlobals* globals = (GtkGameGlobals*)closure;
if ( !!globals->draw ) { CommonGlobals* cGlobals = &globals->cGlobals;
globals->cGlobals.state = state; if ( !!cGlobals->draw ) {
cGlobals->state = state;
globals->stateChar = 'A' + COMMS_RELAYSTATE_ALLCONNECTED - state; globals->stateChar = 'A' + COMMS_RELAYSTATE_ALLCONNECTED - state;
draw_gtk_status( globals->draw, globals->stateChar ); draw_gtk_status( (GtkDrawCtx*)cGlobals->draw, globals->stateChar );
} }
} }
@ -419,10 +420,11 @@ relay_sendNoConn_gtk( const XP_U8* msg, XP_U16 len,
{ {
GtkGameGlobals* globals = (GtkGameGlobals*)closure; GtkGameGlobals* globals = (GtkGameGlobals*)closure;
XP_Bool success = XP_FALSE; XP_Bool success = XP_FALSE;
LaunchParams* params = globals->cGlobals.params; CommonGlobals* cGlobals = &globals->cGlobals;
if ( params->useUdp && !globals->draw ) { LaunchParams* params = cGlobals->params;
XP_U16 seed = comms_getChannelSeed( globals->cGlobals.game.comms ); if ( params->useUdp && !cGlobals->draw ) {
XP_U32 clientToken = makeClientToken( globals->cGlobals.selRow, seed ); XP_U16 seed = comms_getChannelSeed( cGlobals->game.comms );
XP_U32 clientToken = makeClientToken( cGlobals->rowid, seed );
XP_S16 nSent = relaycon_sendnoconn( params, msg, len, relayID, XP_S16 nSent = relaycon_sendnoconn( params, msg, len, relayID,
clientToken ); clientToken );
success = nSent == len; success = nSent == len;
@ -430,18 +432,6 @@ relay_sendNoConn_gtk( const XP_U8* msg, XP_U16 len,
return success; return success;
} /* relay_sendNoConn_gtk */ } /* relay_sendNoConn_gtk */
static void
tryConnectToServer(CommonGlobals* cGlobals)
{
LaunchParams* params = cGlobals->params;
XWStreamCtxt* stream =
mem_stream_make( MPPARM(cGlobals->util->mpool) params->vtMgr,
cGlobals, CHANNEL_NONE,
sendOnClose );
(void)server_initClientConnection( cGlobals->game.server,
stream );
}
#ifdef RELAY_VIA_HTTP #ifdef RELAY_VIA_HTTP
static void static void
onJoined( void* closure, const XP_UCHAR* connname, XWHostID hid ) onJoined( void* closure, const XP_UCHAR* connname, XWHostID hid )
@ -478,7 +468,7 @@ gtk_getFlags( void* closure )
XP_USE( globals ); XP_USE( globals );
return COMMS_XPORT_FLAGS_HASNOCONN; return COMMS_XPORT_FLAGS_HASNOCONN;
# else # else
return (!!globals->draw) ? COMMS_XPORT_FLAGS_NONE return (!!globals->cGlobals.draw) ? COMMS_XPORT_FLAGS_NONE
: COMMS_XPORT_FLAGS_HASNOCONN; : COMMS_XPORT_FLAGS_HASNOCONN;
# endif # endif
} }
@ -584,167 +574,25 @@ addDropChecks( GtkGameGlobals* globals )
static void static void
createOrLoadObjects( GtkGameGlobals* globals ) createOrLoadObjects( GtkGameGlobals* globals )
{ {
XWStreamCtxt* stream = NULL;
XP_Bool opened = XP_FALSE;
#ifndef XWFEATURE_STANDALONE_ONLY #ifndef XWFEATURE_STANDALONE_ONLY
#endif #endif
CommonGlobals* cGlobals = &globals->cGlobals; CommonGlobals* cGlobals = &globals->cGlobals;
LaunchParams* params = cGlobals->params; LaunchParams* params = cGlobals->params;
globals->draw = (GtkDrawCtx*)gtkDrawCtxtMake( globals->drawing_area, cGlobals->draw = gtkDrawCtxtMake( globals->drawing_area,
globals ); globals );
TransportProcs procs; TransportProcs procs;
setTransportProcs( &procs, globals ); setTransportProcs( &procs, globals );
linuxOpenGame( cGlobals, &procs );
if ( !!params->fileName && file_exists( params->fileName ) ) {
stream = streamFromFile( cGlobals, params->fileName );
#ifdef USE_SQLITE
} else if ( !!params->dbFileName && file_exists( params->dbFileName ) ) {
XP_UCHAR buf[32];
XP_SNPRINTF( buf, sizeof(buf), "%d", params->dbFileID );
mpool_setTag( MEMPOOL buf );
stream = streamFromDB( cGlobals );
#endif
} else if ( !!params->pDb && 0 <= cGlobals->selRow ) {
stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
params->vtMgr );
if ( !loadGame( stream, params->pDb, cGlobals->selRow ) ) {
stream_destroy( stream );
stream = NULL;
}
}
if ( !!stream ) {
if ( NULL == cGlobals->dict ) {
cGlobals->dict = makeDictForStream( cGlobals, stream );
}
opened = game_makeFromStream( MEMPOOL stream, &cGlobals->game,
cGlobals->gi, cGlobals->dict,
&cGlobals->dicts, cGlobals->util,
(DrawCtx*)globals->draw,
&cGlobals->cp, &procs );
XP_LOGF( "%s: loaded gi at %p", __func__, &cGlobals->gi );
stream_destroy( stream );
}
if ( !opened ) {
CommsAddrRec addr = cGlobals->addr;
/* XP_MEMSET( &addr, 0, sizeof(addr) ); */
/* addr.conType = cGlobals->addr.conType; */
#ifdef XWFEATURE_RELAY
/* if ( addr.conType == COMMS_CONN_RELAY ) { */
/* XP_ASSERT( !!params->connInfo.relay.relayName ); */
/* globals->cGlobals.defaultServerName */
/* = params->connInfo.relay.relayName; */
/* } */
#endif
game_makeNewGame( MEMPOOL &cGlobals->game, cGlobals->gi,
cGlobals->util, (DrawCtx*)globals->draw,
&cGlobals->cp, &procs
#ifdef SET_GAMESEED
, params->gameSeed
#endif
);
// addr.conType = params->conType;
CommsConnType typ;
for ( XP_U32 st = 0; addr_iter( &addr, &typ, &st ); ) {
if ( params->commsDisableds[typ][0] ) {
comms_setAddrDisabled( cGlobals->game.comms, typ, XP_FALSE, XP_TRUE );
}
if ( params->commsDisableds[typ][1] ) {
comms_setAddrDisabled( cGlobals->game.comms, typ, XP_TRUE, XP_TRUE );
}
switch( typ ) {
#ifdef XWFEATURE_RELAY
case COMMS_CONN_RELAY:
/* addr.u.ip_relay.ipAddr = 0; */
/* addr.u.ip_relay.port = params->connInfo.relay.defaultSendPort; */
/* addr.u.ip_relay.seeksPublicRoom = params->connInfo.relay.seeksPublicRoom; */
/* addr.u.ip_relay.advertiseRoom = params->connInfo.relay.advertiseRoom; */
/* XP_STRNCPY( addr.u.ip_relay.hostName, params->connInfo.relay.relayName, */
/* sizeof(addr.u.ip_relay.hostName) - 1 ); */
/* XP_STRNCPY( addr.u.ip_relay.invite, params->connInfo.relay.invite, */
/* sizeof(addr.u.ip_relay.invite) - 1 ); */
break;
#endif
#ifdef XWFEATURE_BLUETOOTH
case COMMS_CONN_BT:
XP_ASSERT( sizeof(addr.u.bt.btAddr)
>= sizeof(params->connInfo.bt.hostAddr));
XP_MEMCPY( &addr.u.bt.btAddr, &params->connInfo.bt.hostAddr,
sizeof(params->connInfo.bt.hostAddr) );
break;
#endif
#ifdef XWFEATURE_IP_DIRECT
case COMMS_CONN_IP_DIRECT:
XP_STRNCPY( addr.u.ip.hostName_ip, params->connInfo.ip.hostName,
sizeof(addr.u.ip.hostName_ip) - 1 );
addr.u.ip.port_ip = params->connInfo.ip.port;
break;
#endif
#ifdef XWFEATURE_SMS
case COMMS_CONN_SMS:
/* No! Don't overwrite what may be a return address with local
stuff */
/* XP_STRNCPY( addr.u.sms.phone, params->connInfo.sms.phone, */
/* sizeof(addr.u.sms.phone) - 1 ); */
/* addr.u.sms.port = params->connInfo.sms.port; */
break;
#endif
default:
break;
}
}
/* Need to save in order to have a valid selRow for the first send */
saveGame( cGlobals );
#ifndef XWFEATURE_STANDALONE_ONLY
/* This may trigger network activity */
if ( !!cGlobals->game.comms ) {
comms_setAddr( cGlobals->game.comms, &addr );
}
#endif
model_setDictionary( cGlobals->game.model, cGlobals->dict );
setSquareBonuses( cGlobals );
model_setPlayerDicts( cGlobals->game.model, &cGlobals->dicts );
#ifdef XWFEATURE_SEARCHLIMIT
cGlobals->gi->allowHintRect = params->allowHintRect;
#endif
if ( params->needsNewGame ) {
new_game_impl( globals, XP_FALSE );
#ifndef XWFEATURE_STANDALONE_ONLY
} else {
DeviceRole serverRole = cGlobals->gi->serverRole;
if ( serverRole == SERVER_ISCLIENT ) {
tryConnectToServer( cGlobals );
}
#endif
}
}
if ( !params->fileName && !!params->dbName ) { if ( !params->fileName && !!params->dbName ) {
XP_UCHAR buf[64]; XP_UCHAR buf[64];
snprintf( buf, sizeof(buf), "%s / %lld", params->dbName, snprintf( buf, sizeof(buf), "%s / %lld", params->dbName,
cGlobals->selRow ); cGlobals->rowid );
gtk_window_set_title( GTK_WINDOW(globals->window), buf ); gtk_window_set_title( GTK_WINDOW(globals->window), buf );
} }
#ifndef XWFEATURE_STANDALONE_ONLY
if ( !!globals->cGlobals.game.comms ) {
comms_start( globals->cGlobals.game.comms );
}
#endif
server_do( globals->cGlobals.game.server );
saveGame( cGlobals ); /* again, to include address etc. */
addDropChecks( globals ); addDropChecks( globals );
disenable_buttons( globals ); disenable_buttons( globals );
@ -758,11 +606,11 @@ configure_event( GtkWidget* widget, GdkEventConfigure* XP_UNUSED(event),
GtkGameGlobals* globals ) GtkGameGlobals* globals )
{ {
globals->gridOn = XP_TRUE; globals->gridOn = XP_TRUE;
if ( globals->draw == NULL ) { CommonGlobals* cGlobals = &globals->cGlobals;
if ( cGlobals->draw == NULL ) {
createOrLoadObjects( globals ); createOrLoadObjects( globals );
} }
CommonGlobals* cGlobals = &globals->cGlobals;
BoardCtxt* board = cGlobals->game.board; BoardCtxt* board = cGlobals->game.board;
GtkAllocation alloc; GtkAllocation alloc;
@ -796,7 +644,7 @@ destroy_board_window( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
if ( !!globals->cGlobals.game.comms ) { if ( !!globals->cGlobals.game.comms ) {
comms_stop( globals->cGlobals.game.comms ); comms_stop( globals->cGlobals.game.comms );
} }
saveGame( &globals->cGlobals ); linuxSaveGame( &globals->cGlobals );
windowDestroyed( globals ); windowDestroyed( globals );
} }
@ -814,7 +662,7 @@ on_board_window_shown( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
/* If it has pending invite info, send the invitation! */ /* If it has pending invite info, send the invitation! */
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool) XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
cGlobals->params->vtMgr ); cGlobals->params->vtMgr );
if ( loadInviteAddrs( stream, cGlobals->params->pDb, cGlobals->selRow ) ) { if ( loadInviteAddrs( stream, cGlobals->params->pDb, cGlobals->rowid ) ) {
CommsAddrRec addr = {0}; CommsAddrRec addr = {0};
addrFromStream( &addr, stream ); addrFromStream( &addr, stream );
comms_setAddr( cGlobals->game.comms, &addr ); comms_setAddr( cGlobals->game.comms, &addr );
@ -840,7 +688,7 @@ static void
cleanup( GtkGameGlobals* globals ) cleanup( GtkGameGlobals* globals )
{ {
CommonGlobals* cGlobals = &globals->cGlobals; CommonGlobals* cGlobals = &globals->cGlobals;
saveGame( cGlobals ); linuxSaveGame( cGlobals );
if ( 0 < globals->idleID ) { if ( 0 < globals->idleID ) {
g_source_remove( globals->idleID ); g_source_remove( globals->idleID );
} }
@ -955,7 +803,7 @@ new_game_impl( GtkGameGlobals* globals, XP_Bool fireConnDlg )
} }
CurGameInfo* gi = cGlobals->gi; CurGameInfo* gi = cGlobals->gi;
success = newGameDialog( globals, gi, &addr, XP_TRUE, fireConnDlg ); success = gtkNewGameDialog( globals, gi, &addr, XP_TRUE, fireConnDlg );
if ( success ) { if ( success ) {
#ifndef XWFEATURE_STANDALONE_ONLY #ifndef XWFEATURE_STANDALONE_ONLY
XP_Bool isClient = gi->serverRole == SERVER_ISCLIENT; XP_Bool isClient = gi->serverRole == SERVER_ISCLIENT;
@ -1022,7 +870,7 @@ game_info( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
/* Anything to do if OK is clicked? Changed names etc. already saved. Try /* Anything to do if OK is clicked? Changed names etc. already saved. Try
server_do in case one's become a robot. */ server_do in case one's become a robot. */
CurGameInfo* gi = globals->cGlobals.gi; CurGameInfo* gi = globals->cGlobals.gi;
if ( newGameDialog( globals, gi, &addr, XP_FALSE, XP_FALSE ) ) { if ( gtkNewGameDialog( globals, gi, &addr, XP_FALSE, XP_FALSE ) ) {
if ( server_do( globals->cGlobals.game.server ) ) { if ( server_do( globals->cGlobals.game.server ) ) {
board_draw( globals->cGlobals.game.board ); board_draw( globals->cGlobals.game.board );
} }
@ -1858,7 +1706,7 @@ static void
gtk_util_turnChanged( XW_UtilCtxt* uc, XP_S16 XP_UNUSED(newTurn) ) gtk_util_turnChanged( XW_UtilCtxt* uc, XP_S16 XP_UNUSED(newTurn) )
{ {
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure; GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
saveGame( &globals->cGlobals ); linuxSaveGame( &globals->cGlobals );
} }
#endif #endif
@ -1954,22 +1802,6 @@ gtk_util_informNetDict( XW_UtilCtxt* uc, XP_LangCode XP_UNUSED(lang),
} }
} }
static gint
changeRoles( gpointer data )
{
linuxChangeRoles( (CommonGlobals*)data );
return 0;
}
static void
gtk_util_setIsServer( XW_UtilCtxt* uc, XP_Bool isServer )
{
CommonGlobals* cGlobals = (CommonGlobals*)uc->closure;
linuxSetIsServer( cGlobals, isServer );
(void)g_idle_add( changeRoles, cGlobals );
}
/* define this to prevent user events during debugging from stopping the engine */ /* define this to prevent user events during debugging from stopping the engine */
/* #define DONT_ABORT_ENGINE */ /* #define DONT_ABORT_ENGINE */
@ -2334,14 +2166,6 @@ gtk_util_notifyMove( XW_UtilCtxt* uc, XWStreamCtxt* stream )
XP_ASSERT( len <= VSIZE(cGlobals->question) ); XP_ASSERT( len <= VSIZE(cGlobals->question) );
stream_getBytes( stream, cGlobals->question, len ); stream_getBytes( stream, cGlobals->question, len );
(void)g_idle_add( ask_move, globals ); (void)g_idle_add( ask_move, globals );
/* question = strFromStream( stream ); */
/* strcpy( cGlobals->question, question ); */
/* free( question ); */
/* /\*gint chosen = *\/gtkask( globals->window, question, buttons, NULL ); */
// result = GTK_RESPONSE_OK == chosen || chosen == GTK_RESPONSE_YES;
} /* gtk_util_userQuery */ } /* gtk_util_userQuery */
static gint static gint
@ -2501,52 +2325,53 @@ makeButtons( GtkGameGlobals* globals )
static void static void
setupGtkUtilCallbacks( GtkGameGlobals* globals, XW_UtilCtxt* util ) setupGtkUtilCallbacks( GtkGameGlobals* globals, XW_UtilCtxt* util )
{ {
util->vtable->m_util_userError = gtk_util_userError; util->closure = globals;
util->vtable->m_util_notifyMove = gtk_util_notifyMove; #define SET_PROC(NAM) util->vtable->m_util_##NAM = gtk_util_##NAM
util->vtable->m_util_notifyTrade = gtk_util_notifyTrade; SET_PROC(userError);
util->vtable->m_util_notifyPickTileBlank = gtk_util_notifyPickTileBlank; SET_PROC(notifyMove);
util->vtable->m_util_informNeedPickTiles = gtk_util_informNeedPickTiles; SET_PROC(notifyTrade);
util->vtable->m_util_informNeedPassword = gtk_util_informNeedPassword; SET_PROC(notifyPickTileBlank);
util->vtable->m_util_trayHiddenChange = gtk_util_trayHiddenChange; SET_PROC(informNeedPickTiles);
util->vtable->m_util_yOffsetChange = gtk_util_yOffsetChange; SET_PROC(informNeedPassword);
SET_PROC(trayHiddenChange);
SET_PROC(yOffsetChange);
#ifdef XWFEATURE_TURNCHANGENOTIFY #ifdef XWFEATURE_TURNCHANGENOTIFY
util->vtable->m_util_turnChanged = gtk_util_turnChanged; SET_PROC(turnChanged);
#endif #endif
util->vtable->m_util_informMove = gtk_util_informMove; SET_PROC(informMove);
util->vtable->m_util_informUndo = gtk_util_informUndo; SET_PROC(informUndo);
util->vtable->m_util_notifyGameOver = gtk_util_notifyGameOver; SET_PROC(notifyGameOver);
util->vtable->m_util_informNetDict = gtk_util_informNetDict; SET_PROC(informNetDict);
util->vtable->m_util_setIsServer = gtk_util_setIsServer; /* SET_PROC(setIsServer); */
#ifdef XWFEATURE_HILITECELL #ifdef XWFEATURE_HILITECELL
util->vtable->m_util_hiliteCell = gtk_util_hiliteCell; SET_PROC(hiliteCell);
#endif #endif
util->vtable->m_util_altKeyDown = gtk_util_altKeyDown; SET_PROC(altKeyDown);
util->vtable->m_util_engineProgressCallback = SET_PROC(engineProgressCallback);
gtk_util_engineProgressCallback; SET_PROC(setTimer);
util->vtable->m_util_setTimer = gtk_util_setTimer; SET_PROC(clearTimer);
util->vtable->m_util_clearTimer = gtk_util_clearTimer; SET_PROC(requestTime);
util->vtable->m_util_requestTime = gtk_util_requestTime; SET_PROC(notifyIllegalWords);
util->vtable->m_util_notifyIllegalWords = gtk_util_notifyIllegalWords; SET_PROC(remSelected);
util->vtable->m_util_remSelected = gtk_util_remSelected;
#ifndef XWFEATURE_STANDALONE_ONLY #ifndef XWFEATURE_STANDALONE_ONLY
util->vtable->m_util_makeStreamFromAddr = gtk_util_makeStreamFromAddr; SET_PROC(makeStreamFromAddr);
#endif #endif
#ifdef XWFEATURE_CHAT #ifdef XWFEATURE_CHAT
util->vtable->m_util_showChat = gtk_util_showChat; SET_PROC(showChat);
#endif #endif
#ifdef XWFEATURE_SEARCHLIMIT #ifdef XWFEATURE_SEARCHLIMIT
util->vtable->m_util_getTraySearchLimits = gtk_util_getTraySearchLimits; SET_PROC(getTraySearchLimits);
#endif #endif
#ifndef XWFEATURE_MINIWIN #ifndef XWFEATURE_MINIWIN
util->vtable->m_util_bonusSquareHeld = gtk_util_bonusSquareHeld; SET_PROC(bonusSquareHeld);
util->vtable->m_util_playerScoreHeld = gtk_util_playerScoreHeld; SET_PROC(playerScoreHeld);
#endif #endif
#ifdef XWFEATURE_BOARDWORDS #ifdef XWFEATURE_BOARDWORDS
util->vtable->m_util_cellSquareHeld = gtk_util_cellSquareHeld; SET_PROC(cellSquareHeld);
#endif #endif
#undef SET_PROC
util->closure = globals; assertAllCallbacksSet( util );
} /* setupGtkUtilCallbacks */ } /* setupGtkUtilCallbacks */
#ifndef XWFEATURE_STANDALONE_ONLY #ifndef XWFEATURE_STANDALONE_ONLY
@ -2556,33 +2381,6 @@ typedef struct _SockInfo {
int socket; int socket;
} SockInfo; } SockInfo;
static void
gtk_socket_added( void* closure, int newSock, GIOFunc proc )
{
GtkGameGlobals* globals = (GtkGameGlobals*)closure;
if ( newSock != -1 ) {
XP_ASSERT( !!proc );
GIOChannel* channel = g_io_channel_unix_new( newSock );
g_io_channel_set_close_on_unref( channel, TRUE );
#ifdef DEBUG
guint result =
#endif
g_io_add_watch( channel, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI,
proc, globals );
XP_LOGF( "g_io_add_watch(%d) => %d", newSock, result );
}
/* A hack for the bluetooth case. */
CommsCtxt* comms = globals->cGlobals.game.comms;
CommsAddrRec addr;
comms_getAddr( comms, &addr );
if ( (comms != NULL) && (addr_hasType( &addr, COMMS_CONN_BT) ) ) {
comms_resendAll( comms, COMMS_CONN_NONE, XP_FALSE );
}
LOG_RETURN_VOID();
} /* gtk_socket_changed */
static gboolean static gboolean
acceptorInput( GIOChannel* source, GIOCondition condition, gpointer data ) acceptorInput( GIOChannel* source, GIOCondition condition, gpointer data )
{ {
@ -2671,44 +2469,44 @@ initGlobalsNoDraw( GtkGameGlobals* globals, LaunchParams* params,
{ {
memset( globals, 0, sizeof(*globals) ); memset( globals, 0, sizeof(*globals) );
globals->cGlobals.gi = &globals->gi; CommonGlobals* cGlobals = &globals->cGlobals;
cGlobals->gi = &cGlobals->_gi;
if ( !gi ) { if ( !gi ) {
gi = &params->pgi; gi = &params->pgi;
} }
gi_copy( MPPARM(params->mpool) globals->cGlobals.gi, gi ); gi_copy( MPPARM(params->mpool) cGlobals->gi, gi );
globals->cGlobals.params = params; cGlobals->params = params;
globals->cGlobals.lastNTilesToUse = MAX_TRAY_TILES; cGlobals->lastNTilesToUse = MAX_TRAY_TILES;
#ifndef XWFEATURE_STANDALONE_ONLY #ifndef XWFEATURE_STANDALONE_ONLY
# ifdef XWFEATURE_RELAY # ifdef XWFEATURE_RELAY
globals->cGlobals.relaySocket = -1; cGlobals->relaySocket = -1;
# endif # endif
globals->cGlobals.socketAdded = gtk_socket_added; cGlobals->socketAddedClosure = globals;
globals->cGlobals.socketAddedClosure = globals; cGlobals->onSave = gtkOnGameSaved;
globals->cGlobals.onSave = onGameSaved; cGlobals->onSaveClosure = globals;
globals->cGlobals.onSaveClosure = globals; cGlobals->addAcceptor = gtk_socket_acceptor;
globals->cGlobals.addAcceptor = gtk_socket_acceptor;
#endif #endif
globals->cGlobals.cp.showBoardArrow = XP_TRUE; cGlobals->cp.showBoardArrow = XP_TRUE;
globals->cGlobals.cp.hideTileValues = params->hideValues; cGlobals->cp.hideTileValues = params->hideValues;
globals->cGlobals.cp.skipCommitConfirm = params->skipCommitConfirm; cGlobals->cp.skipCommitConfirm = params->skipCommitConfirm;
globals->cGlobals.cp.sortNewTiles = params->sortNewTiles; cGlobals->cp.sortNewTiles = params->sortNewTiles;
globals->cGlobals.cp.showColors = params->showColors; cGlobals->cp.showColors = params->showColors;
globals->cGlobals.cp.allowPeek = params->allowPeek; cGlobals->cp.allowPeek = params->allowPeek;
globals->cGlobals.cp.showRobotScores = params->showRobotScores; cGlobals->cp.showRobotScores = params->showRobotScores;
#ifdef XWFEATURE_SLOW_ROBOT #ifdef XWFEATURE_SLOW_ROBOT
globals->cGlobals.cp.robotThinkMin = params->robotThinkMin; cGlobals->cp.robotThinkMin = params->robotThinkMin;
globals->cGlobals.cp.robotThinkMax = params->robotThinkMax; cGlobals->cp.robotThinkMax = params->robotThinkMax;
globals->cGlobals.cp.robotTradePct = params->robotTradePct; cGlobals->cp.robotTradePct = params->robotTradePct;
#endif #endif
#ifdef XWFEATURE_CROSSHAIRS #ifdef XWFEATURE_CROSSHAIRS
globals->cGlobals.cp.hideCrosshairs = params->hideCrosshairs; cGlobals->cp.hideCrosshairs = params->hideCrosshairs;
#endif #endif
setupUtil( &globals->cGlobals ); setupUtil( cGlobals );
setupGtkUtilCallbacks( globals, globals->cGlobals.util ); setupGtkUtilCallbacks( globals, cGlobals->util );
} }
/* This gets called all the time, e.g. when the mouse moves across /* This gets called all the time, e.g. when the mouse moves across
@ -2727,9 +2525,10 @@ on_draw_event( GtkWidget* widget, cairo_t* cr, gpointer user_data )
/* } */ /* } */
GtkGameGlobals* globals = (GtkGameGlobals*)user_data; GtkGameGlobals* globals = (GtkGameGlobals*)user_data;
board_invalAll( globals->cGlobals.game.board ); CommonGlobals* cGlobals = &globals->cGlobals;
board_draw( globals->cGlobals.game.board ); board_invalAll( cGlobals->game.board );
draw_gtk_status( globals->draw, globals->stateChar ); board_draw( cGlobals->game.board );
draw_gtk_status( (GtkDrawCtx*)cGlobals->draw, globals->stateChar );
XP_USE(widget); XP_USE(widget);
XP_USE(cr); XP_USE(cr);
@ -2737,7 +2536,8 @@ on_draw_event( GtkWidget* widget, cairo_t* cr, gpointer user_data )
} }
void void
initGlobals( GtkGameGlobals* globals, LaunchParams* params, CurGameInfo* gi ) initBoardGlobalsGtk( GtkGameGlobals* globals, LaunchParams* params,
CurGameInfo* gi )
{ {
CommonGlobals* cGlobals = &globals->cGlobals; CommonGlobals* cGlobals = &globals->cGlobals;
short width, height; short width, height;
@ -2746,7 +2546,6 @@ initGlobals( GtkGameGlobals* globals, LaunchParams* params, CurGameInfo* gi )
GtkWidget* menubar; GtkWidget* menubar;
GtkWidget* vbox; GtkWidget* vbox;
GtkWidget* hbox; GtkWidget* hbox;
gulong id;
initGlobalsNoDraw( globals, params, gi ); initGlobalsNoDraw( globals, params, gi );
if ( !!gi ) { if ( !!gi ) {
@ -2765,12 +2564,18 @@ initGlobals( GtkGameGlobals* globals, LaunchParams* params, CurGameInfo* gi )
gtk_container_add( GTK_CONTAINER(window), vbox ); gtk_container_add( GTK_CONTAINER(window), vbox );
gtk_widget_show( vbox ); gtk_widget_show( vbox );
id = g_signal_connect( window, "destroy", G_CALLBACK(destroy_board_window), #ifdef DEBUG
globals ); gulong id =
#endif
g_signal_connect( window, "destroy", G_CALLBACK(destroy_board_window),
globals );
XP_ASSERT( id > 0 ); XP_ASSERT( id > 0 );
XP_ASSERT( !!globals ); XP_ASSERT( !!globals );
id = g_signal_connect( window, "show", G_CALLBACK( on_board_window_shown ), #ifdef DEBUG
globals ); id =
#endif
g_signal_connect( window, "show", G_CALLBACK( on_board_window_shown ),
globals );
XP_ASSERT( id > 0 ); XP_ASSERT( id > 0 );
menubar = makeMenus( globals ); menubar = makeMenus( globals );
@ -2786,8 +2591,11 @@ initGlobals( GtkGameGlobals* globals, LaunchParams* params, CurGameInfo* gi )
drawing_area = gtk_drawing_area_new(); drawing_area = gtk_drawing_area_new();
gtk_widget_add_events( drawing_area, GDK_ALL_EVENTS_MASK ); gtk_widget_add_events( drawing_area, GDK_ALL_EVENTS_MASK );
id = g_signal_connect(G_OBJECT(drawing_area), "draw", #ifdef DEBUG
G_CALLBACK(on_draw_event), globals); id =
#endif
g_signal_connect(G_OBJECT(drawing_area), "draw",
G_CALLBACK(on_draw_event), globals);
XP_ASSERT( id > 0 ); XP_ASSERT( id > 0 );
globals->drawing_area = drawing_area; globals->drawing_area = drawing_area;
@ -2815,8 +2623,11 @@ initGlobals( GtkGameGlobals* globals, LaunchParams* params, CurGameInfo* gi )
gtk_adjustment_new( 0, 0, nRows, 1, 2, gtk_adjustment_new( 0, 0, nRows, 1, 2,
nRows - params->nHidden ); nRows - params->nHidden );
vscrollbar = gtk_scrollbar_new( GTK_ORIENTATION_VERTICAL, globals->adjustment ); vscrollbar = gtk_scrollbar_new( GTK_ORIENTATION_VERTICAL, globals->adjustment );
id = g_signal_connect( globals->adjustment, "value_changed", #ifdef DEBUG
G_CALLBACK(scroll_value_changed), globals ); id =
#endif
g_signal_connect( globals->adjustment, "value_changed",
G_CALLBACK(scroll_value_changed), globals );
XP_ASSERT( id > 0 ); XP_ASSERT( id > 0 );
gtk_widget_show( vscrollbar ); gtk_widget_show( vscrollbar );
gtk_box_pack_start( GTK_BOX(hbox), vscrollbar, FALSE, FALSE, 0 ); gtk_box_pack_start( GTK_BOX(hbox), vscrollbar, FALSE, FALSE, 0 );
@ -2831,18 +2642,29 @@ initGlobals( GtkGameGlobals* globals, LaunchParams* params, CurGameInfo* gi )
GtkWidget* label = globals->countLabel = gtk_label_new( "" ); GtkWidget* label = globals->countLabel = gtk_label_new( "" );
gtk_box_pack_start( GTK_BOX(vbox), label, TRUE, TRUE, 0); gtk_box_pack_start( GTK_BOX(vbox), label, TRUE, TRUE, 0);
gtk_widget_show( label ); gtk_widget_show( label );
#ifdef DEBUG
id = g_signal_connect( drawing_area, "configure-event", id =
G_CALLBACK(configure_event), globals ); #endif
g_signal_connect( drawing_area, "configure-event",
G_CALLBACK(configure_event), globals );
XP_ASSERT( id > 0 ); XP_ASSERT( id > 0 );
id = g_signal_connect( drawing_area, "button_press_event", #ifdef DEBUG
G_CALLBACK(button_press_event), globals ); id =
#endif
g_signal_connect( drawing_area, "button_press_event",
G_CALLBACK(button_press_event), globals );
XP_ASSERT( id > 0 ); XP_ASSERT( id > 0 );
id = g_signal_connect( drawing_area, "motion_notify_event", #ifdef DEBUG
G_CALLBACK(motion_notify_event), globals ); id =
#endif
g_signal_connect( drawing_area, "motion_notify_event",
G_CALLBACK(motion_notify_event), globals );
XP_ASSERT( id > 0 ); XP_ASSERT( id > 0 );
id = g_signal_connect( drawing_area, "button_release_event", #ifdef DEBUG
G_CALLBACK(button_release_event), globals ); id =
#endif
g_signal_connect( drawing_area, "button_release_event",
G_CALLBACK(button_release_event), globals );
XP_ASSERT( id > 0 ); XP_ASSERT( id > 0 );
setOneSecondTimer( cGlobals ); setOneSecondTimer( cGlobals );
@ -2889,7 +2711,7 @@ loadGameNoDraw( GtkGameGlobals* globals, LaunchParams* params,
setTransportProcs( &procs, globals ); setTransportProcs( &procs, globals );
CommonGlobals* cGlobals = &globals->cGlobals; CommonGlobals* cGlobals = &globals->cGlobals;
cGlobals->selRow = rowid; cGlobals->rowid = rowid;
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool) XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
params->vtMgr ); params->vtMgr );
XP_Bool loaded = loadGame( stream, pDb, rowid ); XP_Bool loaded = loadGame( stream, pDb, rowid );
@ -2924,7 +2746,7 @@ makeNewGame( GtkGameGlobals* globals )
if ( !!cGlobals->game.comms ) { if ( !!cGlobals->game.comms ) {
comms_getAddr( cGlobals->game.comms, &cGlobals->addr ); comms_getAddr( cGlobals->game.comms, &cGlobals->addr );
} else { } else {
LaunchParams* params = globals->cGlobals.params; LaunchParams* params = cGlobals->params;
const XP_UCHAR* relayName = params->connInfo.relay.relayName; const XP_UCHAR* relayName = params->connInfo.relay.relayName;
if ( !relayName ) { if ( !relayName ) {
relayName = RELAY_NAME_DEFAULT; relayName = RELAY_NAME_DEFAULT;
@ -2937,8 +2759,8 @@ makeNewGame( GtkGameGlobals* globals )
} }
CurGameInfo* gi = cGlobals->gi; CurGameInfo* gi = cGlobals->gi;
XP_Bool success = newGameDialog( globals, gi, &cGlobals->addr, XP_Bool success = gtkNewGameDialog( globals, gi, &cGlobals->addr,
XP_TRUE, XP_FALSE ); XP_TRUE, XP_FALSE );
if ( success && !!gi->dictName && !cGlobals->dict ) { if ( success && !!gi->dictName && !cGlobals->dict ) {
cGlobals->dict = cGlobals->dict =
linux_dictionary_make( MEMPOOL cGlobals->params, linux_dictionary_make( MEMPOOL cGlobals->params,

View file

@ -94,9 +94,7 @@ typedef struct _DropTypeData {
typedef struct GtkGameGlobals { typedef struct GtkGameGlobals {
CommonGlobals cGlobals; CommonGlobals cGlobals;
CurGameInfo gi;
GtkWidget* window; GtkWidget* window;
GtkDrawCtx* draw;
GtkAppGlobals* apg; GtkAppGlobals* apg;
/* GdkPixmap* pixmap; */ /* GdkPixmap* pixmap; */
GtkWidget* drawing_area; GtkWidget* drawing_area;
@ -184,8 +182,8 @@ typedef struct GtkGameGlobals {
#define GTK_BOTTOM_MARGIN GTK_TOP_MARGIN #define GTK_BOTTOM_MARGIN GTK_TOP_MARGIN
#define GTK_RIGHT_MARGIN GTK_BOARD_LEFT_MARGIN #define GTK_RIGHT_MARGIN GTK_BOARD_LEFT_MARGIN
void initGlobals( GtkGameGlobals* globals, LaunchParams* params, void initBoardGlobalsGtk( GtkGameGlobals* globals, LaunchParams* params,
CurGameInfo* gi ); CurGameInfo* gi );
void freeGlobals( GtkGameGlobals* globals ); void freeGlobals( GtkGameGlobals* globals );
XP_Bool makeNewGame( GtkGameGlobals* globals ); XP_Bool makeNewGame( GtkGameGlobals* globals );
XP_Bool loadGameNoDraw( GtkGameGlobals* globals, LaunchParams* params, XP_Bool loadGameNoDraw( GtkGameGlobals* globals, LaunchParams* params,

View file

@ -1531,7 +1531,6 @@ removeSurface( GtkDrawCtx* dctx )
dctx->surface = NULL; dctx->surface = NULL;
} }
#ifdef DEBUG
static cairo_status_t static cairo_status_t
write_func( void *closure, const unsigned char *data, write_func( void *closure, const unsigned char *data,
unsigned int length ) unsigned int length )
@ -1540,21 +1539,18 @@ write_func( void *closure, const unsigned char *data,
stream_putBytes( stream, data, length ); stream_putBytes( stream, data, length );
return CAIRO_STATUS_SUCCESS; return CAIRO_STATUS_SUCCESS;
} }
#endif
void void
getImage( GtkDrawCtx* XP_UNUSED_DBG(dctx), XWStreamCtxt* XP_UNUSED_DBG(stream) ) getImage( GtkDrawCtx* dctx, XWStreamCtxt* stream )
{ {
LOG_FUNC(); LOG_FUNC();
XP_ASSERT( !!dctx->surface ); XP_ASSERT( !!dctx->surface );
#ifdef DEBUG #ifdef DEBUG
cairo_status_t status = cairo_status_t status =
#endif
cairo_surface_write_to_png_stream( dctx->surface, cairo_surface_write_to_png_stream( dctx->surface,
write_func, stream ); write_func, stream );
XP_ASSERT( CAIRO_STATUS_SUCCESS == status ); XP_ASSERT( CAIRO_STATUS_SUCCESS == status );
#else
error Will Robinson
#endif
} }
void void

View file

@ -36,18 +36,19 @@
static void onNewData( GtkAppGlobals* apg, sqlite3_int64 rowid, static void onNewData( GtkAppGlobals* apg, sqlite3_int64 rowid,
XP_Bool isNew ); XP_Bool isNew );
static void updateButtons( GtkAppGlobals* apg ); static void updateButtons( GtkAppGlobals* apg );
static void open_row( GtkAppGlobals* apg, sqlite3_int64 row, XP_Bool isNew );
static void static void
recordOpened( GtkAppGlobals* apg, GtkGameGlobals* globals ) recordOpened( GtkAppGlobals* apg, GtkGameGlobals* globals )
{ {
apg->globalsList = g_slist_prepend( apg->globalsList, globals ); apg->cag.globalsList = g_slist_prepend( apg->cag.globalsList, globals );
globals->apg = apg; globals->apg = apg;
} }
static void static void
recordClosed( GtkAppGlobals* apg, GtkGameGlobals* globals ) recordClosed( GtkAppGlobals* apg, GtkGameGlobals* globals )
{ {
apg->globalsList = g_slist_remove( apg->globalsList, globals ); apg->cag.globalsList = g_slist_remove( apg->cag.globalsList, globals );
} }
static XP_Bool static XP_Bool
@ -55,9 +56,9 @@ gameIsOpen( GtkAppGlobals* apg, sqlite3_int64 rowid )
{ {
XP_Bool found = XP_FALSE; XP_Bool found = XP_FALSE;
GSList* iter; GSList* iter;
for ( iter = apg->globalsList; !!iter && !found; iter = iter->next ) { for ( iter = apg->cag.globalsList; !!iter && !found; iter = iter->next ) {
GtkGameGlobals* globals = (GtkGameGlobals*)iter->data; GtkGameGlobals* globals = (GtkGameGlobals*)iter->data;
found = globals->cGlobals.selRow == rowid; found = globals->cGlobals.rowid == rowid;
} }
return found; return found;
} }
@ -67,10 +68,10 @@ findOpenGame( const GtkAppGlobals* apg, sqlite3_int64 rowid )
{ {
GtkGameGlobals* result = NULL; GtkGameGlobals* result = NULL;
GSList* iter; GSList* iter;
for ( iter = apg->globalsList; !!iter; iter = iter->next ) { for ( iter = apg->cag.globalsList; !!iter; iter = iter->next ) {
GtkGameGlobals* globals = (GtkGameGlobals*)iter->data; GtkGameGlobals* globals = (GtkGameGlobals*)iter->data;
CommonGlobals* cGlobals = &globals->cGlobals; CommonGlobals* cGlobals = &globals->cGlobals;
if ( cGlobals->selRow == rowid ) { if ( cGlobals->rowid == rowid ) {
result = globals; result = globals;
break; break;
} }
@ -78,7 +79,7 @@ findOpenGame( const GtkAppGlobals* apg, sqlite3_int64 rowid )
return result; return result;
} }
enum { ROW_ITEM, ROW_THUMB, NAME_ITEM, ROOM_ITEM, GAMEID_ITEM, SEED_ITEM, enum { ROW_ITEM, ROW_THUMB, NAME_ITEM, ROOM_ITEM, GAMEID_ITEM, SEED_ITEM, ROLE_ITEM,
CONN_ITEM, RELAYID_ITEM, OVER_ITEM, TURN_ITEM, LOCAL_ITEM, NMOVES_ITEM, NTOTAL_ITEM, CONN_ITEM, RELAYID_ITEM, OVER_ITEM, TURN_ITEM, LOCAL_ITEM, NMOVES_ITEM, NTOTAL_ITEM,
MISSING_ITEM, LASTTURN_ITEM, N_ITEMS }; MISSING_ITEM, LASTTURN_ITEM, N_ITEMS };
@ -169,6 +170,7 @@ init_games_list( GtkAppGlobals* apg )
addTextColumn( list, "Room", ROOM_ITEM ); addTextColumn( list, "Room", ROOM_ITEM );
addTextColumn( list, "GameID", GAMEID_ITEM ); addTextColumn( list, "GameID", GAMEID_ITEM );
addTextColumn( list, "Seed", SEED_ITEM ); addTextColumn( list, "Seed", SEED_ITEM );
addTextColumn( list, "Role", ROLE_ITEM );
addTextColumn( list, "Conn. via", CONN_ITEM ); addTextColumn( list, "Conn. via", CONN_ITEM );
addTextColumn( list, "RelayID", RELAYID_ITEM ); addTextColumn( list, "RelayID", RELAYID_ITEM );
addTextColumn( list, "Ended", OVER_ITEM ); addTextColumn( list, "Ended", OVER_ITEM );
@ -186,6 +188,7 @@ init_games_list( GtkAppGlobals* apg )
G_TYPE_STRING, /* ROOM_ITEM */ G_TYPE_STRING, /* ROOM_ITEM */
G_TYPE_INT, /* GAMEID_ITEM */ G_TYPE_INT, /* GAMEID_ITEM */
G_TYPE_INT, /* SEED_ITEM */ G_TYPE_INT, /* SEED_ITEM */
G_TYPE_INT, /* ROLE_ITEM */
G_TYPE_STRING, /* CONN_ITEM */ G_TYPE_STRING, /* CONN_ITEM */
G_TYPE_STRING, /*RELAYID_ITEM */ G_TYPE_STRING, /*RELAYID_ITEM */
G_TYPE_BOOLEAN, /* OVER_ITEM */ G_TYPE_BOOLEAN, /* OVER_ITEM */
@ -247,6 +250,7 @@ add_to_list( GtkWidget* list, sqlite3_int64 rowid, XP_Bool isNew,
ROOM_ITEM, gib->room, ROOM_ITEM, gib->room,
GAMEID_ITEM, gib->gameID, GAMEID_ITEM, gib->gameID,
SEED_ITEM, gib->seed, SEED_ITEM, gib->seed,
ROLE_ITEM, gib->role,
CONN_ITEM, gib->conn, CONN_ITEM, gib->conn,
RELAYID_ITEM, gib->relayID, RELAYID_ITEM, gib->relayID,
TURN_ITEM, gib->turn, TURN_ITEM, gib->turn,
@ -275,20 +279,20 @@ handle_newgame_button( GtkWidget* XP_UNUSED(widget), void* closure )
{ {
GtkAppGlobals* apg = (GtkAppGlobals*)closure; GtkAppGlobals* apg = (GtkAppGlobals*)closure;
XP_LOGF( "%s called", __func__ ); XP_LOGF( "%s called", __func__ );
GtkGameGlobals* globals = malloc( sizeof(*globals) ); GtkGameGlobals* globals = calloc( 1, sizeof(*globals) );
apg->params->needsNewGame = XP_FALSE; apg->cag.params->needsNewGame = XP_FALSE;
initGlobals( globals, apg->params, NULL ); initBoardGlobalsGtk( globals, apg->cag.params, NULL );
if ( !makeNewGame( globals ) ) { if ( !makeNewGame( globals ) ) {
freeGlobals( globals ); freeGlobals( globals );
} else { } else {
GtkWidget* gameWindow = globals->window; GtkWidget* gameWindow = globals->window;
globals->cGlobals.selRow = -1; globals->cGlobals.rowid = -1;
recordOpened( apg, globals ); recordOpened( apg, globals );
gtk_widget_show( gameWindow ); gtk_widget_show( gameWindow );
} }
} }
void static void
open_row( GtkAppGlobals* apg, sqlite3_int64 row, XP_Bool isNew ) open_row( GtkAppGlobals* apg, sqlite3_int64 row, XP_Bool isNew )
{ {
if ( -1 != row && !gameIsOpen( apg, row ) ) { if ( -1 != row && !gameIsOpen( apg, row ) ) {
@ -296,10 +300,10 @@ open_row( GtkAppGlobals* apg, sqlite3_int64 row, XP_Bool isNew )
onNewData( apg, row, XP_TRUE ); onNewData( apg, row, XP_TRUE );
} }
apg->params->needsNewGame = XP_FALSE; apg->cag.params->needsNewGame = XP_FALSE;
GtkGameGlobals* globals = malloc( sizeof(*globals) ); GtkGameGlobals* globals = malloc( sizeof(*globals) );
initGlobals( globals, apg->params, NULL ); initBoardGlobalsGtk( globals, apg->cag.params, NULL );
globals->cGlobals.selRow = row; globals->cGlobals.rowid = row;
recordOpened( apg, globals ); recordOpened( apg, globals );
gtk_widget_show( globals->window ); gtk_widget_show( globals->window );
} }
@ -320,7 +324,7 @@ handle_open_button( GtkWidget* XP_UNUSED(widget), void* closure )
void void
make_rematch( GtkAppGlobals* apg, const CommonGlobals* cGlobals ) make_rematch( GtkAppGlobals* apg, const CommonGlobals* cGlobals )
{ {
LaunchParams* params = apg->params; LaunchParams* params = apg->cag.params;
XP_ASSERT( params == cGlobals->params ); XP_ASSERT( params == cGlobals->params );
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool) XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
params->vtMgr ); params->vtMgr );
@ -383,7 +387,7 @@ handle_rematch_button( GtkWidget* XP_UNUSED(widget), void* closure )
for ( int ii = 0; ii < selRows->len; ++ii ) { for ( int ii = 0; ii < selRows->len; ++ii ) {
sqlite3_int64 rowid = g_array_index( selRows, sqlite3_int64, ii ); sqlite3_int64 rowid = g_array_index( selRows, sqlite3_int64, ii );
GtkGameGlobals tmpGlobals; GtkGameGlobals tmpGlobals;
if ( loadGameNoDraw( &tmpGlobals, apg->params, rowid ) ) { if ( loadGameNoDraw( &tmpGlobals, apg->cag.params, rowid ) ) {
make_rematch( apg, &tmpGlobals.cGlobals ); make_rematch( apg, &tmpGlobals.cGlobals );
} }
freeGlobals( &tmpGlobals ); freeGlobals( &tmpGlobals );
@ -394,7 +398,7 @@ static void
handle_delete_button( GtkWidget* XP_UNUSED(widget), void* closure ) handle_delete_button( GtkWidget* XP_UNUSED(widget), void* closure )
{ {
GtkAppGlobals* apg = (GtkAppGlobals*)closure; GtkAppGlobals* apg = (GtkAppGlobals*)closure;
LaunchParams* params = apg->params; LaunchParams* params = apg->cag.params;
guint len = apg->selRows->len; guint len = apg->selRows->len;
for ( guint ii = 0; ii < len; ++ii ) { for ( guint ii = 0; ii < len; ++ii ) {
sqlite3_int64 rowid = g_array_index( apg->selRows, sqlite3_int64, ii ); sqlite3_int64 rowid = g_array_index( apg->selRows, sqlite3_int64, ii );
@ -429,18 +433,18 @@ handle_destroy( GtkWidget* XP_UNUSED(widget), gpointer data )
LOG_FUNC(); LOG_FUNC();
GtkAppGlobals* apg = (GtkAppGlobals*)data; GtkAppGlobals* apg = (GtkAppGlobals*)data;
GSList* iter; GSList* iter;
for ( iter = apg->globalsList; !!iter; iter = iter->next ) { for ( iter = apg->cag.globalsList; !!iter; iter = iter->next ) {
GtkGameGlobals* globals = (GtkGameGlobals*)iter->data; GtkGameGlobals* globals = (GtkGameGlobals*)iter->data;
destroy_board_window( NULL, globals ); destroy_board_window( NULL, globals );
// freeGlobals( globals ); // freeGlobals( globals );
} }
g_slist_free( apg->globalsList ); g_slist_free( apg->cag.globalsList );
gchar buf[64]; gchar buf[64];
sprintf( buf, "%d:%d:%d:%d", apg->lastConfigure.x, sprintf( buf, "%d:%d:%d:%d", apg->lastConfigure.x,
apg->lastConfigure.y, apg->lastConfigure.width, apg->lastConfigure.y, apg->lastConfigure.width,
apg->lastConfigure.height ); apg->lastConfigure.height );
db_store( apg->params->pDb, KEY_WIN_LOC, buf ); db_store( apg->cag.params->pDb, KEY_WIN_LOC, buf );
gtk_main_quit(); gtk_main_quit();
} }
@ -477,7 +481,7 @@ static void
setWindowTitle( GtkAppGlobals* apg ) setWindowTitle( GtkAppGlobals* apg )
{ {
GtkWidget* window = apg->window; GtkWidget* window = apg->window;
LaunchParams* params = apg->params; LaunchParams* params = apg->cag.params;
gchar title[128] = {0}; gchar title[128] = {0};
if ( !!params->dbName ) { if ( !!params->dbName ) {
@ -505,7 +509,7 @@ trySetWinConfig( GtkAppGlobals* apg )
int height = 400; int height = 400;
gchar buf[64]; gchar buf[64];
if ( db_fetch_safe( apg->params->pDb, KEY_WIN_LOC, buf, sizeof(buf)) ) { if ( db_fetch_safe( apg->cag.params->pDb, KEY_WIN_LOC, buf, sizeof(buf)) ) {
sscanf( buf, "%d:%d:%d:%d", &xx, &yy, &width, &height ); sscanf( buf, "%d:%d:%d:%d", &xx, &yy, &width, &height );
} }
@ -516,14 +520,14 @@ trySetWinConfig( GtkAppGlobals* apg )
static void static void
handle_movescheck( GtkWidget* XP_UNUSED(widget), GtkAppGlobals* apg ) handle_movescheck( GtkWidget* XP_UNUSED(widget), GtkAppGlobals* apg )
{ {
LaunchParams* params = apg->params; LaunchParams* params = apg->cag.params;
relaycon_checkMsgs( params ); relaycon_checkMsgs( params );
} }
static void static void
handle_relayid_to_clip( GtkWidget* XP_UNUSED(widget), GtkAppGlobals* apg ) handle_relayid_to_clip( GtkWidget* XP_UNUSED(widget), GtkAppGlobals* apg )
{ {
LaunchParams* params = apg->params; LaunchParams* params = apg->cag.params;
XP_U32 relayID = linux_getDevIDRelay( params ); XP_U32 relayID = linux_getDevIDRelay( params );
gchar str[32]; gchar str[32];
snprintf( &str[0], VSIZE(str), "%d", relayID ); snprintf( &str[0], VSIZE(str), "%d", relayID );
@ -535,7 +539,7 @@ static void
makeGamesWindow( GtkAppGlobals* apg ) makeGamesWindow( GtkAppGlobals* apg )
{ {
GtkWidget* window; GtkWidget* window;
LaunchParams* params = apg->params; LaunchParams* params = apg->cag.params;
apg->window = window = gtk_window_new( GTK_WINDOW_TOPLEVEL ); apg->window = window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
g_signal_connect( G_OBJECT(window), "destroy", g_signal_connect( G_OBJECT(window), "destroy",
@ -602,7 +606,7 @@ static GtkWidget*
openDBFile( GtkAppGlobals* apg ) openDBFile( GtkAppGlobals* apg )
{ {
GtkGameGlobals* globals = malloc( sizeof(*globals) ); GtkGameGlobals* globals = malloc( sizeof(*globals) );
initGlobals( globals, apg->params, NULL ); initBoardGlobalsGtk( globals, apg->cag.params, NULL );
GtkWidget* window = globals->window; GtkWidget* window = globals->window;
gtk_widget_show( window ); gtk_widget_show( window );
@ -633,7 +637,7 @@ static void
onNewData( GtkAppGlobals* apg, sqlite3_int64 rowid, XP_Bool isNew ) onNewData( GtkAppGlobals* apg, sqlite3_int64 rowid, XP_Bool isNew )
{ {
GameInfo gib; GameInfo gib;
if ( getGameInfo( apg->params->pDb, rowid, &gib ) ) { if ( getGameInfo( apg->cag.params->pDb, rowid, &gib ) ) {
add_to_list( apg->listWidget, rowid, isNew, &gib ); add_to_list( apg->listWidget, rowid, isNew, &gib );
g_object_unref( gib.snap ); g_object_unref( gib.snap );
} }
@ -651,31 +655,22 @@ feedBufferGTK( GtkAppGlobals* apg, sqlite3_int64 rowid,
seed = comms_getChannelSeed( globals->cGlobals.game.comms ); seed = comms_getChannelSeed( globals->cGlobals.game.comms );
} else { } else {
GtkGameGlobals tmpGlobals; GtkGameGlobals tmpGlobals;
if ( loadGameNoDraw( &tmpGlobals, apg->params, rowid ) ) { if ( loadGameNoDraw( &tmpGlobals, apg->cag.params, rowid ) ) {
gameGotBuf( &tmpGlobals.cGlobals, XP_FALSE, buf, len, from ); gameGotBuf( &tmpGlobals.cGlobals, XP_FALSE, buf, len, from );
seed = comms_getChannelSeed( tmpGlobals.cGlobals.game.comms ); seed = comms_getChannelSeed( tmpGlobals.cGlobals.game.comms );
saveGame( &tmpGlobals.cGlobals ); linuxSaveGame( &tmpGlobals.cGlobals );
} }
freeGlobals( &tmpGlobals ); freeGlobals( &tmpGlobals );
} }
return seed; return seed;
} }
static void
gtkSocketAdded( void* closure, int newSock, GIOFunc proc )
{
GIOChannel* channel = g_io_channel_unix_new( newSock );
(void)g_io_add_watch( channel, G_IO_IN | G_IO_ERR, proc, closure );
LOG_RETURN_VOID();
} /* gtk_socket_changed */
/* Stuff common to receiving invitations */ /* Stuff common to receiving invitations */
static void static void
gameFromInvite( GtkAppGlobals* apg, const NetLaunchInfo* invite, gameFromInvite( GtkAppGlobals* apg, const NetLaunchInfo* invite,
const CommsAddrRec* returnAddr ) const CommsAddrRec* returnAddr )
{ {
LaunchParams* params = apg->params; LaunchParams* params = apg->cag.params;
CurGameInfo gi = {0}; CurGameInfo gi = {0};
gi_copy( MPPARM(params->mpool) &gi, &params->pgi ); gi_copy( MPPARM(params->mpool) &gi, &params->pgi );
@ -688,7 +683,7 @@ gameFromInvite( GtkAppGlobals* apg, const NetLaunchInfo* invite,
GtkGameGlobals* globals = malloc( sizeof(*globals) ); GtkGameGlobals* globals = malloc( sizeof(*globals) );
params->needsNewGame = XP_FALSE; params->needsNewGame = XP_FALSE;
initGlobals( globals, params, &gi ); initBoardGlobalsGtk( globals, params, &gi );
if ( !!returnAddr ) { if ( !!returnAddr ) {
globals->cGlobals.addr = *returnAddr; globals->cGlobals.addr = *returnAddr;
@ -697,7 +692,7 @@ gameFromInvite( GtkAppGlobals* apg, const NetLaunchInfo* invite,
} }
GtkWidget* gameWindow = globals->window; GtkWidget* gameWindow = globals->window;
globals->cGlobals.selRow = -1; globals->cGlobals.rowid = -1;
recordOpened( apg, globals ); recordOpened( apg, globals );
gtk_widget_show( gameWindow ); gtk_widget_show( gameWindow );
@ -712,7 +707,7 @@ relayInviteReceived( void* closure, NetLaunchInfo* invite )
XP_U32 gameID = invite->gameID; XP_U32 gameID = invite->gameID;
sqlite3_int64 rowids[1]; sqlite3_int64 rowids[1];
int nRowIDs = VSIZE(rowids); int nRowIDs = VSIZE(rowids);
getRowsForGameID( apg->params->pDb, gameID, rowids, &nRowIDs ); getRowsForGameID( apg->cag.params->pDb, gameID, rowids, &nRowIDs );
if ( 0 < nRowIDs ) { if ( 0 < nRowIDs ) {
gtktell( apg->window, "Duplicate invite rejected" ); gtktell( apg->window, "Duplicate invite rejected" );
@ -748,7 +743,7 @@ gtkGotMsgForRow( void* closure, const CommsAddrRec* from,
{ {
XP_LOGF( "%s(): got msg of len %d for row %lld", __func__, len, rowid ); XP_LOGF( "%s(): got msg of len %d for row %lld", __func__, len, rowid );
GtkAppGlobals* apg = (GtkAppGlobals*)closure; GtkAppGlobals* apg = (GtkAppGlobals*)closure;
// LaunchParams* params = apg->params; // LaunchParams* params = apg->cag.params;
(void)feedBufferGTK( apg, rowid, buf, len, from ); (void)feedBufferGTK( apg, rowid, buf, len, from );
LOG_RETURN_VOID(); LOG_RETURN_VOID();
} }
@ -758,9 +753,9 @@ requestMsgs( gpointer data )
{ {
GtkAppGlobals* apg = (GtkAppGlobals*)data; GtkAppGlobals* apg = (GtkAppGlobals*)data;
XP_UCHAR devIDBuf[64] = {0}; XP_UCHAR devIDBuf[64] = {0};
db_fetch_safe( apg->params->pDb, KEY_RDEVID, devIDBuf, sizeof(devIDBuf) ); db_fetch_safe( apg->cag.params->pDb, KEY_RDEVID, devIDBuf, sizeof(devIDBuf) );
if ( '\0' != devIDBuf[0] ) { if ( '\0' != devIDBuf[0] ) {
relaycon_requestMsgs( apg->params, devIDBuf ); relaycon_requestMsgs( apg->cag.params, devIDBuf );
} else { } else {
XP_LOGF( "%s: not requesting messages as don't have relay id", __func__ ); XP_LOGF( "%s: not requesting messages as don't have relay id", __func__ );
} }
@ -794,7 +789,7 @@ smsMsgReceivedGTK( void* closure, const CommsAddrRec* from, XP_U32 gameID,
{ {
LOG_FUNC(); LOG_FUNC();
GtkAppGlobals* apg = (GtkAppGlobals*)closure; GtkAppGlobals* apg = (GtkAppGlobals*)closure;
LaunchParams* params = apg->params; LaunchParams* params = apg->cag.params;
sqlite3_int64 rowids[4]; sqlite3_int64 rowids[4];
int nRowIDs = VSIZE(rowids); int nRowIDs = VSIZE(rowids);
@ -817,7 +812,7 @@ static void
gtkDevIDReceived( void* closure, const XP_UCHAR* devID, XP_U16 maxInterval ) gtkDevIDReceived( void* closure, const XP_UCHAR* devID, XP_U16 maxInterval )
{ {
GtkAppGlobals* apg = (GtkAppGlobals*)closure; GtkAppGlobals* apg = (GtkAppGlobals*)closure;
LaunchParams* params = apg->params; LaunchParams* params = apg->cag.params;
if ( !!devID ) { if ( !!devID ) {
XP_LOGF( "%s(devID=%s)", __func__, devID ); XP_LOGF( "%s(devID=%s)", __func__, devID );
db_store( params->pDb, KEY_RDEVID, devID ); db_store( params->pDb, KEY_RDEVID, devID );
@ -842,8 +837,7 @@ gtkErrorMsgRcvd( void* closure, const XP_UCHAR* msg )
} }
void void
onGameSaved( void* closure, sqlite3_int64 rowid, gtkOnGameSaved( void* closure, sqlite3_int64 rowid, XP_Bool firstTime )
XP_Bool firstTime )
{ {
GtkGameGlobals* globals = (GtkGameGlobals*)closure; GtkGameGlobals* globals = (GtkGameGlobals*)closure;
GtkAppGlobals* apg = globals->apg; GtkAppGlobals* apg = globals->apg;
@ -874,10 +868,10 @@ gtkmain( LaunchParams* params )
sigaction( SIGTERM, &act, NULL ); sigaction( SIGTERM, &act, NULL );
apg.selRows = g_array_new( FALSE, FALSE, sizeof( sqlite3_int64 ) ); apg.selRows = g_array_new( FALSE, FALSE, sizeof( sqlite3_int64 ) );
apg.params = params; apg.cag.params = params;
XP_ASSERT( !!params->dbName || params->dbFileName ); XP_ASSERT( !!params->dbName || params->dbFileName );
if ( !!params->dbName ) { if ( !!params->dbName ) {
params->pDb = openGamesDB( params->dbName ); /* params->pDb = openGamesDB( params->dbName ); */
/* Check if we have a local ID already. If we do and it's /* Check if we have a local ID already. If we do and it's
changed, we care. */ changed, we care. */
@ -890,7 +884,6 @@ gtkmain( LaunchParams* params )
.msgNoticeReceived = gtkNoticeRcvd, .msgNoticeReceived = gtkNoticeRcvd,
.devIDReceived = gtkDevIDReceived, .devIDReceived = gtkDevIDReceived,
.msgErrorMsg = gtkErrorMsgRcvd, .msgErrorMsg = gtkErrorMsgRcvd,
.socketAdded = gtkSocketAdded,
.inviteReceived = relayInviteReceived, .inviteReceived = relayInviteReceived,
}; };
@ -919,7 +912,6 @@ gtkmain( LaunchParams* params )
} }
if ( !!myPhone && 0 < myPort ) { if ( !!myPhone && 0 < myPort ) {
SMSProcs smsProcs = { SMSProcs smsProcs = {
.socketAdded = gtkSocketAdded,
.inviteReceived = smsInviteReceived, .inviteReceived = smsInviteReceived,
.msgReceived = smsMsgReceivedGTK, .msgReceived = smsMsgReceivedGTK,
}; };
@ -943,8 +935,8 @@ gtkmain( LaunchParams* params )
gtk_main(); gtk_main();
device_store( params->dutil ); device_store( params->dutil );
closeGamesDB( params->pDb ); /* closeGamesDB( params->pDb ); */
params->pDb = NULL; /* params->pDb = NULL; */
relaycon_cleanup( params ); relaycon_cleanup( params );
#ifdef XWFEATURE_SMS #ifdef XWFEATURE_SMS
linux_sms_cleanup( params ); linux_sms_cleanup( params );

View file

@ -25,8 +25,7 @@
int gtkmain( LaunchParams* params ); int gtkmain( LaunchParams* params );
void windowDestroyed( GtkGameGlobals* globals ); void windowDestroyed( GtkGameGlobals* globals );
void onGameSaved( void* closure, sqlite3_int64 rowid, XP_Bool firstTime ); void gtkOnGameSaved( void* closure, sqlite3_int64 rowid, XP_Bool firstTime );
void open_row( GtkAppGlobals* apg, sqlite3_int64 row, XP_Bool isNew );
void make_rematch( GtkAppGlobals* apg, const CommonGlobals* cGlobals ); void make_rematch( GtkAppGlobals* apg, const CommonGlobals* cGlobals );
#endif #endif

View file

@ -639,8 +639,8 @@ setDefaults( CurGameInfo* gi )
} }
gboolean gboolean
newGameDialog( GtkGameGlobals* globals, CurGameInfo* gi, CommsAddrRec* addr, gtkNewGameDialog( GtkGameGlobals* globals, CurGameInfo* gi, CommsAddrRec* addr,
XP_Bool isNewGame, XP_Bool fireConnDlg ) XP_Bool isNewGame, XP_Bool fireConnDlg )
{ {
GtkNewGameState state; GtkNewGameState state;
XP_MEMSET( &state, 0, sizeof(state) ); XP_MEMSET( &state, 0, sizeof(state) );

View file

@ -26,9 +26,9 @@
#include "gtkboard.h" #include "gtkboard.h"
gboolean newGameDialog( GtkGameGlobals* globals, CurGameInfo* gi, gboolean gtkNewGameDialog( GtkGameGlobals* globals, CurGameInfo* gi,
CommsAddrRec* addr, XP_Bool isNewGame, CommsAddrRec* addr, XP_Bool isNewGame,
XP_Bool fireConnDlg ); XP_Bool fireConnDlg );
#endif /* _GTKNEWGAME_H_ */ #endif /* _GTKNEWGAME_H_ */
#endif /* PLATFORM_GTK */ #endif /* PLATFORM_GTK */

View file

@ -94,7 +94,7 @@ dutils_init( MPFORMAL VTableMgr* vtMgr, void* closure )
return result; return result;
} }
void dutils_free( XW_DUtilCtxt** ducp ) void dutils_free( XW_DUtilCtxt** XP_UNUSED_DBG(ducp) )
{ {
# ifdef MEM_DEBUG # ifdef MEM_DEBUG
XP_FREEP( (*ducp)->mpool, ducp ); XP_FREEP( (*ducp)->mpool, ducp );

View file

@ -44,6 +44,7 @@
#include "comms.h" #include "comms.h"
#include "strutils.h" #include "strutils.h"
#include "uuidhack.h" #include "uuidhack.h"
#include "gsrcwrap.h"
#define MAX_CLIENTS 1 #define MAX_CLIENTS 1
@ -186,7 +187,7 @@ lbt_connectSocket( LinBtStuff* btStuff, const CommsAddrRec* addrP )
// connect to server // connect to server
&& (0 == connect( sock, (struct sockaddr *)&saddr, sizeof(saddr) )) ) { && (0 == connect( sock, (struct sockaddr *)&saddr, sizeof(saddr) )) ) {
CommonGlobals* globals = btStuff->globals; CommonGlobals* globals = btStuff->globals;
(*globals->socketAdded)( globals->socketAddedClosure, sock, bt_socket_proc ); ADD_SOCKET( globals->socketAddedClosure, sock, bt_socket_proc );
btStuff->socket = sock; btStuff->socket = sock;
} else { } else {
XP_LOGF( "%s: connect->%s; closing socket %d", __func__, strerror(errno), sock ); XP_LOGF( "%s: connect->%s; closing socket %d", __func__, strerror(errno), sock );
@ -215,7 +216,7 @@ lbt_accept( int listener, void* ctxt )
success = sock >= 0; success = sock >= 0;
if ( success ) { if ( success ) {
(*globals->socketAdded)( globals->socketAddedClosure, sock, bt_socket_proc ); ADD_SOCKET( globals->socketAddedClosure, sock, bt_socket_proc );
XP_ASSERT( btStuff->socket == -1 ); XP_ASSERT( btStuff->socket == -1 );
btStuff->socket = sock; btStuff->socket = sock;
} else { } else {

View file

@ -72,6 +72,7 @@
#include "strutils.h" #include "strutils.h"
#include "dbgutil.h" #include "dbgutil.h"
#include "dictiter.h" #include "dictiter.h"
#include "gsrcwrap.h"
/* #include "commgr.h" */ /* #include "commgr.h" */
/* #include "compipe.h" */ /* #include "compipe.h" */
#include "memstream.h" #include "memstream.h"
@ -80,6 +81,12 @@
#define DEFAULT_PORT 10997 #define DEFAULT_PORT 10997
#define DEFAULT_LISTEN_PORT 4998 #define DEFAULT_LISTEN_PORT 4998
#ifdef MEM_DEBUG
# define MEMPOOL cGlobals->util->mpool,
#else
# define MEMPOOL
#endif
static int blocking_read( int fd, unsigned char* buf, const int len ); static int blocking_read( int fd, unsigned char* buf, const int len );
XP_Bool XP_Bool
@ -120,6 +127,191 @@ streamFromFile( CommonGlobals* cGlobals, char* name )
return stream; return stream;
} /* streamFromFile */ } /* streamFromFile */
void
tryConnectToServer( CommonGlobals* cGlobals )
{
LaunchParams* params = cGlobals->params;
XWStreamCtxt* stream =
mem_stream_make( MPPARM(cGlobals->util->mpool) params->vtMgr,
cGlobals, CHANNEL_NONE,
sendOnClose );
(void)server_initClientConnection( cGlobals->game.server,
stream );
}
static bool
canMakeFromGI( const CurGameInfo* gi )
{
LOG_FUNC();
bool result = 0 < gi->nPlayers
&& 0 < gi->dictLang
;
bool haveDict = !!gi->dictName;
bool allHaveDicts = true;
for ( int ii = 0; result && ii < gi->nPlayers; ++ii ) {
const LocalPlayer* lp = &gi->players[ii];
result = !lp->isLocal || '\0' != lp->name[0];
if ( allHaveDicts ) {
allHaveDicts = !!lp->dictName;
}
}
result = result && (haveDict || allHaveDicts);
LOG_RETURNF( "%d", result );
XP_ASSERT( result );
return result;
}
void
linuxOpenGame( CommonGlobals* cGlobals, const TransportProcs* procs )
{
LOG_FUNC();
XWStreamCtxt* stream = NULL;
XP_Bool opened = XP_FALSE;
LaunchParams* params = cGlobals->params;
if ( !!params->fileName && file_exists( params->fileName ) ) {
stream = streamFromFile( cGlobals, params->fileName );
#ifdef USE_SQLITE
} else if ( !!params->dbFileName && file_exists( params->dbFileName ) ) {
XP_UCHAR buf[32];
XP_SNPRINTF( buf, sizeof(buf), "%d", params->dbFileID );
mpool_setTag( MEMPOOL buf );
stream = streamFromDB( cGlobals );
#endif
} else if ( !!params->pDb && 0 <= cGlobals->rowid ) {
stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
params->vtMgr );
if ( !loadGame( stream, params->pDb, cGlobals->rowid ) ) {
stream_destroy( stream );
stream = NULL;
}
}
if ( !!stream ) {
if ( NULL == cGlobals->dict ) {
cGlobals->dict = makeDictForStream( cGlobals, stream );
}
opened = game_makeFromStream( MEMPOOL stream, &cGlobals->game,
cGlobals->gi, cGlobals->dict,
&cGlobals->dicts, cGlobals->util,
cGlobals->draw,
&cGlobals->cp, procs );
XP_LOGF( "%s: loaded gi at %p", __func__, &cGlobals->gi );
stream_destroy( stream );
}
if ( !opened && canMakeFromGI( cGlobals->gi ) ) {
#ifdef XWFEATURE_RELAY
/* if ( addr.conType == COMMS_CONN_RELAY ) { */
/* XP_ASSERT( !!params->connInfo.relay.relayName ); */
/* globals->cGlobals.defaultServerName */
/* = params->connInfo.relay.relayName; */
/* } */
#endif
game_makeNewGame( MEMPOOL &cGlobals->game, cGlobals->gi,
cGlobals->util, cGlobals->draw,
&cGlobals->cp, procs
#ifdef SET_GAMESEED
, params->gameSeed
#endif
);
CommsAddrRec addr = cGlobals->addr;
// addr.conType = params->conType;
CommsConnType typ;
for ( XP_U32 st = 0; addr_iter( &addr, &typ, &st ); ) {
if ( params->commsDisableds[typ][0] ) {
comms_setAddrDisabled( cGlobals->game.comms, typ, XP_FALSE, XP_TRUE );
}
if ( params->commsDisableds[typ][1] ) {
comms_setAddrDisabled( cGlobals->game.comms, typ, XP_TRUE, XP_TRUE );
}
switch( typ ) {
#ifdef XWFEATURE_RELAY
case COMMS_CONN_RELAY:
/* addr.u.ip_relay.ipAddr = 0; */
/* addr.u.ip_relay.port = params->connInfo.relay.defaultSendPort; */
/* addr.u.ip_relay.seeksPublicRoom = params->connInfo.relay.seeksPublicRoom; */
/* addr.u.ip_relay.advertiseRoom = params->connInfo.relay.advertiseRoom; */
/* XP_STRNCPY( addr.u.ip_relay.hostName, params->connInfo.relay.relayName, */
/* sizeof(addr.u.ip_relay.hostName) - 1 ); */
/* XP_STRNCPY( addr.u.ip_relay.invite, params->connInfo.relay.invite, */
/* sizeof(addr.u.ip_relay.invite) - 1 ); */
break;
#endif
#ifdef XWFEATURE_BLUETOOTH
case COMMS_CONN_BT:
XP_ASSERT( sizeof(addr.u.bt.btAddr)
>= sizeof(params->connInfo.bt.hostAddr));
XP_MEMCPY( &addr.u.bt.btAddr, &params->connInfo.bt.hostAddr,
sizeof(params->connInfo.bt.hostAddr) );
break;
#endif
#ifdef XWFEATURE_IP_DIRECT
case COMMS_CONN_IP_DIRECT:
XP_STRNCPY( addr.u.ip.hostName_ip, params->connInfo.ip.hostName,
sizeof(addr.u.ip.hostName_ip) - 1 );
addr.u.ip.port_ip = params->connInfo.ip.port;
break;
#endif
#ifdef XWFEATURE_SMS
case COMMS_CONN_SMS:
/* No! Don't overwrite what may be a return address with local
stuff */
/* XP_STRNCPY( addr.u.sms.phone, params->connInfo.sms.phone, */
/* sizeof(addr.u.sms.phone) - 1 ); */
/* addr.u.sms.port = params->connInfo.sms.port; */
break;
#endif
default:
break;
}
}
model_setDictionary( cGlobals->game.model, cGlobals->dict );
setSquareBonuses( cGlobals );
model_setPlayerDicts( cGlobals->game.model, &cGlobals->dicts );
/* Need to save in order to have a valid selRow for the first send */
linuxSaveGame( cGlobals );
#ifndef XWFEATURE_STANDALONE_ONLY
/* This may trigger network activity */
if ( !!cGlobals->game.comms ) {
comms_setAddr( cGlobals->game.comms, &addr );
}
#endif
#ifdef XWFEATURE_SEARCHLIMIT
cGlobals->gi->allowHintRect = params->allowHintRect;
#endif
if ( params->needsNewGame ) {
XP_ASSERT(0);
// new_game_impl( globals, XP_FALSE );
#ifndef XWFEATURE_STANDALONE_ONLY
} else {
DeviceRole serverRole = cGlobals->gi->serverRole;
if ( serverRole == SERVER_ISCLIENT ) {
tryConnectToServer( cGlobals );
}
#endif
}
}
#ifndef XWFEATURE_STANDALONE_ONLY
if ( !!cGlobals->game.comms ) {
comms_start( cGlobals->game.comms );
}
#endif
server_do( cGlobals->game.server );
linuxSaveGame( cGlobals ); /* again, to include address etc. */
}
#ifdef USE_SQLITE #ifdef USE_SQLITE
XWStreamCtxt* XWStreamCtxt*
streamFromDB( CommonGlobals* cGlobals ) streamFromDB( CommonGlobals* cGlobals )
@ -189,7 +381,7 @@ gameGotBuf( CommonGlobals* cGlobals, XP_Bool hasDraw, const XP_U8* buf,
if ( !!stream ) { if ( !!stream ) {
redraw = game_receiveMessage( game, stream, from ); redraw = game_receiveMessage( game, stream, from );
if ( redraw ) { if ( redraw ) {
saveGame( cGlobals ); linuxSaveGame( cGlobals );
} }
stream_destroy( stream ); stream_destroy( stream );
@ -328,7 +520,7 @@ strFromStream( XWStreamCtxt* stream )
} /* strFromStream */ } /* strFromStream */
void void
saveGame( CommonGlobals* cGlobals ) linuxSaveGame( CommonGlobals* cGlobals )
{ {
LOG_FUNC(); LOG_FUNC();
sqlite3* pDb = cGlobals->params->pDb; sqlite3* pDb = cGlobals->params->pDb;
@ -336,7 +528,7 @@ saveGame( CommonGlobals* cGlobals )
(!!cGlobals->params->fileName || !!pDb) ) { (!!cGlobals->params->fileName || !!pDb) ) {
XP_Bool doSave = XP_TRUE; XP_Bool doSave = XP_TRUE;
XP_Bool newGame = !file_exists( cGlobals->params->fileName ) XP_Bool newGame = !file_exists( cGlobals->params->fileName )
|| -1 == cGlobals->selRow; || -1 == cGlobals->rowid;
/* don't fail to save first time! */ /* don't fail to save first time! */
if ( 0 < cGlobals->params->saveFailPct && !newGame ) { if ( 0 < cGlobals->params->saveFailPct && !newGame ) {
XP_U16 pct = XP_RANDOM() % 100; XP_U16 pct = XP_RANDOM() % 100;
@ -550,7 +742,14 @@ secondTimerFired( gpointer data )
void void
setOneSecondTimer( CommonGlobals* cGlobals ) setOneSecondTimer( CommonGlobals* cGlobals )
{ {
(void)g_timeout_add_seconds( 1, secondTimerFired, cGlobals ); guint id = g_timeout_add_seconds( 1, secondTimerFired, cGlobals );
cGlobals->secondsTimerID = id;
}
void
clearOneSecondTimer( CommonGlobals* cGlobals )
{
g_source_remove( cGlobals->secondsTimerID );
} }
#endif #endif
@ -603,6 +802,7 @@ typedef enum {
,CMD_NOHEARTBEAT ,CMD_NOHEARTBEAT
,CMD_HOSTNAME ,CMD_HOSTNAME
,CMD_CLOSESTDIN ,CMD_CLOSESTDIN
,CMD_NOCLOSESTDIN
,CMD_QUITAFTER ,CMD_QUITAFTER
,CMD_BOARDSIZE ,CMD_BOARDSIZE
,CMD_HIDEVALUES ,CMD_HIDEVALUES
@ -655,6 +855,7 @@ typedef enum {
#if defined PLATFORM_GTK && defined PLATFORM_NCURSES #if defined PLATFORM_GTK && defined PLATFORM_NCURSES
,CMD_GTK ,CMD_GTK
,CMD_CURSES ,CMD_CURSES
,CMD_CURSES_LIST_HT
#endif #endif
#if defined PLATFORM_GTK #if defined PLATFORM_GTK
,CMD_ASKNEWGAME ,CMD_ASKNEWGAME
@ -725,6 +926,7 @@ static CmdInfoRec CmdInfoRecs[] = {
,{ CMD_NOHEARTBEAT, false, "no-heartbeat", "don't send heartbeats" } ,{ CMD_NOHEARTBEAT, false, "no-heartbeat", "don't send heartbeats" }
,{ CMD_HOSTNAME, true, "host", "name of remote host" } ,{ CMD_HOSTNAME, true, "host", "name of remote host" }
,{ CMD_CLOSESTDIN, false, "close-stdin", "close stdin on start" } ,{ CMD_CLOSESTDIN, false, "close-stdin", "close stdin on start" }
,{ CMD_NOCLOSESTDIN, false, "no-close-stdin", "do not close stdin on start" }
,{ CMD_QUITAFTER, true, "quit-after", "exit <n> seconds after game's done" } ,{ CMD_QUITAFTER, true, "quit-after", "exit <n> seconds after game's done" }
,{ CMD_BOARDSIZE, true, "board-size", "board is <n> by <n> cells" } ,{ CMD_BOARDSIZE, true, "board-size", "board is <n> by <n> cells" }
,{ CMD_HIDEVALUES, false, "hide-values", "show letters, not nums, on tiles" } ,{ CMD_HIDEVALUES, false, "hide-values", "show letters, not nums, on tiles" }
@ -784,6 +986,7 @@ static CmdInfoRec CmdInfoRecs[] = {
#if defined PLATFORM_GTK && defined PLATFORM_NCURSES #if defined PLATFORM_GTK && defined PLATFORM_NCURSES
,{ CMD_GTK, false, "gtk", "use GTK for display" } ,{ CMD_GTK, false, "gtk", "use GTK for display" }
,{ CMD_CURSES, false, "curses", "use curses for display" } ,{ CMD_CURSES, false, "curses", "use curses for display" }
,{ CMD_CURSES_LIST_HT, true, "curses-list-ht", "how many cols tall is the games list" }
#endif #endif
#if defined PLATFORM_GTK #if defined PLATFORM_GTK
,{ CMD_ASKNEWGAME, false, "ask-new", "put up ui for new game params" } ,{ CMD_ASKNEWGAME, false, "ask-new", "put up ui for new game params" }
@ -1121,7 +1324,8 @@ linux_relay_ioproc( GIOChannel* source, GIOCondition condition, gpointer data )
unsigned char buf[1024]; unsigned char buf[1024];
int sock = g_io_channel_unix_get_fd( source ); int sock = g_io_channel_unix_get_fd( source );
if ( cGlobals->relaySocket != sock ) { if ( cGlobals->relaySocket != sock ) {
XP_LOGF( "%s: changing relaySocket from %d to %d", __func__, cGlobals->relaySocket, sock ); XP_LOGF( "%s: changing relaySocket from %d to %d", __func__,
cGlobals->relaySocket, sock );
cGlobals->relaySocket = sock; cGlobals->relaySocket = sock;
} }
int nBytes = linux_relay_receive( cGlobals, sock, buf, sizeof(buf) ); int nBytes = linux_relay_receive( cGlobals, sock, buf, sizeof(buf) );
@ -1164,9 +1368,9 @@ linux_relay_send( CommonGlobals* cGlobals, const XP_U8* buf, XP_U16 buflen,
{ {
XP_S16 result = 0; XP_S16 result = 0;
if ( cGlobals->params->useUdp ) { if ( cGlobals->params->useUdp ) {
XP_ASSERT( -1 != cGlobals->selRow ); XP_ASSERT( -1 != cGlobals->rowid );
XP_U16 seed = comms_getChannelSeed( cGlobals->game.comms ); XP_U16 seed = comms_getChannelSeed( cGlobals->game.comms );
XP_U32 clientToken = makeClientToken( cGlobals->selRow, seed ); XP_U32 clientToken = makeClientToken( cGlobals->rowid, seed );
result = relaycon_send( cGlobals->params, buf, buflen, result = relaycon_send( cGlobals->params, buf, buflen,
clientToken, addrRec ); clientToken, addrRec );
} else { } else {
@ -1176,7 +1380,7 @@ linux_relay_send( CommonGlobals* cGlobals, const XP_U8* buf, XP_U16 buflen,
if ( sock == -1 ) { if ( sock == -1 ) {
XP_LOGF( "%s: socket uninitialized", __func__ ); XP_LOGF( "%s: socket uninitialized", __func__ );
sock = linux_init_relay_socket( cGlobals, addrRec ); sock = linux_init_relay_socket( cGlobals, addrRec );
(*cGlobals->socketAdded)( cGlobals, sock, linux_relay_ioproc ); ADD_SOCKET( cGlobals, sock, linux_relay_ioproc );
} }
if ( sock != -1 ) { if ( sock != -1 ) {
@ -1262,7 +1466,7 @@ linux_send( const XP_U8* buf, XP_U16 buflen, const XP_UCHAR* XP_UNUSED_DBG(msgNo
#endif #endif
#if defined XWFEATURE_BLUETOOTH #if defined XWFEATURE_BLUETOOTH
case COMMS_CONN_BT: { case COMMS_CONN_BT: {
XP_Bool isServer = comms_getIsServer( cGlobals->game.comms ); XP_Bool isServer = game_getIsServer( &cGlobals->game );
linux_bt_open( cGlobals, isServer ); linux_bt_open( cGlobals, isServer );
nSent = linux_bt_send( buf, buflen, addrRec, cGlobals ); nSent = linux_bt_send( buf, buflen, addrRec, cGlobals );
} }
@ -1506,7 +1710,7 @@ linux_util_addrChange( XW_UtilCtxt* uc,
switch ( typ ) { switch ( typ ) {
#ifdef XWFEATURE_BLUETOOTH #ifdef XWFEATURE_BLUETOOTH
case COMMS_CONN_BT: { case COMMS_CONN_BT: {
XP_Bool isServer = comms_getIsServer( cGlobals->game.comms ); XP_Bool isServer = game_getIsServer( &cGlobals->game );
linux_bt_open( cGlobals, isServer ); linux_bt_open( cGlobals, isServer );
} }
break; break;
@ -1530,18 +1734,10 @@ linux_util_addrChange( XW_UtilCtxt* uc,
} }
} }
void static gint
linuxSetIsServer( CommonGlobals* cGlobals, XP_Bool isServer ) changeRolesIdle( gpointer data )
{
XP_LOGF( "%s(isServer=%d)", __func__, isServer );
DeviceRole newRole = isServer? SERVER_ISSERVER : SERVER_ISCLIENT;
cGlobals->params->serverRole = newRole;
cGlobals->gi->serverRole = newRole;
}
void
linuxChangeRoles( CommonGlobals* cGlobals )
{ {
CommonGlobals* cGlobals = (CommonGlobals*)data;
ServerCtxt* server = cGlobals->game.server; ServerCtxt* server = cGlobals->game.server;
server_reset( server, cGlobals->game.comms ); server_reset( server, cGlobals->game.comms );
if ( SERVER_ISCLIENT == cGlobals->gi->serverRole ) { if ( SERVER_ISCLIENT == cGlobals->gi->serverRole ) {
@ -1551,7 +1747,23 @@ linuxChangeRoles( CommonGlobals* cGlobals )
(void)server_initClientConnection( server, stream ); (void)server_initClientConnection( server, stream );
} }
(void)server_do( server ); (void)server_do( server );
return 0;
} }
static void
linux_util_setIsServer( XW_UtilCtxt* uc, XP_Bool isServer )
{
XP_LOGF( "%s(isServer=%d)", __func__, isServer );
CommonGlobals* cGlobals = (CommonGlobals*)uc->closure;
DeviceRole newRole = isServer? SERVER_ISSERVER : SERVER_ISCLIENT;
cGlobals->params->serverRole = newRole;
cGlobals->gi->serverRole = newRole;
(void)ADD_ONETIME_IDLE( changeRolesIdle, cGlobals );
XP_ASSERT( isServer == game_getIsServer( &cGlobals->game ) );
}
#endif #endif
unsigned int unsigned int
@ -1906,59 +2118,22 @@ setupLinuxUtilCallbacks( XW_UtilCtxt* util )
#ifndef XWFEATURE_STANDALONE_ONLY #ifndef XWFEATURE_STANDALONE_ONLY
util->vtable->m_util_informMissing = linux_util_informMissing; util->vtable->m_util_informMissing = linux_util_informMissing;
util->vtable->m_util_addrChange = linux_util_addrChange; util->vtable->m_util_addrChange = linux_util_addrChange;
util->vtable->m_util_setIsServer = linux_util_setIsServer;
#endif #endif
} }
/* Set up cGlobals->gi and cGlobals->addr based on params fields */
void void
initFromParams( CommonGlobals* cGlobals, LaunchParams* params ) assertAllCallbacksSet( XW_UtilCtxt* util )
{ {
LOG_FUNC(); LOG_FUNC();
/* CurGameInfo */ XWStreamCtxt* (**proc)(XW_UtilCtxt*, XP_PlayerAddr ) =
cGlobals->gi = &params->pgi; &util->vtable->m_util_makeStreamFromAddr;
for ( int ii = 0; ii < sizeof(*util->vtable)/sizeof(*proc); ++ii ) {
/* addr */ if ( !*proc ) {
CommsAddrRec* addr = &cGlobals->addr; XP_LOGF( "%s(): null ptr at index %d", __func__, ii );
XP_MEMSET( addr, 0, sizeof(*addr) ); XP_ASSERT( 0 );
CommsConnType typ;
for ( XP_U32 st = 0; addr_iter( &params->addr, &typ, &st ); ) {
switch( typ ) {
#ifdef XWFEATURE_RELAY
case COMMS_CONN_RELAY:
addr_addType( addr, COMMS_CONN_RELAY );
addr->u.ip_relay.ipAddr = 0; /* ??? */
addr->u.ip_relay.port = params->connInfo.relay.defaultSendPort;
addr->u.ip_relay.seeksPublicRoom =
params->connInfo.relay.seeksPublicRoom;
addr->u.ip_relay.advertiseRoom = params->connInfo.relay.advertiseRoom;
XP_STRNCPY( addr->u.ip_relay.hostName,
params->connInfo.relay.relayName,
sizeof(addr->u.ip_relay.hostName) - 1 );
XP_STRNCPY( addr->u.ip_relay.invite, params->connInfo.relay.invite,
sizeof(addr->u.ip_relay.invite) - 1 );
break;
#endif
#ifdef XWFEATURE_SMS
case COMMS_CONN_SMS:
addr_addType( addr, COMMS_CONN_SMS );
XP_STRNCPY( addr->u.sms.phone, params->connInfo.sms.myPhone,
sizeof(addr->u.sms.phone) - 1 );
addr->u.sms.port = params->connInfo.sms.port;
break;
#endif
#ifdef XWFEATURE_BLUETOOTH
case COMMS_CONN_BT:
addr_addType( addr, COMMS_CONN_BT );
XP_ASSERT( sizeof(addr->u.bt.btAddr)
>= sizeof(params->connInfo.bt.hostAddr));
XP_MEMCPY( &addr->u.bt.btAddr, &params->connInfo.bt.hostAddr,
sizeof(params->connInfo.bt.hostAddr) );
break;
#endif
default:
break;
} }
++proc;
} }
} }
@ -1972,6 +2147,12 @@ setupUtil( CommonGlobals* cGlobals )
setupLinuxUtilCallbacks( util ); setupLinuxUtilCallbacks( util );
} }
void
disposeUtil( CommonGlobals* cGlobals )
{
linux_util_vt_destroy( cGlobals->util );
}
static void static void
initParams( LaunchParams* params ) initParams( LaunchParams* params )
{ {
@ -2000,6 +2181,9 @@ initParams( LaunchParams* params )
static void static void
freeParams( LaunchParams* params ) freeParams( LaunchParams* params )
{ {
closeGamesDB( params->pDb );
params->pDb = NULL;
vtmgr_destroy( MPPARM(params->mpool) params->vtMgr ); vtmgr_destroy( MPPARM(params->mpool) params->vtMgr );
dutils_free( &params->dutil ); dutils_free( &params->dutil );
dmgr_destroy( params->dictMgr ); dmgr_destroy( params->dictMgr );
@ -2116,6 +2300,7 @@ main( int argc, char** argv )
mainParams.useMmap = XP_TRUE; mainParams.useMmap = XP_TRUE;
mainParams.useUdp = true; mainParams.useUdp = true;
mainParams.dbName = "xwgames.sqldb"; mainParams.dbName = "xwgames.sqldb";
mainParams.cursesListWinHt = 5;
char* envDictPath = getenv( "XW_DICTDIR" ); char* envDictPath = getenv( "XW_DICTDIR" );
XP_LOGF( "%s: envDictPath=%s", __func__, envDictPath ); XP_LOGF( "%s: envDictPath=%s", __func__, envDictPath );
@ -2180,6 +2365,7 @@ main( int argc, char** argv )
if ( !path ) { if ( !path ) {
path = "."; path = ".";
} }
XP_LOGF( "%s(): appending dict path: %s", __func__, path );
mainParams.dictDirs = g_slist_append( mainParams.dictDirs, path ); mainParams.dictDirs = g_slist_append( mainParams.dictDirs, path );
break; break;
#ifdef XWFEATURE_WALKDICT #ifdef XWFEATURE_WALKDICT
@ -2376,6 +2562,9 @@ main( int argc, char** argv )
case CMD_CLOSESTDIN: case CMD_CLOSESTDIN:
mainParams.closeStdin = XP_TRUE; mainParams.closeStdin = XP_TRUE;
break; break;
case CMD_NOCLOSESTDIN:
mainParams.closeStdin = XP_FALSE;
break;
case CMD_QUITAFTER: case CMD_QUITAFTER:
mainParams.quitAfter = atoi(optarg); mainParams.quitAfter = atoi(optarg);
break; break;
@ -2481,6 +2670,9 @@ main( int argc, char** argv )
case CMD_CURSES: case CMD_CURSES:
mainParams.useCurses = XP_TRUE; mainParams.useCurses = XP_TRUE;
break; break;
case CMD_CURSES_LIST_HT:
mainParams.cursesListWinHt = atoi(optarg);
break;
#endif #endif
#if defined PLATFORM_GTK #if defined PLATFORM_GTK
case CMD_ASKNEWGAME: case CMD_ASKNEWGAME:
@ -2539,17 +2731,22 @@ main( int argc, char** argv )
} }
} }
XP_LOGF( "%s(): here: %s", __func__, mainParams.pgi.dictName );
if ( !!mainParams.pgi.dictName ) { if ( !!mainParams.pgi.dictName ) {
XP_LOGF( "%s(): there", __func__ );
/* char path[256]; */ /* char path[256]; */
/* getDictPath( &mainParams, mainParams.gi.dictName, path, VSIZE(path) ); */ /* getDictPath( &mainParams, mainParams.gi.dictName, path, VSIZE(path) ); */
/* mainParams.dict = */ DictionaryCtxt* dict =
/* linux_dictionary_make( MPPARM(mainParams.mpool) &mainParams, */ linux_dictionary_make( MPPARM(mainParams.mpool) &mainParams,
/* mainParams.pgi.dictName, */ mainParams.pgi.dictName,
/* mainParams.useMmap ); */ mainParams.useMmap );
/* XP_ASSERT( !!mainParams.dict ); */ XP_ASSERT( !!dict );
/* mainParams.pgi.dictLang = dict_getLangCode( mainParams.dict ); */ mainParams.pgi.dictLang = dict_getLangCode( dict );
XP_LOGF( "%s(): set lang code: %d", __func__, mainParams.pgi.dictLang );
dict_unref( dict );
} else if ( isServer ) { } else if ( isServer ) {
#ifdef STUBBED_DICT #ifdef STUBBED_DICT
foo
mainParams.dict = mainParams.dict =
make_stubbed_dict( MPPARM_NOCOMMA(mainParams.util->mpool) ); make_stubbed_dict( MPPARM_NOCOMMA(mainParams.util->mpool) );
XP_WARNF( "no dictionary provided: using English stub dict\n" ); XP_WARNF( "no dictionary provided: using English stub dict\n" );
@ -2666,21 +2863,19 @@ main( int argc, char** argv )
mainParams.serverRole = SERVER_ISCLIENT; mainParams.serverRole = SERVER_ISCLIENT;
} }
/* if ( mainParams.needsNewGame ) { */ XP_ASSERT( !!mainParams.dbName );
/* gi_disposePlayerInfo( MPPARM(mainParams.mpool) &mainParams.pgi ); */ mainParams.pDb = openGamesDB( mainParams.dbName );
/* gi_initPlayerInfo( MPPARM(mainParams.mpool) &mainParams.pgi, NULL ); */
/* } */
if ( mainParams.useCurses ) { if ( mainParams.useCurses ) {
if ( mainParams.needsNewGame ) { /* if ( mainParams.needsNewGame ) { */
/* curses doesn't have newgame dialog */ /* /\* curses doesn't have newgame dialog *\/ */
usage( argv[0], "game params required for curses version, e.g. --name Eric --room MyRoom" /* usage( argv[0], "game params required for curses version, e.g. --name Eric --room MyRoom" */
" --remote-player --dict-dir ../ --game-dict CollegeEng_2to8.xwd"); /* " --remote-player --dict-dir ../ --game-dict CollegeEng_2to8.xwd"); */
} else { /* } else { */
#if defined PLATFORM_NCURSES #if defined PLATFORM_NCURSES
cursesmain( isServer, &mainParams ); cursesmain( isServer, &mainParams );
#endif #endif
} /* } */
} else { } else {
#if defined PLATFORM_GTK #if defined PLATFORM_GTK
gtk_init( &argc, &argv ); gtk_init( &argc, &argv );
@ -2694,6 +2889,8 @@ main( int argc, char** argv )
free( longopts ); free( longopts );
g_slist_free( mainParams.dictDirs ); g_slist_free( mainParams.dictDirs );
gsw_logIdles();
XP_LOGF( "%s exiting main, returning %d", argv[0], result ); XP_LOGF( "%s exiting main, returning %d", argv[0], result );
return result; return result;
} /* main */ } /* main */

View file

@ -70,7 +70,7 @@ void writeToFile( XWStreamCtxt* stream, void* closure );
XP_Bool getDictPath( const LaunchParams *params, const char* name, XP_Bool getDictPath( const LaunchParams *params, const char* name,
char* result, int resultLen ); char* result, int resultLen );
GSList* listDicts( const LaunchParams *params ); GSList* listDicts( const LaunchParams *params );
void saveGame( CommonGlobals* cGlobals ); void linuxSaveGame( CommonGlobals* cGlobals );
void linux_close_socket( CommonGlobals* cGlobals ); void linux_close_socket( CommonGlobals* cGlobals );
@ -87,18 +87,18 @@ void do_nbs_then_close( CommonGlobals* cGlobals,
#ifdef USE_GLIBLOOP #ifdef USE_GLIBLOOP
void setOneSecondTimer( CommonGlobals* cGlobals ); void setOneSecondTimer( CommonGlobals* cGlobals );
void clearOneSecondTimer( CommonGlobals* cGlobals );
#else #else
# define setOneSecondTimer( cGlobals ) # define setOneSecondTimer( cGlobals )
#endif #endif
void setupLinuxUtilCallbacks( XW_UtilCtxt* util ); void setupLinuxUtilCallbacks( XW_UtilCtxt* util );
void initFromParams( CommonGlobals* cGlobals, LaunchParams* params ); void assertAllCallbacksSet( XW_UtilCtxt* util );
void setupUtil( CommonGlobals* cGlobals ); void setupUtil( CommonGlobals* cGlobals );
void disposeUtil( CommonGlobals* cGlobals );
DictionaryCtxt* makeDictForStream( CommonGlobals* cGlobals, DictionaryCtxt* makeDictForStream( CommonGlobals* cGlobals,
XWStreamCtxt* stream ); XWStreamCtxt* stream );
void linuxSetIsServer( CommonGlobals* cGlobals, XP_Bool isServer );
void linuxChangeRoles( CommonGlobals* cGlobals );
void sendRelayReg( LaunchParams* params, sqlite3* pDb ); void sendRelayReg( LaunchParams* params, sqlite3* pDb );
void gameGotBuf( CommonGlobals* globals, XP_Bool haveDraw, void gameGotBuf( CommonGlobals* globals, XP_Bool haveDraw,
@ -111,6 +111,8 @@ void linux_doInitialReg( LaunchParams* params, XP_Bool idIsNew );
XP_Bool linux_setupDevidParams( LaunchParams* params ); XP_Bool linux_setupDevidParams( LaunchParams* params );
unsigned int makeRandomInt(); unsigned int makeRandomInt();
void linuxOpenGame( CommonGlobals* cGlobals, const TransportProcs* procs );
void tryConnectToServer( CommonGlobals* cGlobals );
/* void initParams( LaunchParams* params ); */ /* void initParams( LaunchParams* params ); */
/* void freeParams( LaunchParams* params ); */ /* void freeParams( LaunchParams* params ); */

View file

@ -34,7 +34,6 @@ typedef struct _SMSProcs {
void (*devIDReceived)( void* closure, const XP_UCHAR* devID, void (*devIDReceived)( void* closure, const XP_UCHAR* devID,
XP_U16 maxInterval ); XP_U16 maxInterval );
void (*msgErrorMsg)( void* closure, const XP_UCHAR* msg ); void (*msgErrorMsg)( void* closure, const XP_UCHAR* msg );
SocketAddedFunc socketAdded;
} SMSProcs; } SMSProcs;

View file

@ -114,6 +114,7 @@ typedef struct LaunchParams {
XP_U16 splitPackets; XP_U16 splitPackets;
XP_U16 chatsInterval; /* 0 means disabled */ XP_U16 chatsInterval; /* 0 means disabled */
XP_U16 askTimeout; XP_U16 askTimeout;
int cursesListWinHt;
#ifdef XWFEATURE_SEARCHLIMIT #ifdef XWFEATURE_SEARCHLIMIT
XP_Bool allowHintRect; XP_Bool allowHintRect;
#endif #endif
@ -170,7 +171,7 @@ typedef struct LaunchParams {
typedef struct CommonGlobals CommonGlobals; typedef struct CommonGlobals CommonGlobals;
typedef void (*SocketAddedFunc)( void* closure, int newsock, GIOFunc func ); typedef guint (*SocketAddedFunc)( void* closure, int newsock, GIOFunc func );
typedef XP_Bool (*Acceptor)( int sock, void* ctxt ); typedef XP_Bool (*Acceptor)( int sock, void* ctxt );
typedef void (*AddAcceptorFunc)(int listener, Acceptor func, typedef void (*AddAcceptorFunc)(int listener, Acceptor func,
CommonGlobals* globals, void** storage ); CommonGlobals* globals, void** storage );
@ -189,11 +190,13 @@ typedef void (*OnSaveFunc)( void* closure, sqlite3_int64 rowid,
XP_Bool firstTime ); XP_Bool firstTime );
struct CommonGlobals { struct CommonGlobals {
CurGameInfo _gi;
LaunchParams* params; LaunchParams* params;
CommonPrefs cp; CommonPrefs cp;
XW_UtilCtxt* util; XW_UtilCtxt* util;
XWGame game; XWGame game;
DrawCtx* draw;
CurGameInfo* gi; CurGameInfo* gi;
CommsAddrRec addr; CommsAddrRec addr;
DictionaryCtxt* dict; DictionaryCtxt* dict;
@ -202,9 +205,8 @@ struct CommonGlobals {
XP_U16 lastStreamSize; XP_U16 lastStreamSize;
XP_U16 nMissing; XP_U16 nMissing;
XP_Bool manualFinal; /* use asked for final scores */ XP_Bool manualFinal; /* use asked for final scores */
sqlite3_int64 selRow; sqlite3_int64 rowid;
SocketAddedFunc socketAdded;
void* socketAddedClosure; void* socketAddedClosure;
OnSaveFunc onSave; OnSaveFunc onSave;
void* onSaveClosure; void* onSaveClosure;
@ -248,10 +250,16 @@ struct CommonGlobals {
#endif #endif
TimerInfo timerInfo[NUM_TIMERS_PLUS_ONE]; TimerInfo timerInfo[NUM_TIMERS_PLUS_ONE];
guint secondsTimerID;
XP_U16 curSaveToken; XP_U16 curSaveToken;
}; };
typedef struct _CommonAppGlobals {
LaunchParams* params;
GSList* globalsList;
} CommonAppGlobals;
typedef struct _SourceData { typedef struct _SourceData {
GIOChannel* channel; GIOChannel* channel;
gint watch; gint watch;
@ -261,9 +269,8 @@ typedef struct _SourceData {
#ifdef PLATFORM_GTK #ifdef PLATFORM_GTK
typedef struct _GtkAppGlobals { typedef struct _GtkAppGlobals {
CommonAppGlobals cag;
GArray* selRows; GArray* selRows;
LaunchParams* params;
GSList* globalsList;
GList* sources; GList* sources;
GtkWidget* window; GtkWidget* window;
GtkWidget* listWidget; GtkWidget* listWidget;

View file

@ -28,6 +28,7 @@
#include "linuxmain.h" #include "linuxmain.h"
#include "comtypes.h" #include "comtypes.h"
#include "gamesdb.h" #include "gamesdb.h"
#include "gsrcwrap.h"
#define MAX_MOVE_CHECK_MS ((XP_U16)(1000 * 60 * 60 * 24)) #define MAX_MOVE_CHECK_MS ((XP_U16)(1000 * 60 * 60 * 24))
#define RELAY_API_PROTO "http" #define RELAY_API_PROTO "http"
@ -265,7 +266,7 @@ relaycon_init( LaunchParams* params, const RelayConnProcs* procs,
XP_MEMCPY( storage->host, host, XP_STRLEN(host) + 1 ); XP_MEMCPY( storage->host, host, XP_STRLEN(host) + 1 );
} else { } else {
storage->socket = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); storage->socket = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
(*procs->socketAdded)( storage, storage->socket, relaycon_receive ); ADD_SOCKET( storage, storage->socket, relaycon_receive );
XP_MEMSET( &storage->saddr, 0, sizeof(storage->saddr) ); XP_MEMSET( &storage->saddr, 0, sizeof(storage->saddr) );
storage->saddr.sin_family = PF_INET; storage->saddr.sin_family = PF_INET;
@ -705,6 +706,7 @@ process( RelayConStorage* storage, XP_U8* buf, ssize_t nRead )
XP_LOGF( "%s: error reading udp socket: %d (%s)", __func__, XP_LOGF( "%s: error reading udp socket: %d (%s)", __func__,
errno, strerror(errno) ); errno, strerror(errno) );
} }
LOG_RETURNF( "%d", TRUE );
return TRUE; return TRUE;
} }
@ -719,21 +721,24 @@ relaycon_receive( GIOChannel* source, GIOCondition XP_UNUSED_DBG(condition), gpo
socklen_t fromlen = sizeof(from); socklen_t fromlen = sizeof(from);
int socket = g_io_channel_unix_get_fd( source ); int socket = g_io_channel_unix_get_fd( source );
XP_LOGF( "%s: calling recvfrom on socket %d", __func__, socket );
ssize_t nRead = recvfrom( socket, buf, sizeof(buf), 0, /* flags */ ssize_t nRead = recvfrom( socket, buf, sizeof(buf), 0, /* flags */
(struct sockaddr*)&from, &fromlen ); (struct sockaddr*)&from, &fromlen );
gchar* b64 = g_base64_encode( (const guchar*)buf, gchar* b64 = g_base64_encode( (const guchar*)buf,
((0 <= nRead)? nRead : 0) ); ((0 <= nRead)? nRead : 0) );
XP_LOGF( "%s: read %zd bytes ('%s')", __func__, nRead, b64 );
#ifdef COMMS_CHECKSUM #ifdef COMMS_CHECKSUM
gchar* sum = g_compute_checksum_for_data( G_CHECKSUM_MD5, buf, nRead ); gchar* sum = g_compute_checksum_for_data( G_CHECKSUM_MD5, buf, nRead );
XP_LOGF( "%s: read %zd bytes ('%s')(sum=%s)", __func__, nRead, b64, sum ); XP_LOGF( "%s: read %zd bytes ('%s')(sum=%s)", __func__, nRead, b64, sum );
g_free( sum ); g_free( sum );
#else
XP_LOGF( "%s: read %zd bytes ('%s')", __func__, nRead, b64 );
#endif #endif
g_free( b64 ); g_free( b64 );
return process( storage, buf, nRead );
gboolean result = process( storage, buf, nRead );
// LOG_RETURNF( "%d", result );
return result;
} }
void void

View file

@ -34,7 +34,6 @@ typedef struct _Procs {
XP_U16 maxInterval ); XP_U16 maxInterval );
void (*msgErrorMsg)( void* closure, const XP_UCHAR* msg ); void (*msgErrorMsg)( void* closure, const XP_UCHAR* msg );
void (*inviteReceived)( void* closure, NetLaunchInfo* invit ); void (*inviteReceived)( void* closure, NetLaunchInfo* invit );
SocketAddedFunc socketAdded;
} RelayConnProcs; } RelayConnProcs;
void relaycon_init( LaunchParams* params, const RelayConnProcs* procs, void relaycon_init( LaunchParams* params, const RelayConnProcs* procs,

View file

@ -163,7 +163,7 @@ class Device():
sTilesLeftTrayPat = re.compile('.*player \d+ now has (\d+) tiles') sTilesLeftTrayPat = re.compile('.*player \d+ now has (\d+) tiles')
sRelayIDPat = re.compile('.*UPDATE games.*seed=(\d+),.*relayid=\'([^\']+)\'.*') sRelayIDPat = re.compile('.*UPDATE games.*seed=(\d+),.*relayid=\'([^\']+)\'.*')
def __init__(self, args, game, indx, params, room, peers, db, log, nInGame): def __init__(self, args, game, indx, params, room, peers, db, log, script, nInGame):
self.game = game self.game = game
self.indx = indx self.indx = indx
self.args = args self.args = args
@ -173,6 +173,7 @@ class Device():
self.room = room self.room = room
self.db = db self.db = db
self.logPath = log self.logPath = log
self.script = script
self.nInGame = nInGame self.nInGame = nInGame
# runtime stuff; init now # runtime stuff; init now
self.app = args.APP_OLD self.app = args.APP_OLD
@ -198,6 +199,8 @@ class Device():
if pct >= random.randint(0, 99): if pct >= random.randint(0, 99):
print('launch(): upgrading from ', self.app, ' to ', self.args.APP_NEW) print('launch(): upgrading from ', self.app, ' to ', self.args.APP_NEW)
self.app = self.args.APP_NEW self.app = self.args.APP_NEW
# nuke script to force regeneration
os.unlink(self.script)
def logReaderMain(self): def logReaderMain(self):
assert self and self.proc assert self and self.proc
@ -234,19 +237,26 @@ class Device():
# print('logReaderMain done, wrote lines:', nLines, 'to', self.logPath); # print('logReaderMain done, wrote lines:', nLines, 'to', self.logPath);
def checkScript(self):
if not os.path.exists(self.script):
args = ['exec'] # without exec means terminate() won't work
if self.args.VALGRIND:
args += ['valgrind']
# args += ['--leak-check=full']
# args += ['--track-origins=yes']
args += [self.app] + [str(p) for p in self.params]
if self.devID: args.extend( ' '.split(self.devID))
args += [ '$*' ]
with open( self.script, 'w' ) as fil:
fil.write( "#!/bin/sh\n" )
fil.write( ' '.join(args) + '\n' )
os.chmod(self.script, 0o755)
def launch(self): def launch(self):
args = []
if self.args.VALGRIND:
args += ['valgrind']
# args += ['--leak-check=full']
# args += ['--track-origins=yes']
# Upgrade if appropriate
self.setApp(self.args.UPGRADE_PCT) self.setApp(self.args.UPGRADE_PCT)
self.checkScript()
args += [self.app] + [str(p) for p in self.params]
if self.devID: args.extend( ' '.split(self.devID))
self.launchCount += 1 self.launchCount += 1
args = [ self.script ]
self.proc = subprocess.Popen(args, stdout = subprocess.DEVNULL, self.proc = subprocess.Popen(args, stdout = subprocess.DEVNULL,
stderr = subprocess.PIPE, universal_newlines = True) stderr = subprocess.PIPE, universal_newlines = True)
self.pid = self.proc.pid self.pid = self.proc.pid
@ -287,8 +297,8 @@ class Device():
def moveFiles(self): def moveFiles(self):
assert not self.running() assert not self.running()
shutil.move(self.logPath, self.args.LOGDIR + '/done') for fil in [ self.logPath, self.db, self.script ]:
shutil.move(self.db, self.args.LOGDIR + '/done') shutil.move(fil, self.args.LOGDIR + '/done')
def send_dead(self): def send_dead(self):
if self.args.ADD_RELAY: if self.args.ADD_RELAY:
@ -393,6 +403,7 @@ def build_cmds(args):
DEV += 1 DEV += 1
DB = '{}/{:02d}_{:02d}_DB.sql3'.format(args.LOGDIR, GAME, DEV) DB = '{}/{:02d}_{:02d}_DB.sql3'.format(args.LOGDIR, GAME, DEV)
LOG = '{}/{:02d}_{:02d}_LOG.txt'.format(args.LOGDIR, GAME, DEV) LOG = '{}/{:02d}_{:02d}_LOG.txt'.format(args.LOGDIR, GAME, DEV)
SCRIPT = '{}/start_{:02d}_{:02d}.sh'.format(args.LOGDIR, GAME, DEV)
PARAMS = player_params(args, NLOCALS, NPLAYERS, DEV) PARAMS = player_params(args, NLOCALS, NPLAYERS, DEV)
PARAMS += PLAT_PARMS PARAMS += PLAT_PARMS
@ -442,7 +453,7 @@ def build_cmds(args):
# print('PARAMS:', PARAMS) # print('PARAMS:', PARAMS)
dev = Device(args, GAME, COUNTER, PARAMS, ROOM, peers, DB, LOG, len(LOCALS)) dev = Device(args, GAME, COUNTER, PARAMS, ROOM, peers, DB, LOG, SCRIPT, len(LOCALS))
peers.add(dev) peers.add(dev)
dev.update_ldevid() dev.update_ldevid()
devs.append(dev) devs.append(dev)
@ -515,7 +526,7 @@ def summarizeTileCounts(devs, endTime, state):
state['lastChange'] = now state['lastChange'] = now
state['tilesStr'] = tilesStr state['tilesStr'] = tilesStr
return now - state['lastChange'] < datetime.timedelta(minutes = 1) return now - state['lastChange'] < datetime.timedelta(seconds = 30)
def countCores(): def countCores():
return len(glob.glob1('/tmp',"core*")) return len(glob.glob1('/tmp',"core*"))
@ -549,18 +560,7 @@ def run_cmds(args, devs):
if dev.handleAllDone(): if dev.handleAllDone():
devs.remove(dev) devs.remove(dev)
else: else:
# if [ -n "$ONE_PER_ROOM" -a 0 -ne ${ROOM_PIDS[$ROOM]} ]; then
# continue
# fi
# try_upgrade $KEY
# try_upgrade_upd $KEY
dev.launch() dev.launch()
# PID=$!
# # renice doesn't work on one of my machines...
# renice -n 1 -p $PID >/dev/null 2>&1 || /bin/true
# PIDS[$KEY]=$PID
# ROOM_PIDS[$ROOM]=$PID
# MINEND[$KEY]=$(($NOW + $MINRUN))
elif dev.minTimeExpired(): elif dev.minTimeExpired():
dev.kill() dev.kill()
if dev.handleAllDone(): if dev.handleAllDone():

View file

@ -3,7 +3,26 @@
# This script just runs the curses app with a set of params known to # This script just runs the curses app with a set of params known to
# work. At least when it was last committed. :-) # work. At least when it was last committed. :-)
usage() {
echo "usage: $0 [--help] [param-for-xwords]*"
exit 1
}
PARAMS=''
while [ $# -gt 0 ]; do
case $1 in
--help)
usage;
;;
*)
PARAMS="$PARAMS $1"
;;
esac
shift
done
WD=$(cd $(dirname $0)/.. && pwd) WD=$(cd $(dirname $0)/.. && pwd)
cd $WD cd $WD
./obj_linux_memdbg/xwords --curses --name Eric --robot Kati --dict-dir ./ --game-dict ../dict.xwd 2>/dev/null ./obj_linux_memdbg/xwords --curses --name Eric --name Kati \
--dict-dir ./ --game-dict ../dict.xwd \
$PARAMS \