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

View file

@ -53,7 +53,7 @@ load( XW_DUtilCtxt* dutil )
if ( 0 < stream_getSize( stream ) ) {
state->devCount = stream_getU16( stream );
++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 {
XP_LOGF( "%s(): empty stream!!", __func__ );
}

View file

@ -388,6 +388,13 @@ game_getState( const XWGame* game, GameStateInfo* gsi )
comms_countPendingPackets(game->comms) : 0;
}
XP_Bool
game_getIsServer( const XWGame* game )
{
XP_Bool result = comms_getIsServer( game->comms );
return result;
}
void
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, nColsNBits, gi->boardSize );
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, 2, gi->phoniesAction );
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_getState( const XWGame* game, GameStateInfo* gsi );
XP_Bool game_getIsServer( const XWGame* game );
void gi_initPlayerInfo( MPFORMAL CurGameInfo* gi,
const XP_UCHAR* nameTemplate );

View file

@ -237,8 +237,10 @@ syncPlayers( ServerCtxt* server )
static XP_Bool
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
@ -867,7 +869,6 @@ makeRobotMove( ServerCtxt* server )
XP_Bool result = XP_FALSE;
XP_Bool searchComplete;
XP_S16 turn;
const TrayTileSet* tileSet;
MoveInfo newMove = {0};
ModelCtxt* model = server->vol.model;
CurGameInfo* gi = server->vol.gi;
@ -899,7 +900,7 @@ makeRobotMove( ServerCtxt* server )
model_resetCurrentTurn( model, turn );
if ( !forceTrade ) {
tileSet = model_getPlayerTiles( model, turn );
const TrayTileSet* tileSet = model_getPlayerTiles( model, turn );
#ifdef XWFEATURE_BONUSALL
XP_U16 allTilesBonus = server_figureFinishBonus( server, turn );
#endif
@ -1143,7 +1144,7 @@ server_do( ServerCtxt* server )
} else {
XP_Bool moreToDo = XP_FALSE;
server->serverDoing = XP_TRUE;
XP_LOGF( "%s(): gameState: %s", __func__, getStateStr(server->nv.gameState) );
switch( server->nv.gameState ) {
case XWSTATE_BEGIN:
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 isServer = amServer( server );
const XW_Proto code = readProto( server, incoming );
XP_LOGF( "%s(code=%s)", __func__, codeToStr(code) );
if ( code == XWPROTO_DEVICE_REGISTRATION ) {
accepted = isServer;

View file

@ -257,7 +257,7 @@ smsproto_prepOutbound( SMSProto* state, SMS_CMD cmd, XP_U32 gameID,
}
static SMSMsgArray*
appendLocMsg( SMSProto* state, SMSMsgArray* arr, SMSMsgLoc* msg )
appendLocMsg( SMSProto* XP_UNUSED_DBG(state), SMSMsgArray* arr, SMSMsgLoc* msg )
{
if ( NULL == arr ) {
arr = XP_CALLOC( state->mpool, sizeof(*arr) );
@ -273,7 +273,7 @@ appendLocMsg( SMSProto* state, SMSMsgArray* arr, SMSMsgLoc* msg )
}
static SMSMsgArray*
appendNetMsg( SMSProto* state, SMSMsgArray* arr, SMSMsgNet* msg )
appendNetMsg( SMSProto* XP_UNUSED_DBG(state), SMSMsgArray* arr, SMSMsgNet* msg )
{
if ( NULL == arr ) {
arr = XP_CALLOC( state->mpool, sizeof(*arr) );
@ -396,7 +396,7 @@ smsproto_freeMsgArray( SMSProto* state, SMSMsgArray* arr )
}
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 );
@ -595,7 +595,7 @@ rmFromPhoneRec( SMSProto* state, int fromPhoneIndex )
}
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];
MsgIDRec* msgIDRec = &fromPhoneRec->msgIDRecs[msgIDIndex];

View file

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

View file

@ -21,6 +21,7 @@ BUILD_DIR ?= .
ifeq ($(MEMDEBUG),TRUE)
DEFINES = -DMEM_DEBUG -DDEBUG -DENABLE_LOGGING -DNUMBER_KEY_AS_INDEX
DEFINES += -DCOMMS_CHECKSUM
DEFINES += -DLOG_COMMS_MSGNOS
CFLAGS += -g $(GPROFFLAG) -Wall -Wunused-parameter -Wcast-align -Werror -O0
# DEFINES += -DDEBUG_HASHING
CFLAGS += -DDEBUG_TS -rdynamic
@ -144,8 +145,6 @@ DEFINES += -DCURSES_CELL_WIDTH=$(CURSES_CELL_WIDTH)
endif
DEFINES += $(UNICODE)
DEFINES += -DLOG_COMMS_MSGNOS
# Networking-related features. Only set these if STANDALONE is not set
ifeq ($(STANDALONE),)
@ -210,16 +209,19 @@ endif
ifdef DO_CURSES
CURSES_OBJS = \
$(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)/cursesask.o \
$(BUILD_PLAT_DIR)/cursesdlgutil.o \
$(BUILD_PLAT_DIR)/cursesletterask.o
endif
ifndef LIB_NO_UI
MAIN_OBJS = $(BUILD_PLAT_DIR)/linuxmain.o
MAIN_OBJS = $(BUILD_PLAT_DIR)/linuxmain.o \
$(BUILD_PLAT_DIR)/gsrcwrap.o
endif
OBJ = \
$(BUILD_PLAT_DIR)/filestream.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.
*/
int
cursesask( CursesAppGlobals* globals, const char* question, short numButtons,
cursesask( WINDOW* window, const char* question, short numButtons,
const char** buttons )
{
WINDOW* confWin;
int x, y, rows, row, nLines;
int left, top;
short newSelButton = 0;
short curSelButton = 1; /* force draw by being different */
short spacePerButton, num;
@ -45,7 +46,8 @@ cursesask( CursesAppGlobals* globals, const char* question, short numButtons,
FormatInfo fi;
int len;
getmaxyx(globals->boardWin, y, x);
getmaxyx( window, y, x);
getbegyx( window, top, left );
measureAskText( question, x-2, &fi );
len = fi.maxLen;
@ -62,8 +64,8 @@ cursesask( CursesAppGlobals* globals, const char* question, short numButtons,
}
nLines = ASK_HEIGHT + rows - 1;
confWin = newwin( nLines, len+(PAD*2),
(y/2) - (nLines/2), (x-len-2)/2 );
confWin = newwin( nLines, len+(PAD*2), top + ((y/2) - (nLines/2)),
left + ((x-len-2)/2) );
keypad( confWin, TRUE );
wclear( confWin );
box( confWin, '|', '-');
@ -120,9 +122,9 @@ cursesask( CursesAppGlobals* globals, const char* question, short numButtons,
delwin( confWin );
/* this leaves a ghost line, but I can't figure out a better way. */
wtouchln( globals->boardWin, (y/2)-(nLines/2), ASK_HEIGHT + rows - 1, 1 );
wrefresh( globals->boardWin );
wtouchln( window, (y/2)-(nLines/2), ASK_HEIGHT + rows - 1, 1 );
wrefresh( window );
return curSelButton;
} /* ask */
} /* cursesask */
#endif /* PLATFORM_NCURSES */

View file

@ -22,7 +22,7 @@
#include "cursesmain.h"
int cursesask( CursesAppGlobals* globals, const char* question,
int cursesask( WINDOW* window, const char* question,
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;
} /* curses_drawctxt_init */
void
cursesDrawCtxtFree( DrawCtx* pdctx )
{
CursesDrawCtx* dctx = (CursesDrawCtx*)pdctx;
free( dctx->vtable );
free( dctx );
}
#endif /* PLATFORM_NCURSES */

View file

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

View file

@ -23,7 +23,7 @@
#include "linuxmain.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 );

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; -*- */
/*
* 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
* modify it under the terms of the GNU General Public License
@ -39,11 +39,17 @@
#include "server.h"
#include "xwstate.h"
#include "util.h"
#include "cursesmenu.h"
#include "cursesboard.h"
/* #include "compipe.h" */
typedef struct CursesAppGlobals CursesAppGlobals;
typedef struct CursesBoardGlobals CursesBoardGlobals;
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); */
#define FD_MAX 6
@ -51,56 +57,10 @@ typedef XP_Bool (*EventFunc)(CursesAppGlobals* globals, int ch);
#define FD_TIMEEVT 1
#define FD_FIRSTSOCKET 2
struct CursesAppGlobals {
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
};
// typedef struct CursesBoardGlobals;
DrawCtx* cursesDrawCtxtMake( WINDOW* boardWin );
void cursesDrawCtxtFree( DrawCtx* dctx );
/* 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,
@ -114,5 +74,6 @@ DrawCtx* cursesDrawCtxtMake( WINDOW* boardWin );
void cursesmain( XP_Bool isServer, LaunchParams* params );
bool handleQuit( void* closure, int unused_key );
#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)"
",nmoves INT"
",seed INT"
",nPending INT"
",role INT"
",dictlang INT"
",gameid INT"
",ntotal INT(2)"
",nmissing INT(2)"
",lastMoveTime INT"
",scores TEXT"
",dupTimerExpires INT"
")";
result = sqlite3_exec( pDb, createGamesStr, NULL, NULL, NULL );
@ -161,17 +165,17 @@ void
writeToDB( XWStreamCtxt* stream, void* closure )
{
CommonGlobals* cGlobals = (CommonGlobals*)closure;
sqlite3_int64 selRow = cGlobals->selRow;
sqlite3_int64 selRow = cGlobals->rowid;
sqlite3* pDb = cGlobals->params->pDb;
XP_Bool newGame = -1 == selRow;
selRow = writeBlobColumnStream( stream, pDb, selRow, "game" );
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 );
} else {
assert( selRow == cGlobals->selRow );
assert( selRow == cGlobals->rowid );
}
(*cGlobals->onSave)( cGlobals->onSaveClosure, selRow, newGame );
@ -193,8 +197,8 @@ addSnapshot( CommonGlobals* cGlobals )
cGlobals->params->vtMgr );
getImage( dctx, stream );
removeSurface( dctx );
cGlobals->selRow = writeBlobColumnStream( stream, cGlobals->params->pDb,
cGlobals->selRow, "snap" );
cGlobals->rowid = writeBlobColumnStream( stream, cGlobals->params->pDb,
cGlobals->rowid, "snap" );
stream_destroy( stream );
}
@ -215,8 +219,11 @@ summarize( CommonGlobals* cGlobals )
XP_U32 lastMoveTime = server_getLastMoveTime( game->server );
XP_U16 seed = 0;
XP_S16 nMissing = 0;
XP_U16 nTotal = cGlobals->gi->nPlayers;
XP_U32 gameID = cGlobals->gi->gameID;
XP_U16 nPending = 0;
const CurGameInfo* gi = cGlobals->gi;
XP_U16 nTotal = gi->nPlayers;
XP_U32 gameID = gi->gameID;
XP_LangCode dictLang = gi->dictLang;
XP_ASSERT( 0 != gameID );
CommsAddrRec addr = {0};
gchar* room = "";
@ -225,6 +232,24 @@ summarize( CommonGlobals* cGlobals )
gchar connvia[128] = {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 ) {
nMissing = server_getMissingPlayers( game->server );
comms_getAddr( game->comms, &addr );
@ -255,19 +280,21 @@ summarize( CommonGlobals* cGlobals )
seed = comms_getChannelSeed( game->comms );
XP_U16 len = VSIZE(relayID);
(void)comms_getRelayID( game->comms, relayID, &len );
nPending = comms_countPendingPackets( game->comms );
} else {
strcat( connvia, "local" );
}
const char* fmt = "UPDATE games "
" SET room='%s', ended=%d, turn=%d, local=%d, ntotal=%d, "
" nmissing=%d, nmoves=%d, seed=%d, gameid=%d, connvia='%s', "
" relayid='%s', lastMoveTime=%d"
" nmissing=%d, nmoves=%d, seed=%d, dictlang=%d, gameid=%d, connvia='%s', "
" relayid='%s', lastMoveTime=%d, scores='%s', nPending=%d, role=%d"
" 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,
nTotal, nMissing, nMoves, seed, gameID, connvia, relayID, lastMoveTime,
cGlobals->selRow );
nTotal, nMissing, nMoves, seed, dictLang, gameID, connvia, relayID, lastMoveTime,
scoresStr, nPending, gi->serverRole, cGlobals->rowid );
XP_LOGF( "query: %s", buf );
sqlite3_stmt* 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 ) {
addSnapshot( cGlobals );
}
g_free( scoresStr );
}
GSList*
@ -370,7 +398,7 @@ getGameInfo( sqlite3* pDb, sqlite3_int64 rowid, GameInfo* gib )
{
XP_Bool success = XP_FALSE;
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";
XP_UCHAR query[256];
snprintf( query, sizeof(query), fmt, rowid );
@ -384,12 +412,14 @@ getGameInfo( sqlite3* pDb, sqlite3_int64 rowid, GameInfo* gib )
int col = 0;
int len = sizeof(gib->room);
getColumnText( ppStmt, col++, gib->room, &len );
gib->rowid = rowid;
gib->gameOver = 1 == sqlite3_column_int( ppStmt, col++ );
gib->turn = sqlite3_column_int( ppStmt, col++ );
gib->turnLocal = 1 == sqlite3_column_int( ppStmt, col++ );
gib->nMoves = sqlite3_column_int( ppStmt, col++ );
gib->nTotal = 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++ );
len = sizeof(gib->conn);
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++ );
len = sizeof(gib->relayID);
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 );
#ifdef PLATFORM_GTK
@ -533,8 +567,11 @@ db_fetch( sqlite3* pDb, const gchar* key, gchar* buf, gint* buflen )
XP_ASSERT( !!pDb );
FetchResult result = NOT_THERE;
char query[256];
int len = snprintf( query, sizeof(query),
"SELECT value from pairs where key = '%s'", key );
#ifdef DEBUG
int len =
#endif
snprintf( query, sizeof(query),
"SELECT value from pairs where key = '%s'", key );
XP_ASSERT( len < sizeof(query) );
sqlite3_stmt *ppStmt;
int sqlResult = sqlite3_prepare_v2( pDb, query, -1, &ppStmt, NULL );

View file

@ -28,13 +28,16 @@
#include "comtypes.h"
typedef struct _GameInfo {
sqlite3_int64 rowid;
XP_UCHAR name[128];
XP_UCHAR room[128];
XP_UCHAR conn[128];
XP_UCHAR scores[128];
XP_UCHAR relayID[32];
#ifdef PLATFORM_GTK
GdkPixbuf* snap;
#endif
XP_LangCode dictLang;
XP_U32 gameID;
XP_S16 nMoves;
XP_Bool gameOver;
@ -43,7 +46,9 @@ typedef struct _GameInfo {
XP_U16 nTotal;
XP_S16 nMissing;
XP_U16 seed;
XP_U16 nPending;
XP_U32 lastMoveTime;
XP_U16 role;
} GameInfo;
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) );
GtkGameGlobals* globals = (GtkGameGlobals*)closure;
if ( !!globals->draw ) {
globals->cGlobals.state = state;
CommonGlobals* cGlobals = &globals->cGlobals;
if ( !!cGlobals->draw ) {
cGlobals->state = 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;
XP_Bool success = XP_FALSE;
LaunchParams* params = globals->cGlobals.params;
if ( params->useUdp && !globals->draw ) {
XP_U16 seed = comms_getChannelSeed( globals->cGlobals.game.comms );
XP_U32 clientToken = makeClientToken( globals->cGlobals.selRow, seed );
CommonGlobals* cGlobals = &globals->cGlobals;
LaunchParams* params = cGlobals->params;
if ( params->useUdp && !cGlobals->draw ) {
XP_U16 seed = comms_getChannelSeed( cGlobals->game.comms );
XP_U32 clientToken = makeClientToken( cGlobals->rowid, seed );
XP_S16 nSent = relaycon_sendnoconn( params, msg, len, relayID,
clientToken );
success = nSent == len;
@ -430,18 +432,6 @@ relay_sendNoConn_gtk( const XP_U8* msg, XP_U16 len,
return success;
} /* 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
static void
onJoined( void* closure, const XP_UCHAR* connname, XWHostID hid )
@ -478,7 +468,7 @@ gtk_getFlags( void* closure )
XP_USE( globals );
return COMMS_XPORT_FLAGS_HASNOCONN;
# else
return (!!globals->draw) ? COMMS_XPORT_FLAGS_NONE
return (!!globals->cGlobals.draw) ? COMMS_XPORT_FLAGS_NONE
: COMMS_XPORT_FLAGS_HASNOCONN;
# endif
}
@ -584,167 +574,25 @@ addDropChecks( GtkGameGlobals* globals )
static void
createOrLoadObjects( GtkGameGlobals* globals )
{
XWStreamCtxt* stream = NULL;
XP_Bool opened = XP_FALSE;
#ifndef XWFEATURE_STANDALONE_ONLY
#endif
CommonGlobals* cGlobals = &globals->cGlobals;
LaunchParams* params = cGlobals->params;
globals->draw = (GtkDrawCtx*)gtkDrawCtxtMake( globals->drawing_area,
globals );
cGlobals->draw = gtkDrawCtxtMake( globals->drawing_area,
globals );
TransportProcs procs;
setTransportProcs( &procs, globals );
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
}
}
linuxOpenGame( cGlobals, &procs );
if ( !params->fileName && !!params->dbName ) {
XP_UCHAR buf[64];
snprintf( buf, sizeof(buf), "%s / %lld", params->dbName,
cGlobals->selRow );
cGlobals->rowid );
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 );
disenable_buttons( globals );
@ -758,11 +606,11 @@ configure_event( GtkWidget* widget, GdkEventConfigure* XP_UNUSED(event),
GtkGameGlobals* globals )
{
globals->gridOn = XP_TRUE;
if ( globals->draw == NULL ) {
CommonGlobals* cGlobals = &globals->cGlobals;
if ( cGlobals->draw == NULL ) {
createOrLoadObjects( globals );
}
CommonGlobals* cGlobals = &globals->cGlobals;
BoardCtxt* board = cGlobals->game.board;
GtkAllocation alloc;
@ -796,7 +644,7 @@ destroy_board_window( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
if ( !!globals->cGlobals.game.comms ) {
comms_stop( globals->cGlobals.game.comms );
}
saveGame( &globals->cGlobals );
linuxSaveGame( &globals->cGlobals );
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! */
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
cGlobals->params->vtMgr );
if ( loadInviteAddrs( stream, cGlobals->params->pDb, cGlobals->selRow ) ) {
if ( loadInviteAddrs( stream, cGlobals->params->pDb, cGlobals->rowid ) ) {
CommsAddrRec addr = {0};
addrFromStream( &addr, stream );
comms_setAddr( cGlobals->game.comms, &addr );
@ -840,7 +688,7 @@ static void
cleanup( GtkGameGlobals* globals )
{
CommonGlobals* cGlobals = &globals->cGlobals;
saveGame( cGlobals );
linuxSaveGame( cGlobals );
if ( 0 < globals->idleID ) {
g_source_remove( globals->idleID );
}
@ -955,7 +803,7 @@ new_game_impl( GtkGameGlobals* globals, XP_Bool fireConnDlg )
}
CurGameInfo* gi = cGlobals->gi;
success = newGameDialog( globals, gi, &addr, XP_TRUE, fireConnDlg );
success = gtkNewGameDialog( globals, gi, &addr, XP_TRUE, fireConnDlg );
if ( success ) {
#ifndef XWFEATURE_STANDALONE_ONLY
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
server_do in case one's become a robot. */
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 ) ) {
board_draw( globals->cGlobals.game.board );
}
@ -1858,7 +1706,7 @@ static void
gtk_util_turnChanged( XW_UtilCtxt* uc, XP_S16 XP_UNUSED(newTurn) )
{
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
saveGame( &globals->cGlobals );
linuxSaveGame( &globals->cGlobals );
}
#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 DONT_ABORT_ENGINE */
@ -2334,14 +2166,6 @@ gtk_util_notifyMove( XW_UtilCtxt* uc, XWStreamCtxt* stream )
XP_ASSERT( len <= VSIZE(cGlobals->question) );
stream_getBytes( stream, cGlobals->question, len );
(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 */
static gint
@ -2501,52 +2325,53 @@ makeButtons( GtkGameGlobals* globals )
static void
setupGtkUtilCallbacks( GtkGameGlobals* globals, XW_UtilCtxt* util )
{
util->vtable->m_util_userError = gtk_util_userError;
util->vtable->m_util_notifyMove = gtk_util_notifyMove;
util->vtable->m_util_notifyTrade = gtk_util_notifyTrade;
util->vtable->m_util_notifyPickTileBlank = gtk_util_notifyPickTileBlank;
util->vtable->m_util_informNeedPickTiles = gtk_util_informNeedPickTiles;
util->vtable->m_util_informNeedPassword = gtk_util_informNeedPassword;
util->vtable->m_util_trayHiddenChange = gtk_util_trayHiddenChange;
util->vtable->m_util_yOffsetChange = gtk_util_yOffsetChange;
util->closure = globals;
#define SET_PROC(NAM) util->vtable->m_util_##NAM = gtk_util_##NAM
SET_PROC(userError);
SET_PROC(notifyMove);
SET_PROC(notifyTrade);
SET_PROC(notifyPickTileBlank);
SET_PROC(informNeedPickTiles);
SET_PROC(informNeedPassword);
SET_PROC(trayHiddenChange);
SET_PROC(yOffsetChange);
#ifdef XWFEATURE_TURNCHANGENOTIFY
util->vtable->m_util_turnChanged = gtk_util_turnChanged;
SET_PROC(turnChanged);
#endif
util->vtable->m_util_informMove = gtk_util_informMove;
util->vtable->m_util_informUndo = gtk_util_informUndo;
util->vtable->m_util_notifyGameOver = gtk_util_notifyGameOver;
util->vtable->m_util_informNetDict = gtk_util_informNetDict;
util->vtable->m_util_setIsServer = gtk_util_setIsServer;
SET_PROC(informMove);
SET_PROC(informUndo);
SET_PROC(notifyGameOver);
SET_PROC(informNetDict);
/* SET_PROC(setIsServer); */
#ifdef XWFEATURE_HILITECELL
util->vtable->m_util_hiliteCell = gtk_util_hiliteCell;
SET_PROC(hiliteCell);
#endif
util->vtable->m_util_altKeyDown = gtk_util_altKeyDown;
util->vtable->m_util_engineProgressCallback =
gtk_util_engineProgressCallback;
util->vtable->m_util_setTimer = gtk_util_setTimer;
util->vtable->m_util_clearTimer = gtk_util_clearTimer;
util->vtable->m_util_requestTime = gtk_util_requestTime;
util->vtable->m_util_notifyIllegalWords = gtk_util_notifyIllegalWords;
util->vtable->m_util_remSelected = gtk_util_remSelected;
SET_PROC(altKeyDown);
SET_PROC(engineProgressCallback);
SET_PROC(setTimer);
SET_PROC(clearTimer);
SET_PROC(requestTime);
SET_PROC(notifyIllegalWords);
SET_PROC(remSelected);
#ifndef XWFEATURE_STANDALONE_ONLY
util->vtable->m_util_makeStreamFromAddr = gtk_util_makeStreamFromAddr;
SET_PROC(makeStreamFromAddr);
#endif
#ifdef XWFEATURE_CHAT
util->vtable->m_util_showChat = gtk_util_showChat;
SET_PROC(showChat);
#endif
#ifdef XWFEATURE_SEARCHLIMIT
util->vtable->m_util_getTraySearchLimits = gtk_util_getTraySearchLimits;
SET_PROC(getTraySearchLimits);
#endif
#ifndef XWFEATURE_MINIWIN
util->vtable->m_util_bonusSquareHeld = gtk_util_bonusSquareHeld;
util->vtable->m_util_playerScoreHeld = gtk_util_playerScoreHeld;
SET_PROC(bonusSquareHeld);
SET_PROC(playerScoreHeld);
#endif
#ifdef XWFEATURE_BOARDWORDS
util->vtable->m_util_cellSquareHeld = gtk_util_cellSquareHeld;
SET_PROC(cellSquareHeld);
#endif
util->closure = globals;
#undef SET_PROC
assertAllCallbacksSet( util );
} /* setupGtkUtilCallbacks */
#ifndef XWFEATURE_STANDALONE_ONLY
@ -2556,33 +2381,6 @@ typedef struct _SockInfo {
int socket;
} 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
acceptorInput( GIOChannel* source, GIOCondition condition, gpointer data )
{
@ -2671,44 +2469,44 @@ initGlobalsNoDraw( GtkGameGlobals* globals, LaunchParams* params,
{
memset( globals, 0, sizeof(*globals) );
globals->cGlobals.gi = &globals->gi;
CommonGlobals* cGlobals = &globals->cGlobals;
cGlobals->gi = &cGlobals->_gi;
if ( !gi ) {
gi = &params->pgi;
}
gi_copy( MPPARM(params->mpool) globals->cGlobals.gi, gi );
gi_copy( MPPARM(params->mpool) cGlobals->gi, gi );
globals->cGlobals.params = params;
globals->cGlobals.lastNTilesToUse = MAX_TRAY_TILES;
cGlobals->params = params;
cGlobals->lastNTilesToUse = MAX_TRAY_TILES;
#ifndef XWFEATURE_STANDALONE_ONLY
# ifdef XWFEATURE_RELAY
globals->cGlobals.relaySocket = -1;
cGlobals->relaySocket = -1;
# endif
globals->cGlobals.socketAdded = gtk_socket_added;
globals->cGlobals.socketAddedClosure = globals;
globals->cGlobals.onSave = onGameSaved;
globals->cGlobals.onSaveClosure = globals;
globals->cGlobals.addAcceptor = gtk_socket_acceptor;
cGlobals->socketAddedClosure = globals;
cGlobals->onSave = gtkOnGameSaved;
cGlobals->onSaveClosure = globals;
cGlobals->addAcceptor = gtk_socket_acceptor;
#endif
globals->cGlobals.cp.showBoardArrow = XP_TRUE;
globals->cGlobals.cp.hideTileValues = params->hideValues;
globals->cGlobals.cp.skipCommitConfirm = params->skipCommitConfirm;
globals->cGlobals.cp.sortNewTiles = params->sortNewTiles;
globals->cGlobals.cp.showColors = params->showColors;
globals->cGlobals.cp.allowPeek = params->allowPeek;
globals->cGlobals.cp.showRobotScores = params->showRobotScores;
cGlobals->cp.showBoardArrow = XP_TRUE;
cGlobals->cp.hideTileValues = params->hideValues;
cGlobals->cp.skipCommitConfirm = params->skipCommitConfirm;
cGlobals->cp.sortNewTiles = params->sortNewTiles;
cGlobals->cp.showColors = params->showColors;
cGlobals->cp.allowPeek = params->allowPeek;
cGlobals->cp.showRobotScores = params->showRobotScores;
#ifdef XWFEATURE_SLOW_ROBOT
globals->cGlobals.cp.robotThinkMin = params->robotThinkMin;
globals->cGlobals.cp.robotThinkMax = params->robotThinkMax;
globals->cGlobals.cp.robotTradePct = params->robotTradePct;
cGlobals->cp.robotThinkMin = params->robotThinkMin;
cGlobals->cp.robotThinkMax = params->robotThinkMax;
cGlobals->cp.robotTradePct = params->robotTradePct;
#endif
#ifdef XWFEATURE_CROSSHAIRS
globals->cGlobals.cp.hideCrosshairs = params->hideCrosshairs;
cGlobals->cp.hideCrosshairs = params->hideCrosshairs;
#endif
setupUtil( &globals->cGlobals );
setupGtkUtilCallbacks( globals, globals->cGlobals.util );
setupUtil( cGlobals );
setupGtkUtilCallbacks( globals, cGlobals->util );
}
/* 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;
board_invalAll( globals->cGlobals.game.board );
board_draw( globals->cGlobals.game.board );
draw_gtk_status( globals->draw, globals->stateChar );
CommonGlobals* cGlobals = &globals->cGlobals;
board_invalAll( cGlobals->game.board );
board_draw( cGlobals->game.board );
draw_gtk_status( (GtkDrawCtx*)cGlobals->draw, globals->stateChar );
XP_USE(widget);
XP_USE(cr);
@ -2737,7 +2536,8 @@ on_draw_event( GtkWidget* widget, cairo_t* cr, gpointer user_data )
}
void
initGlobals( GtkGameGlobals* globals, LaunchParams* params, CurGameInfo* gi )
initBoardGlobalsGtk( GtkGameGlobals* globals, LaunchParams* params,
CurGameInfo* gi )
{
CommonGlobals* cGlobals = &globals->cGlobals;
short width, height;
@ -2746,7 +2546,6 @@ initGlobals( GtkGameGlobals* globals, LaunchParams* params, CurGameInfo* gi )
GtkWidget* menubar;
GtkWidget* vbox;
GtkWidget* hbox;
gulong id;
initGlobalsNoDraw( globals, params, gi );
if ( !!gi ) {
@ -2765,12 +2564,18 @@ initGlobals( GtkGameGlobals* globals, LaunchParams* params, CurGameInfo* gi )
gtk_container_add( GTK_CONTAINER(window), vbox );
gtk_widget_show( vbox );
id = g_signal_connect( window, "destroy", G_CALLBACK(destroy_board_window),
globals );
#ifdef DEBUG
gulong id =
#endif
g_signal_connect( window, "destroy", G_CALLBACK(destroy_board_window),
globals );
XP_ASSERT( id > 0 );
XP_ASSERT( !!globals );
id = g_signal_connect( window, "show", G_CALLBACK( on_board_window_shown ),
globals );
#ifdef DEBUG
id =
#endif
g_signal_connect( window, "show", G_CALLBACK( on_board_window_shown ),
globals );
XP_ASSERT( id > 0 );
menubar = makeMenus( globals );
@ -2786,8 +2591,11 @@ initGlobals( GtkGameGlobals* globals, LaunchParams* params, CurGameInfo* gi )
drawing_area = gtk_drawing_area_new();
gtk_widget_add_events( drawing_area, GDK_ALL_EVENTS_MASK );
id = g_signal_connect(G_OBJECT(drawing_area), "draw",
G_CALLBACK(on_draw_event), globals);
#ifdef DEBUG
id =
#endif
g_signal_connect(G_OBJECT(drawing_area), "draw",
G_CALLBACK(on_draw_event), globals);
XP_ASSERT( id > 0 );
globals->drawing_area = drawing_area;
@ -2815,8 +2623,11 @@ initGlobals( GtkGameGlobals* globals, LaunchParams* params, CurGameInfo* gi )
gtk_adjustment_new( 0, 0, nRows, 1, 2,
nRows - params->nHidden );
vscrollbar = gtk_scrollbar_new( GTK_ORIENTATION_VERTICAL, globals->adjustment );
id = g_signal_connect( globals->adjustment, "value_changed",
G_CALLBACK(scroll_value_changed), globals );
#ifdef DEBUG
id =
#endif
g_signal_connect( globals->adjustment, "value_changed",
G_CALLBACK(scroll_value_changed), globals );
XP_ASSERT( id > 0 );
gtk_widget_show( vscrollbar );
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( "" );
gtk_box_pack_start( GTK_BOX(vbox), label, TRUE, TRUE, 0);
gtk_widget_show( label );
id = g_signal_connect( drawing_area, "configure-event",
G_CALLBACK(configure_event), globals );
#ifdef DEBUG
id =
#endif
g_signal_connect( drawing_area, "configure-event",
G_CALLBACK(configure_event), globals );
XP_ASSERT( id > 0 );
id = g_signal_connect( drawing_area, "button_press_event",
G_CALLBACK(button_press_event), globals );
#ifdef DEBUG
id =
#endif
g_signal_connect( drawing_area, "button_press_event",
G_CALLBACK(button_press_event), globals );
XP_ASSERT( id > 0 );
id = g_signal_connect( drawing_area, "motion_notify_event",
G_CALLBACK(motion_notify_event), globals );
#ifdef DEBUG
id =
#endif
g_signal_connect( drawing_area, "motion_notify_event",
G_CALLBACK(motion_notify_event), globals );
XP_ASSERT( id > 0 );
id = g_signal_connect( drawing_area, "button_release_event",
G_CALLBACK(button_release_event), globals );
#ifdef DEBUG
id =
#endif
g_signal_connect( drawing_area, "button_release_event",
G_CALLBACK(button_release_event), globals );
XP_ASSERT( id > 0 );
setOneSecondTimer( cGlobals );
@ -2889,7 +2711,7 @@ loadGameNoDraw( GtkGameGlobals* globals, LaunchParams* params,
setTransportProcs( &procs, globals );
CommonGlobals* cGlobals = &globals->cGlobals;
cGlobals->selRow = rowid;
cGlobals->rowid = rowid;
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
params->vtMgr );
XP_Bool loaded = loadGame( stream, pDb, rowid );
@ -2924,7 +2746,7 @@ makeNewGame( GtkGameGlobals* globals )
if ( !!cGlobals->game.comms ) {
comms_getAddr( cGlobals->game.comms, &cGlobals->addr );
} else {
LaunchParams* params = globals->cGlobals.params;
LaunchParams* params = cGlobals->params;
const XP_UCHAR* relayName = params->connInfo.relay.relayName;
if ( !relayName ) {
relayName = RELAY_NAME_DEFAULT;
@ -2937,8 +2759,8 @@ makeNewGame( GtkGameGlobals* globals )
}
CurGameInfo* gi = cGlobals->gi;
XP_Bool success = newGameDialog( globals, gi, &cGlobals->addr,
XP_TRUE, XP_FALSE );
XP_Bool success = gtkNewGameDialog( globals, gi, &cGlobals->addr,
XP_TRUE, XP_FALSE );
if ( success && !!gi->dictName && !cGlobals->dict ) {
cGlobals->dict =
linux_dictionary_make( MEMPOOL cGlobals->params,

View file

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

View file

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

View file

@ -36,18 +36,19 @@
static void onNewData( GtkAppGlobals* apg, sqlite3_int64 rowid,
XP_Bool isNew );
static void updateButtons( GtkAppGlobals* apg );
static void open_row( GtkAppGlobals* apg, sqlite3_int64 row, XP_Bool isNew );
static void
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;
}
static void
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
@ -55,9 +56,9 @@ gameIsOpen( GtkAppGlobals* apg, sqlite3_int64 rowid )
{
XP_Bool found = XP_FALSE;
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;
found = globals->cGlobals.selRow == rowid;
found = globals->cGlobals.rowid == rowid;
}
return found;
}
@ -67,10 +68,10 @@ findOpenGame( const GtkAppGlobals* apg, sqlite3_int64 rowid )
{
GtkGameGlobals* result = NULL;
GSList* iter;
for ( iter = apg->globalsList; !!iter; iter = iter->next ) {
for ( iter = apg->cag.globalsList; !!iter; iter = iter->next ) {
GtkGameGlobals* globals = (GtkGameGlobals*)iter->data;
CommonGlobals* cGlobals = &globals->cGlobals;
if ( cGlobals->selRow == rowid ) {
if ( cGlobals->rowid == rowid ) {
result = globals;
break;
}
@ -78,7 +79,7 @@ findOpenGame( const GtkAppGlobals* apg, sqlite3_int64 rowid )
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,
MISSING_ITEM, LASTTURN_ITEM, N_ITEMS };
@ -169,6 +170,7 @@ init_games_list( GtkAppGlobals* apg )
addTextColumn( list, "Room", ROOM_ITEM );
addTextColumn( list, "GameID", GAMEID_ITEM );
addTextColumn( list, "Seed", SEED_ITEM );
addTextColumn( list, "Role", ROLE_ITEM );
addTextColumn( list, "Conn. via", CONN_ITEM );
addTextColumn( list, "RelayID", RELAYID_ITEM );
addTextColumn( list, "Ended", OVER_ITEM );
@ -186,6 +188,7 @@ init_games_list( GtkAppGlobals* apg )
G_TYPE_STRING, /* ROOM_ITEM */
G_TYPE_INT, /* GAMEID_ITEM */
G_TYPE_INT, /* SEED_ITEM */
G_TYPE_INT, /* ROLE_ITEM */
G_TYPE_STRING, /* CONN_ITEM */
G_TYPE_STRING, /*RELAYID_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,
GAMEID_ITEM, gib->gameID,
SEED_ITEM, gib->seed,
ROLE_ITEM, gib->role,
CONN_ITEM, gib->conn,
RELAYID_ITEM, gib->relayID,
TURN_ITEM, gib->turn,
@ -275,20 +279,20 @@ handle_newgame_button( GtkWidget* XP_UNUSED(widget), void* closure )
{
GtkAppGlobals* apg = (GtkAppGlobals*)closure;
XP_LOGF( "%s called", __func__ );
GtkGameGlobals* globals = malloc( sizeof(*globals) );
apg->params->needsNewGame = XP_FALSE;
initGlobals( globals, apg->params, NULL );
GtkGameGlobals* globals = calloc( 1, sizeof(*globals) );
apg->cag.params->needsNewGame = XP_FALSE;
initBoardGlobalsGtk( globals, apg->cag.params, NULL );
if ( !makeNewGame( globals ) ) {
freeGlobals( globals );
} else {
GtkWidget* gameWindow = globals->window;
globals->cGlobals.selRow = -1;
globals->cGlobals.rowid = -1;
recordOpened( apg, globals );
gtk_widget_show( gameWindow );
}
}
void
static void
open_row( GtkAppGlobals* apg, sqlite3_int64 row, XP_Bool isNew )
{
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 );
}
apg->params->needsNewGame = XP_FALSE;
apg->cag.params->needsNewGame = XP_FALSE;
GtkGameGlobals* globals = malloc( sizeof(*globals) );
initGlobals( globals, apg->params, NULL );
globals->cGlobals.selRow = row;
initBoardGlobalsGtk( globals, apg->cag.params, NULL );
globals->cGlobals.rowid = row;
recordOpened( apg, globals );
gtk_widget_show( globals->window );
}
@ -320,7 +324,7 @@ handle_open_button( GtkWidget* XP_UNUSED(widget), void* closure )
void
make_rematch( GtkAppGlobals* apg, const CommonGlobals* cGlobals )
{
LaunchParams* params = apg->params;
LaunchParams* params = apg->cag.params;
XP_ASSERT( params == cGlobals->params );
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
params->vtMgr );
@ -383,7 +387,7 @@ handle_rematch_button( GtkWidget* XP_UNUSED(widget), void* closure )
for ( int ii = 0; ii < selRows->len; ++ii ) {
sqlite3_int64 rowid = g_array_index( selRows, sqlite3_int64, ii );
GtkGameGlobals tmpGlobals;
if ( loadGameNoDraw( &tmpGlobals, apg->params, rowid ) ) {
if ( loadGameNoDraw( &tmpGlobals, apg->cag.params, rowid ) ) {
make_rematch( apg, &tmpGlobals.cGlobals );
}
freeGlobals( &tmpGlobals );
@ -394,7 +398,7 @@ static void
handle_delete_button( GtkWidget* XP_UNUSED(widget), void* closure )
{
GtkAppGlobals* apg = (GtkAppGlobals*)closure;
LaunchParams* params = apg->params;
LaunchParams* params = apg->cag.params;
guint len = apg->selRows->len;
for ( guint ii = 0; ii < len; ++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();
GtkAppGlobals* apg = (GtkAppGlobals*)data;
GSList* iter;
for ( iter = apg->globalsList; !!iter; iter = iter->next ) {
for ( iter = apg->cag.globalsList; !!iter; iter = iter->next ) {
GtkGameGlobals* globals = (GtkGameGlobals*)iter->data;
destroy_board_window( NULL, globals );
// freeGlobals( globals );
}
g_slist_free( apg->globalsList );
g_slist_free( apg->cag.globalsList );
gchar buf[64];
sprintf( buf, "%d:%d:%d:%d", apg->lastConfigure.x,
apg->lastConfigure.y, apg->lastConfigure.width,
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();
}
@ -477,7 +481,7 @@ static void
setWindowTitle( GtkAppGlobals* apg )
{
GtkWidget* window = apg->window;
LaunchParams* params = apg->params;
LaunchParams* params = apg->cag.params;
gchar title[128] = {0};
if ( !!params->dbName ) {
@ -505,7 +509,7 @@ trySetWinConfig( GtkAppGlobals* apg )
int height = 400;
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 );
}
@ -516,14 +520,14 @@ trySetWinConfig( GtkAppGlobals* apg )
static void
handle_movescheck( GtkWidget* XP_UNUSED(widget), GtkAppGlobals* apg )
{
LaunchParams* params = apg->params;
LaunchParams* params = apg->cag.params;
relaycon_checkMsgs( params );
}
static void
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 );
gchar str[32];
snprintf( &str[0], VSIZE(str), "%d", relayID );
@ -535,7 +539,7 @@ static void
makeGamesWindow( GtkAppGlobals* apg )
{
GtkWidget* window;
LaunchParams* params = apg->params;
LaunchParams* params = apg->cag.params;
apg->window = window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
g_signal_connect( G_OBJECT(window), "destroy",
@ -602,7 +606,7 @@ static GtkWidget*
openDBFile( GtkAppGlobals* apg )
{
GtkGameGlobals* globals = malloc( sizeof(*globals) );
initGlobals( globals, apg->params, NULL );
initBoardGlobalsGtk( globals, apg->cag.params, NULL );
GtkWidget* window = globals->window;
gtk_widget_show( window );
@ -633,7 +637,7 @@ static void
onNewData( GtkAppGlobals* apg, sqlite3_int64 rowid, XP_Bool isNew )
{
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 );
g_object_unref( gib.snap );
}
@ -651,31 +655,22 @@ feedBufferGTK( GtkAppGlobals* apg, sqlite3_int64 rowid,
seed = comms_getChannelSeed( globals->cGlobals.game.comms );
} else {
GtkGameGlobals tmpGlobals;
if ( loadGameNoDraw( &tmpGlobals, apg->params, rowid ) ) {
if ( loadGameNoDraw( &tmpGlobals, apg->cag.params, rowid ) ) {
gameGotBuf( &tmpGlobals.cGlobals, XP_FALSE, buf, len, from );
seed = comms_getChannelSeed( tmpGlobals.cGlobals.game.comms );
saveGame( &tmpGlobals.cGlobals );
linuxSaveGame( &tmpGlobals.cGlobals );
}
freeGlobals( &tmpGlobals );
}
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 */
static void
gameFromInvite( GtkAppGlobals* apg, const NetLaunchInfo* invite,
const CommsAddrRec* returnAddr )
{
LaunchParams* params = apg->params;
LaunchParams* params = apg->cag.params;
CurGameInfo gi = {0};
gi_copy( MPPARM(params->mpool) &gi, &params->pgi );
@ -688,7 +683,7 @@ gameFromInvite( GtkAppGlobals* apg, const NetLaunchInfo* invite,
GtkGameGlobals* globals = malloc( sizeof(*globals) );
params->needsNewGame = XP_FALSE;
initGlobals( globals, params, &gi );
initBoardGlobalsGtk( globals, params, &gi );
if ( !!returnAddr ) {
globals->cGlobals.addr = *returnAddr;
@ -697,7 +692,7 @@ gameFromInvite( GtkAppGlobals* apg, const NetLaunchInfo* invite,
}
GtkWidget* gameWindow = globals->window;
globals->cGlobals.selRow = -1;
globals->cGlobals.rowid = -1;
recordOpened( apg, globals );
gtk_widget_show( gameWindow );
@ -712,7 +707,7 @@ relayInviteReceived( void* closure, NetLaunchInfo* invite )
XP_U32 gameID = invite->gameID;
sqlite3_int64 rowids[1];
int nRowIDs = VSIZE(rowids);
getRowsForGameID( apg->params->pDb, gameID, rowids, &nRowIDs );
getRowsForGameID( apg->cag.params->pDb, gameID, rowids, &nRowIDs );
if ( 0 < nRowIDs ) {
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 );
GtkAppGlobals* apg = (GtkAppGlobals*)closure;
// LaunchParams* params = apg->params;
// LaunchParams* params = apg->cag.params;
(void)feedBufferGTK( apg, rowid, buf, len, from );
LOG_RETURN_VOID();
}
@ -758,9 +753,9 @@ requestMsgs( gpointer data )
{
GtkAppGlobals* apg = (GtkAppGlobals*)data;
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] ) {
relaycon_requestMsgs( apg->params, devIDBuf );
relaycon_requestMsgs( apg->cag.params, devIDBuf );
} else {
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();
GtkAppGlobals* apg = (GtkAppGlobals*)closure;
LaunchParams* params = apg->params;
LaunchParams* params = apg->cag.params;
sqlite3_int64 rowids[4];
int nRowIDs = VSIZE(rowids);
@ -817,7 +812,7 @@ static void
gtkDevIDReceived( void* closure, const XP_UCHAR* devID, XP_U16 maxInterval )
{
GtkAppGlobals* apg = (GtkAppGlobals*)closure;
LaunchParams* params = apg->params;
LaunchParams* params = apg->cag.params;
if ( !!devID ) {
XP_LOGF( "%s(devID=%s)", __func__, devID );
db_store( params->pDb, KEY_RDEVID, devID );
@ -842,8 +837,7 @@ gtkErrorMsgRcvd( void* closure, const XP_UCHAR* msg )
}
void
onGameSaved( void* closure, sqlite3_int64 rowid,
XP_Bool firstTime )
gtkOnGameSaved( void* closure, sqlite3_int64 rowid, XP_Bool firstTime )
{
GtkGameGlobals* globals = (GtkGameGlobals*)closure;
GtkAppGlobals* apg = globals->apg;
@ -874,10 +868,10 @@ gtkmain( LaunchParams* params )
sigaction( SIGTERM, &act, NULL );
apg.selRows = g_array_new( FALSE, FALSE, sizeof( sqlite3_int64 ) );
apg.params = params;
apg.cag.params = params;
XP_ASSERT( !!params->dbName || params->dbFileName );
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
changed, we care. */
@ -890,7 +884,6 @@ gtkmain( LaunchParams* params )
.msgNoticeReceived = gtkNoticeRcvd,
.devIDReceived = gtkDevIDReceived,
.msgErrorMsg = gtkErrorMsgRcvd,
.socketAdded = gtkSocketAdded,
.inviteReceived = relayInviteReceived,
};
@ -919,7 +912,6 @@ gtkmain( LaunchParams* params )
}
if ( !!myPhone && 0 < myPort ) {
SMSProcs smsProcs = {
.socketAdded = gtkSocketAdded,
.inviteReceived = smsInviteReceived,
.msgReceived = smsMsgReceivedGTK,
};
@ -943,8 +935,8 @@ gtkmain( LaunchParams* params )
gtk_main();
device_store( params->dutil );
closeGamesDB( params->pDb );
params->pDb = NULL;
/* closeGamesDB( params->pDb ); */
/* params->pDb = NULL; */
relaycon_cleanup( params );
#ifdef XWFEATURE_SMS
linux_sms_cleanup( params );

View file

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

View file

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

View file

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

View file

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

View file

@ -44,6 +44,7 @@
#include "comms.h"
#include "strutils.h"
#include "uuidhack.h"
#include "gsrcwrap.h"
#define MAX_CLIENTS 1
@ -186,7 +187,7 @@ lbt_connectSocket( LinBtStuff* btStuff, const CommsAddrRec* addrP )
// connect to server
&& (0 == connect( sock, (struct sockaddr *)&saddr, sizeof(saddr) )) ) {
CommonGlobals* globals = btStuff->globals;
(*globals->socketAdded)( globals->socketAddedClosure, sock, bt_socket_proc );
ADD_SOCKET( globals->socketAddedClosure, sock, bt_socket_proc );
btStuff->socket = sock;
} else {
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;
if ( success ) {
(*globals->socketAdded)( globals->socketAddedClosure, sock, bt_socket_proc );
ADD_SOCKET( globals->socketAddedClosure, sock, bt_socket_proc );
XP_ASSERT( btStuff->socket == -1 );
btStuff->socket = sock;
} else {

View file

@ -72,6 +72,7 @@
#include "strutils.h"
#include "dbgutil.h"
#include "dictiter.h"
#include "gsrcwrap.h"
/* #include "commgr.h" */
/* #include "compipe.h" */
#include "memstream.h"
@ -80,6 +81,12 @@
#define DEFAULT_PORT 10997
#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 );
XP_Bool
@ -120,6 +127,191 @@ streamFromFile( CommonGlobals* cGlobals, char* name )
return stream;
} /* 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
XWStreamCtxt*
streamFromDB( CommonGlobals* cGlobals )
@ -189,7 +381,7 @@ gameGotBuf( CommonGlobals* cGlobals, XP_Bool hasDraw, const XP_U8* buf,
if ( !!stream ) {
redraw = game_receiveMessage( game, stream, from );
if ( redraw ) {
saveGame( cGlobals );
linuxSaveGame( cGlobals );
}
stream_destroy( stream );
@ -328,7 +520,7 @@ strFromStream( XWStreamCtxt* stream )
} /* strFromStream */
void
saveGame( CommonGlobals* cGlobals )
linuxSaveGame( CommonGlobals* cGlobals )
{
LOG_FUNC();
sqlite3* pDb = cGlobals->params->pDb;
@ -336,7 +528,7 @@ saveGame( CommonGlobals* cGlobals )
(!!cGlobals->params->fileName || !!pDb) ) {
XP_Bool doSave = XP_TRUE;
XP_Bool newGame = !file_exists( cGlobals->params->fileName )
|| -1 == cGlobals->selRow;
|| -1 == cGlobals->rowid;
/* don't fail to save first time! */
if ( 0 < cGlobals->params->saveFailPct && !newGame ) {
XP_U16 pct = XP_RANDOM() % 100;
@ -550,7 +742,14 @@ secondTimerFired( gpointer data )
void
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
@ -603,6 +802,7 @@ typedef enum {
,CMD_NOHEARTBEAT
,CMD_HOSTNAME
,CMD_CLOSESTDIN
,CMD_NOCLOSESTDIN
,CMD_QUITAFTER
,CMD_BOARDSIZE
,CMD_HIDEVALUES
@ -655,6 +855,7 @@ typedef enum {
#if defined PLATFORM_GTK && defined PLATFORM_NCURSES
,CMD_GTK
,CMD_CURSES
,CMD_CURSES_LIST_HT
#endif
#if defined PLATFORM_GTK
,CMD_ASKNEWGAME
@ -725,6 +926,7 @@ static CmdInfoRec CmdInfoRecs[] = {
,{ CMD_NOHEARTBEAT, false, "no-heartbeat", "don't send heartbeats" }
,{ CMD_HOSTNAME, true, "host", "name of remote host" }
,{ 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_BOARDSIZE, true, "board-size", "board is <n> by <n> cells" }
,{ 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
,{ CMD_GTK, false, "gtk", "use GTK 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
#if defined PLATFORM_GTK
,{ 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];
int sock = g_io_channel_unix_get_fd( source );
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;
}
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;
if ( cGlobals->params->useUdp ) {
XP_ASSERT( -1 != cGlobals->selRow );
XP_ASSERT( -1 != cGlobals->rowid );
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,
clientToken, addrRec );
} else {
@ -1176,7 +1380,7 @@ linux_relay_send( CommonGlobals* cGlobals, const XP_U8* buf, XP_U16 buflen,
if ( sock == -1 ) {
XP_LOGF( "%s: socket uninitialized", __func__ );
sock = linux_init_relay_socket( cGlobals, addrRec );
(*cGlobals->socketAdded)( cGlobals, sock, linux_relay_ioproc );
ADD_SOCKET( cGlobals, sock, linux_relay_ioproc );
}
if ( sock != -1 ) {
@ -1262,7 +1466,7 @@ linux_send( const XP_U8* buf, XP_U16 buflen, const XP_UCHAR* XP_UNUSED_DBG(msgNo
#endif
#if defined XWFEATURE_BLUETOOTH
case COMMS_CONN_BT: {
XP_Bool isServer = comms_getIsServer( cGlobals->game.comms );
XP_Bool isServer = game_getIsServer( &cGlobals->game );
linux_bt_open( cGlobals, isServer );
nSent = linux_bt_send( buf, buflen, addrRec, cGlobals );
}
@ -1506,7 +1710,7 @@ linux_util_addrChange( XW_UtilCtxt* uc,
switch ( typ ) {
#ifdef XWFEATURE_BLUETOOTH
case COMMS_CONN_BT: {
XP_Bool isServer = comms_getIsServer( cGlobals->game.comms );
XP_Bool isServer = game_getIsServer( &cGlobals->game );
linux_bt_open( cGlobals, isServer );
}
break;
@ -1530,18 +1734,10 @@ linux_util_addrChange( XW_UtilCtxt* uc,
}
}
void
linuxSetIsServer( CommonGlobals* cGlobals, XP_Bool isServer )
{
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 )
static gint
changeRolesIdle( gpointer data )
{
CommonGlobals* cGlobals = (CommonGlobals*)data;
ServerCtxt* server = cGlobals->game.server;
server_reset( server, cGlobals->game.comms );
if ( SERVER_ISCLIENT == cGlobals->gi->serverRole ) {
@ -1551,7 +1747,23 @@ linuxChangeRoles( CommonGlobals* cGlobals )
(void)server_initClientConnection( server, stream );
}
(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
unsigned int
@ -1906,59 +2118,22 @@ setupLinuxUtilCallbacks( XW_UtilCtxt* util )
#ifndef XWFEATURE_STANDALONE_ONLY
util->vtable->m_util_informMissing = linux_util_informMissing;
util->vtable->m_util_addrChange = linux_util_addrChange;
util->vtable->m_util_setIsServer = linux_util_setIsServer;
#endif
}
/* Set up cGlobals->gi and cGlobals->addr based on params fields */
void
initFromParams( CommonGlobals* cGlobals, LaunchParams* params )
assertAllCallbacksSet( XW_UtilCtxt* util )
{
LOG_FUNC();
/* CurGameInfo */
cGlobals->gi = &params->pgi;
/* addr */
CommsAddrRec* addr = &cGlobals->addr;
XP_MEMSET( addr, 0, sizeof(*addr) );
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;
XWStreamCtxt* (**proc)(XW_UtilCtxt*, XP_PlayerAddr ) =
&util->vtable->m_util_makeStreamFromAddr;
for ( int ii = 0; ii < sizeof(*util->vtable)/sizeof(*proc); ++ii ) {
if ( !*proc ) {
XP_LOGF( "%s(): null ptr at index %d", __func__, ii );
XP_ASSERT( 0 );
}
++proc;
}
}
@ -1972,6 +2147,12 @@ setupUtil( CommonGlobals* cGlobals )
setupLinuxUtilCallbacks( util );
}
void
disposeUtil( CommonGlobals* cGlobals )
{
linux_util_vt_destroy( cGlobals->util );
}
static void
initParams( LaunchParams* params )
{
@ -2000,6 +2181,9 @@ initParams( LaunchParams* params )
static void
freeParams( LaunchParams* params )
{
closeGamesDB( params->pDb );
params->pDb = NULL;
vtmgr_destroy( MPPARM(params->mpool) params->vtMgr );
dutils_free( &params->dutil );
dmgr_destroy( params->dictMgr );
@ -2116,6 +2300,7 @@ main( int argc, char** argv )
mainParams.useMmap = XP_TRUE;
mainParams.useUdp = true;
mainParams.dbName = "xwgames.sqldb";
mainParams.cursesListWinHt = 5;
char* envDictPath = getenv( "XW_DICTDIR" );
XP_LOGF( "%s: envDictPath=%s", __func__, envDictPath );
@ -2180,6 +2365,7 @@ main( int argc, char** argv )
if ( !path ) {
path = ".";
}
XP_LOGF( "%s(): appending dict path: %s", __func__, path );
mainParams.dictDirs = g_slist_append( mainParams.dictDirs, path );
break;
#ifdef XWFEATURE_WALKDICT
@ -2376,6 +2562,9 @@ main( int argc, char** argv )
case CMD_CLOSESTDIN:
mainParams.closeStdin = XP_TRUE;
break;
case CMD_NOCLOSESTDIN:
mainParams.closeStdin = XP_FALSE;
break;
case CMD_QUITAFTER:
mainParams.quitAfter = atoi(optarg);
break;
@ -2481,6 +2670,9 @@ main( int argc, char** argv )
case CMD_CURSES:
mainParams.useCurses = XP_TRUE;
break;
case CMD_CURSES_LIST_HT:
mainParams.cursesListWinHt = atoi(optarg);
break;
#endif
#if defined PLATFORM_GTK
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 ) {
XP_LOGF( "%s(): there", __func__ );
/* char path[256]; */
/* getDictPath( &mainParams, mainParams.gi.dictName, path, VSIZE(path) ); */
/* mainParams.dict = */
/* linux_dictionary_make( MPPARM(mainParams.mpool) &mainParams, */
/* mainParams.pgi.dictName, */
/* mainParams.useMmap ); */
/* XP_ASSERT( !!mainParams.dict ); */
/* mainParams.pgi.dictLang = dict_getLangCode( mainParams.dict ); */
DictionaryCtxt* dict =
linux_dictionary_make( MPPARM(mainParams.mpool) &mainParams,
mainParams.pgi.dictName,
mainParams.useMmap );
XP_ASSERT( !!dict );
mainParams.pgi.dictLang = dict_getLangCode( dict );
XP_LOGF( "%s(): set lang code: %d", __func__, mainParams.pgi.dictLang );
dict_unref( dict );
} else if ( isServer ) {
#ifdef STUBBED_DICT
foo
mainParams.dict =
make_stubbed_dict( MPPARM_NOCOMMA(mainParams.util->mpool) );
XP_WARNF( "no dictionary provided: using English stub dict\n" );
@ -2666,21 +2863,19 @@ main( int argc, char** argv )
mainParams.serverRole = SERVER_ISCLIENT;
}
/* if ( mainParams.needsNewGame ) { */
/* gi_disposePlayerInfo( MPPARM(mainParams.mpool) &mainParams.pgi ); */
/* gi_initPlayerInfo( MPPARM(mainParams.mpool) &mainParams.pgi, NULL ); */
/* } */
XP_ASSERT( !!mainParams.dbName );
mainParams.pDb = openGamesDB( mainParams.dbName );
if ( mainParams.useCurses ) {
if ( mainParams.needsNewGame ) {
/* curses doesn't have newgame dialog */
usage( argv[0], "game params required for curses version, e.g. --name Eric --room MyRoom"
" --remote-player --dict-dir ../ --game-dict CollegeEng_2to8.xwd");
} else {
/* if ( mainParams.needsNewGame ) { */
/* /\* curses doesn't have newgame dialog *\/ */
/* usage( argv[0], "game params required for curses version, e.g. --name Eric --room MyRoom" */
/* " --remote-player --dict-dir ../ --game-dict CollegeEng_2to8.xwd"); */
/* } else { */
#if defined PLATFORM_NCURSES
cursesmain( isServer, &mainParams );
cursesmain( isServer, &mainParams );
#endif
}
/* } */
} else {
#if defined PLATFORM_GTK
gtk_init( &argc, &argv );
@ -2694,6 +2889,8 @@ main( int argc, char** argv )
free( longopts );
g_slist_free( mainParams.dictDirs );
gsw_logIdles();
XP_LOGF( "%s exiting main, returning %d", argv[0], result );
return result;
} /* main */

View file

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

View file

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

View file

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

View file

@ -28,6 +28,7 @@
#include "linuxmain.h"
#include "comtypes.h"
#include "gamesdb.h"
#include "gsrcwrap.h"
#define MAX_MOVE_CHECK_MS ((XP_U16)(1000 * 60 * 60 * 24))
#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 );
} else {
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) );
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__,
errno, strerror(errno) );
}
LOG_RETURNF( "%d", TRUE );
return TRUE;
}
@ -719,21 +721,24 @@ relaycon_receive( GIOChannel* source, GIOCondition XP_UNUSED_DBG(condition), gpo
socklen_t fromlen = sizeof(from);
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 */
(struct sockaddr*)&from, &fromlen );
gchar* b64 = g_base64_encode( (const guchar*)buf,
((0 <= nRead)? nRead : 0) );
XP_LOGF( "%s: read %zd bytes ('%s')", __func__, nRead, b64 );
#ifdef COMMS_CHECKSUM
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 );
g_free( sum );
#else
XP_LOGF( "%s: read %zd bytes ('%s')", __func__, nRead, b64 );
#endif
g_free( b64 );
return process( storage, buf, nRead );
gboolean result = process( storage, buf, nRead );
// LOG_RETURNF( "%d", result );
return result;
}
void

View file

@ -34,7 +34,6 @@ typedef struct _Procs {
XP_U16 maxInterval );
void (*msgErrorMsg)( void* closure, const XP_UCHAR* msg );
void (*inviteReceived)( void* closure, NetLaunchInfo* invit );
SocketAddedFunc socketAdded;
} RelayConnProcs;
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')
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.indx = indx
self.args = args
@ -173,6 +173,7 @@ class Device():
self.room = room
self.db = db
self.logPath = log
self.script = script
self.nInGame = nInGame
# runtime stuff; init now
self.app = args.APP_OLD
@ -198,6 +199,8 @@ class Device():
if pct >= random.randint(0, 99):
print('launch(): upgrading from ', self.app, ' to ', self.args.APP_NEW)
self.app = self.args.APP_NEW
# nuke script to force regeneration
os.unlink(self.script)
def logReaderMain(self):
assert self and self.proc
@ -234,19 +237,26 @@ class Device():
# 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):
args = []
if self.args.VALGRIND:
args += ['valgrind']
# args += ['--leak-check=full']
# args += ['--track-origins=yes']
# Upgrade if appropriate
self.setApp(self.args.UPGRADE_PCT)
args += [self.app] + [str(p) for p in self.params]
if self.devID: args.extend( ' '.split(self.devID))
self.checkScript()
self.launchCount += 1
args = [ self.script ]
self.proc = subprocess.Popen(args, stdout = subprocess.DEVNULL,
stderr = subprocess.PIPE, universal_newlines = True)
self.pid = self.proc.pid
@ -287,8 +297,8 @@ class Device():
def moveFiles(self):
assert not self.running()
shutil.move(self.logPath, self.args.LOGDIR + '/done')
shutil.move(self.db, self.args.LOGDIR + '/done')
for fil in [ self.logPath, self.db, self.script ]:
shutil.move(fil, self.args.LOGDIR + '/done')
def send_dead(self):
if self.args.ADD_RELAY:
@ -393,6 +403,7 @@ def build_cmds(args):
DEV += 1
DB = '{}/{:02d}_{:02d}_DB.sql3'.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 += PLAT_PARMS
@ -442,7 +453,7 @@ def build_cmds(args):
# 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)
dev.update_ldevid()
devs.append(dev)
@ -515,7 +526,7 @@ def summarizeTileCounts(devs, endTime, state):
state['lastChange'] = now
state['tilesStr'] = tilesStr
return now - state['lastChange'] < datetime.timedelta(minutes = 1)
return now - state['lastChange'] < datetime.timedelta(seconds = 30)
def countCores():
return len(glob.glob1('/tmp',"core*"))
@ -549,18 +560,7 @@ def run_cmds(args, devs):
if dev.handleAllDone():
devs.remove(dev)
else:
# if [ -n "$ONE_PER_ROOM" -a 0 -ne ${ROOM_PIDS[$ROOM]} ]; then
# continue
# fi
# try_upgrade $KEY
# try_upgrade_upd $KEY
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():
dev.kill()
if dev.handleAllDone():

View file

@ -3,7 +3,26 @@
# This script just runs the curses app with a set of params known to
# 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)
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 \