2020-01-24 18:05:16 +01:00
|
|
|
/* -*- 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <ncurses.h>
|
|
|
|
#include <wchar.h>
|
|
|
|
|
|
|
|
#include "cursesboard.h"
|
|
|
|
#include "linuxmain.h"
|
|
|
|
#include "linuxutl.h"
|
|
|
|
#include "relaycon.h"
|
|
|
|
#include "cursesask.h"
|
|
|
|
#include "cursesmenu.h"
|
|
|
|
#include "cursesletterask.h"
|
|
|
|
#include "linuxdict.h"
|
|
|
|
#include "gamesdb.h"
|
|
|
|
#include "game.h"
|
|
|
|
#include "gsrcwrap.h"
|
|
|
|
|
|
|
|
typedef struct CursesBoardState {
|
|
|
|
CursesAppGlobals* aGlobals;
|
|
|
|
LaunchParams* params;
|
|
|
|
CursesMenuState* menuState;
|
|
|
|
OnGameSaved onGameSaved;
|
|
|
|
|
|
|
|
GSList* games;
|
|
|
|
} CursesBoardState;
|
|
|
|
|
|
|
|
struct CursesBoardGlobals {
|
|
|
|
CommonGlobals cGlobals;
|
|
|
|
CursesBoardState* cbState;
|
|
|
|
CursesMenuState* menuState; /* null if we're not using menus */
|
|
|
|
int refCount;
|
|
|
|
|
|
|
|
TransportProcs procs;
|
|
|
|
|
|
|
|
XP_Bool doDraw;
|
|
|
|
|
|
|
|
union {
|
|
|
|
struct {
|
|
|
|
XWStreamCtxt* stream; /* how we can reach the server */
|
|
|
|
} client;
|
|
|
|
struct {
|
|
|
|
int serverSocket;
|
|
|
|
XP_Bool socketOpen;
|
|
|
|
} server;
|
|
|
|
} csInfo;
|
|
|
|
XWGameState state;
|
|
|
|
XP_U16 nChatsSent;
|
|
|
|
XP_U16 nextQueryTimeSecs;
|
|
|
|
|
|
|
|
WINDOW* boardWin;
|
|
|
|
int winWidth, winHeight;
|
|
|
|
|
|
|
|
const MenuList* lastSubMenu;
|
|
|
|
|
|
|
|
XP_Bool amServer; /* this process acting as server */
|
|
|
|
};
|
|
|
|
|
|
|
|
static CursesBoardGlobals* findOrOpen( CursesBoardState* cbState,
|
|
|
|
sqlite3_int64 rowid );
|
|
|
|
static void enableDraw( CursesBoardGlobals* bGlobals,
|
|
|
|
int width, int top, int height );
|
|
|
|
static CursesBoardGlobals* ref( CursesBoardGlobals* bGlobals );
|
|
|
|
static void unref( CursesBoardGlobals* bGlobals );
|
|
|
|
static void setupBoard( CursesBoardGlobals* bGlobals );
|
|
|
|
|
|
|
|
CursesBoardState*
|
|
|
|
cb_init( CursesAppGlobals* aGlobals, LaunchParams* params,
|
|
|
|
CursesMenuState* menuState, OnGameSaved onGameSaved )
|
|
|
|
{
|
|
|
|
CursesBoardState* result = g_malloc0( sizeof(*result) );
|
|
|
|
result->aGlobals = aGlobals;
|
|
|
|
result->params = params;
|
|
|
|
result->menuState = menuState;
|
|
|
|
result->onGameSaved = onGameSaved;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cb_open( CursesBoardState* cbState, sqlite3_int64 rowid,
|
|
|
|
int width, int top, int height )
|
|
|
|
{
|
|
|
|
LOG_FUNC();
|
|
|
|
CursesBoardGlobals* bGlobals = findOrOpen( cbState, rowid );
|
|
|
|
enableDraw( bGlobals, width, top, height );
|
|
|
|
setupBoard( bGlobals );
|
|
|
|
|
|
|
|
CommonGlobals* cGlobals = &bGlobals->cGlobals;
|
|
|
|
if ( !!cGlobals->game.comms ) {
|
|
|
|
comms_resendAll( cGlobals->game.comms, COMMS_CONN_NONE, XP_FALSE );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cb_new( CursesBoardState* cbState, int width, int top, int height )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = findOrOpen( cbState, -1 );
|
|
|
|
enableDraw( bGlobals, width, top, height );
|
|
|
|
setupBoard( bGlobals );
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef KEYBOARD_NAV
|
|
|
|
static bool handleLeft( void* closure, int key );
|
|
|
|
static bool handleRight( void* closure, int key );
|
|
|
|
static bool handleUp( void* closure, int key );
|
|
|
|
static bool handleDown( void* closure, int key );
|
|
|
|
static bool handleClose( void* closure, int key );
|
|
|
|
#endif
|
|
|
|
static bool handleAltLeft( void* closure, int key );
|
|
|
|
static bool handleAltRight( void* closure, int key );
|
|
|
|
static bool handleAltUp( void* closure, int key );
|
|
|
|
static bool handleAltDown( void* closure, int key );
|
|
|
|
static bool handleJuggle( void* closure, int key );
|
|
|
|
static bool handleHide( void* closure, int key );
|
|
|
|
/* static bool handleResend( void* closure, int key ); */
|
|
|
|
static bool handleSpace( void* closure, int key );
|
|
|
|
static bool handleRet( void* closure, int key );
|
|
|
|
static bool handleHint( void* closure, int key );
|
|
|
|
static bool handleCommit( void* closure, int key );
|
|
|
|
static bool handleFlip( void* closure, int key );
|
|
|
|
static bool handleToggleValues( void* closure, int key );
|
|
|
|
static bool handleBackspace( void* closure, int key );
|
|
|
|
static bool handleUndo( void* closure, int key );
|
|
|
|
static bool handleReplace( void* closure, int key );
|
|
|
|
#ifdef CURSES_SMALL_SCREEN
|
|
|
|
static bool handleRootKeyShow( void* closure, int key );
|
|
|
|
static bool handleRootKeyHide( void* closure, int key );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void relay_connd_curses( void* closure, XP_UCHAR* const room,
|
|
|
|
XP_Bool reconnect, XP_U16 devOrder,
|
|
|
|
XP_Bool allHere, XP_U16 nMissing );
|
|
|
|
static void relay_status_curses( void* closure, CommsRelayState state );
|
|
|
|
static void relay_error_curses( void* closure, XWREASON relayErr );
|
|
|
|
static XP_Bool relay_sendNoConn_curses( const XP_U8* msg, XP_U16 len,
|
|
|
|
const XP_UCHAR* msgNo,
|
|
|
|
const XP_UCHAR* relayID, void* closure );
|
|
|
|
static void curses_countChanged( void* closure, XP_U16 newCount );
|
|
|
|
static XP_U32 curses_getFlags( void* closure );
|
|
|
|
#ifdef RELAY_VIA_HTTP
|
|
|
|
static void relay_requestJoin_curses( void* closure, const XP_UCHAR* devID,
|
|
|
|
const XP_UCHAR* room, XP_U16 nPlayersHere,
|
|
|
|
XP_U16 nPlayersTotal, XP_U16 seed, XP_U16 lang );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
const MenuList g_allMenuList[] = {
|
|
|
|
{ handleLeft, "Left", "H", 'H' },
|
|
|
|
{ handleRight, "Right", "L", 'L' },
|
|
|
|
{ handleUp, "Up", "J", 'J' },
|
|
|
|
{ handleDown, "Down", "K", 'K' },
|
|
|
|
{ handleClose, "Close", "W", 'W' },
|
|
|
|
{ handleSpace, "Raise focus", "<spc>", ' ' },
|
|
|
|
{ handleRet, "Click/tap", "<ret>", '\r' },
|
|
|
|
};
|
|
|
|
|
|
|
|
const MenuList g_boardMenuList[] = {
|
|
|
|
{ handleAltLeft, "Force left", "{", '{' },
|
|
|
|
{ handleAltRight, "Force right", "}", '}' },
|
|
|
|
{ handleAltUp, "Force up", "_", '_' },
|
|
|
|
{ handleAltDown, "Force down", "+", '+' },
|
|
|
|
{ handleHint, "Hint", "?", '?' },
|
|
|
|
|
|
|
|
{ handleCommit, "Commit move", "C", 'C' },
|
|
|
|
{ handleFlip, "Flip", "F", 'F' },
|
|
|
|
{ handleToggleValues, "Show values", "V", 'V' },
|
|
|
|
|
|
|
|
{ handleBackspace, "Remove from board", "<del>", 8 },
|
|
|
|
{ handleUndo, "Undo prev", "U", 'U' },
|
|
|
|
{ handleReplace, "uNdo cur", "N", 'N' },
|
|
|
|
|
|
|
|
{ NULL, NULL, NULL, '\0'}
|
|
|
|
};
|
|
|
|
|
|
|
|
const MenuList g_trayMenuList[] = {
|
|
|
|
{ handleJuggle, "Juggle", "G", 'G' },
|
|
|
|
{ handleHide, "[un]hIde", "I", 'I' },
|
|
|
|
{ handleAltLeft, "Divider left", "{", '{' },
|
|
|
|
{ handleAltRight, "Divider right", "}", '}' },
|
|
|
|
|
|
|
|
{ NULL, NULL, NULL, '\0'}
|
|
|
|
};
|
|
|
|
|
|
|
|
const MenuList g_scoreMenuList[] = {
|
|
|
|
#ifdef KEYBOARD_NAV
|
|
|
|
#endif
|
|
|
|
{ NULL, NULL, NULL, '\0'}
|
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef KEYBOARD_NAV
|
|
|
|
static void
|
|
|
|
changeMenuForFocus( CursesBoardGlobals* bGlobals, BoardObjectType focussed )
|
|
|
|
{
|
|
|
|
const MenuList* subMenu = NULL;
|
|
|
|
if ( focussed == OBJ_TRAY ) {
|
|
|
|
subMenu = g_trayMenuList;
|
|
|
|
} else if ( focussed == OBJ_BOARD ) {
|
|
|
|
subMenu = g_boardMenuList;
|
|
|
|
} else if ( focussed == OBJ_SCORE ) {
|
|
|
|
subMenu = g_scoreMenuList;
|
|
|
|
}
|
|
|
|
|
|
|
|
CursesMenuState* menuState = bGlobals->menuState;
|
|
|
|
if ( !!menuState ) {
|
|
|
|
cmenu_removeMenus( menuState, bGlobals->lastSubMenu, NULL );
|
|
|
|
bGlobals->lastSubMenu = subMenu;
|
|
|
|
|
|
|
|
cmenu_addMenus( menuState, bGlobals, subMenu, NULL );
|
|
|
|
}
|
|
|
|
// drawMenuLargeOrSmall( globals->apg, menuList, globals );
|
|
|
|
} /* changeMenuForFocus */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void setupCursesUtilCallbacks( CursesBoardGlobals* bGlobals, XW_UtilCtxt* util );
|
|
|
|
|
|
|
|
static void initMenus( CursesBoardGlobals* bGlobals )
|
|
|
|
{
|
|
|
|
if ( !bGlobals->menuState ) {
|
|
|
|
bGlobals->menuState = bGlobals->cbState->menuState;
|
|
|
|
cmenu_push( bGlobals->menuState, bGlobals, g_allMenuList, NULL );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
onGameSaved( void* closure, sqlite3_int64 rowid, XP_Bool firstTime )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)closure;
|
|
|
|
CommonGlobals* cGlobals = &bGlobals->cGlobals;
|
|
|
|
/* onCursesGameSaved( bGlobals->aGlobals, rowid ); */
|
|
|
|
|
|
|
|
BoardCtxt* board = cGlobals->game.board;
|
|
|
|
board_invalAll( board );
|
|
|
|
board_draw( board );
|
|
|
|
/* May not be recorded */
|
|
|
|
XP_ASSERT( cGlobals->rowid == rowid );
|
|
|
|
// cGlobals->rowid = rowid;
|
|
|
|
|
|
|
|
CursesBoardState* cbState = bGlobals->cbState;
|
|
|
|
(*cbState->onGameSaved)( cbState->aGlobals, rowid, firstTime );
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
fire_acceptor( GIOChannel* source, GIOCondition condition, gpointer data )
|
|
|
|
{
|
|
|
|
if ( 0 != (G_IO_IN & condition) ) {
|
|
|
|
CursesBoardGlobals* globals = (CursesBoardGlobals*)data;
|
|
|
|
|
|
|
|
int fd = g_io_channel_unix_get_fd( source );
|
|
|
|
XP_ASSERT( fd == globals->csInfo.server.serverSocket );
|
|
|
|
(*globals->cGlobals.acceptor)( fd, globals );
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
curses_socket_acceptor( int listener, Acceptor func, CommonGlobals* cGlobals,
|
|
|
|
void** XP_UNUSED(storage) )
|
|
|
|
{
|
|
|
|
if ( -1 == listener ) {
|
|
|
|
XP_LOGF( "%s: removal of listener not implemented!!!!!", __func__ );
|
|
|
|
} else {
|
|
|
|
CursesBoardGlobals* globals = (CursesBoardGlobals*)cGlobals;
|
|
|
|
XP_ASSERT( !cGlobals->acceptor || (func == cGlobals->acceptor) );
|
|
|
|
cGlobals->acceptor = func;
|
|
|
|
globals->csInfo.server.serverSocket = listener;
|
|
|
|
ADD_SOCKET( globals, listener, fire_acceptor );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
copyParmsAddr( CommonGlobals* cGlobals )
|
|
|
|
{
|
|
|
|
LaunchParams* params = cGlobals->params;
|
|
|
|
CommsAddrRec* addr = &cGlobals->addr;
|
|
|
|
|
|
|
|
CommsConnType typ;
|
|
|
|
for ( XP_U32 st = 0; addr_iter( ¶ms->addr, &typ, &st ); ) {
|
|
|
|
addr_addType( addr, typ );
|
|
|
|
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_SMS
|
|
|
|
case 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:
|
|
|
|
XP_ASSERT( sizeof(addr->u.bt.btAddr)
|
|
|
|
>= sizeof(params->connInfo.bt.hostAddr));
|
|
|
|
XP_MEMCPY( &addr->u.bt.btAddr, ¶ms->connInfo.bt.hostAddr,
|
|
|
|
sizeof(params->connInfo.bt.hostAddr) );
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static CursesBoardGlobals*
|
|
|
|
commonInit( CursesBoardState* cbState, sqlite3_int64 rowid )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = g_malloc0( sizeof(*bGlobals) );
|
|
|
|
XP_LOGF( "%s(): alloc'd bGlobals %p", __func__, bGlobals );
|
|
|
|
CommonGlobals* cGlobals = &bGlobals->cGlobals;
|
|
|
|
LaunchParams* params = cbState->params;
|
|
|
|
|
|
|
|
cGlobals->gi = &cGlobals->_gi;
|
|
|
|
gi_copy( MPPARM(params->mpool) cGlobals->gi, ¶ms->pgi );
|
|
|
|
|
|
|
|
cGlobals->rowid = rowid;
|
|
|
|
bGlobals->cbState = cbState;
|
|
|
|
cGlobals->params = params;
|
|
|
|
setupUtil( cGlobals );
|
|
|
|
setupCursesUtilCallbacks( bGlobals, cGlobals->util );
|
|
|
|
|
|
|
|
cGlobals->socketAddedClosure = bGlobals;
|
|
|
|
cGlobals->onSave = onGameSaved;
|
|
|
|
cGlobals->onSaveClosure = bGlobals;
|
|
|
|
cGlobals->addAcceptor = curses_socket_acceptor;
|
|
|
|
|
|
|
|
bGlobals->procs.closure = cGlobals;
|
|
|
|
bGlobals->procs.send = LINUX_SEND;
|
|
|
|
#ifdef COMMS_HEARTBEAT
|
|
|
|
bGlobals->procs.reset = linux_reset;
|
|
|
|
#endif
|
|
|
|
bGlobals->procs.rstatus = relay_status_curses;
|
|
|
|
bGlobals->procs.rconnd = relay_connd_curses;
|
|
|
|
bGlobals->procs.rerror = relay_error_curses;
|
|
|
|
bGlobals->procs.sendNoConn = relay_sendNoConn_curses;
|
|
|
|
bGlobals->procs.countChanged = curses_countChanged;
|
|
|
|
bGlobals->procs.getFlags = curses_getFlags;
|
|
|
|
# ifdef RELAY_VIA_HTTP
|
|
|
|
bGlobals->procs.requestJoin = relay_requestJoin_curses;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
copyParmsAddr( cGlobals );
|
|
|
|
|
|
|
|
CurGameInfo* gi = cGlobals->gi;
|
|
|
|
if ( !!gi ) {
|
|
|
|
XP_ASSERT( !cGlobals->dict );
|
|
|
|
cGlobals->dict = linux_dictionary_make( MPPARM(cGlobals->util->mpool) params,
|
|
|
|
gi->dictName, XP_TRUE );
|
|
|
|
gi->dictLang = dict_getLangCode( cGlobals->dict );
|
|
|
|
}
|
|
|
|
|
|
|
|
setOneSecondTimer( cGlobals );
|
|
|
|
return bGlobals;
|
|
|
|
} /* commonInit */
|
|
|
|
|
|
|
|
static void
|
|
|
|
disposeBoard( CursesBoardGlobals* bGlobals )
|
|
|
|
{
|
|
|
|
XP_LOGF( "%s(): passed bGlobals %p", __func__, bGlobals );
|
|
|
|
/* XP_ASSERT( 0 == bGlobals->refCount ); */
|
|
|
|
CommonGlobals* cGlobals = &bGlobals->cGlobals;
|
|
|
|
if ( !!bGlobals->boardWin ) {
|
|
|
|
cursesDrawCtxtFree( cGlobals->draw );
|
|
|
|
wclear( bGlobals->boardWin );
|
|
|
|
wrefresh( bGlobals->boardWin );
|
|
|
|
delwin( bGlobals->boardWin );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !!bGlobals->cbState->menuState ) {
|
|
|
|
cmenu_pop( bGlobals->cbState->menuState );
|
|
|
|
}
|
|
|
|
|
|
|
|
clearOneSecondTimer( cGlobals );
|
|
|
|
|
|
|
|
gi_disposePlayerInfo( MPPARM(cGlobals->util->mpool) cGlobals->gi );
|
|
|
|
game_dispose( &cGlobals->game );
|
|
|
|
|
|
|
|
if ( !!cGlobals->dict ) {
|
|
|
|
dict_unref( cGlobals->dict );
|
|
|
|
}
|
|
|
|
|
|
|
|
disposeUtil( cGlobals );
|
|
|
|
|
|
|
|
CursesBoardState* cbState = bGlobals->cbState;
|
|
|
|
cbState->games = g_slist_remove( cbState->games, bGlobals );
|
|
|
|
|
|
|
|
/* onCursesBoardClosing( bGlobals->aGlobals, bGlobals ); */
|
|
|
|
g_free( bGlobals );
|
|
|
|
}
|
|
|
|
|
|
|
|
static CursesBoardGlobals*
|
|
|
|
ref( CursesBoardGlobals* bGlobals )
|
|
|
|
{
|
|
|
|
++bGlobals->refCount;
|
|
|
|
XP_LOGF( "%s(): refCount now %d", __func__, bGlobals->refCount );
|
|
|
|
return bGlobals;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
unref( CursesBoardGlobals* bGlobals )
|
|
|
|
{
|
|
|
|
--bGlobals->refCount;
|
|
|
|
XP_LOGF( "%s(): refCount now %d", __func__, bGlobals->refCount );
|
|
|
|
if ( 0 == bGlobals->refCount ) {
|
|
|
|
disposeBoard( bGlobals );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
utf8_len( const char* face )
|
|
|
|
{
|
|
|
|
const int max = strlen(face);
|
|
|
|
int count = 0;
|
|
|
|
mbstate_t ps = {0};
|
|
|
|
for ( int offset = 0; offset < max; ) {
|
|
|
|
size_t nBytes = mbrlen( &face[offset], max - offset, &ps );
|
|
|
|
if ( 0 < nBytes ) {
|
|
|
|
++count;
|
|
|
|
offset += nBytes;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
getFromDict( const CommonGlobals* cGlobals, XP_U16* fontWidthP,
|
|
|
|
XP_U16* fontHtP )
|
|
|
|
{
|
|
|
|
int maxLen = 0;
|
|
|
|
|
|
|
|
DictionaryCtxt* dict = cGlobals->dict;
|
|
|
|
for ( Tile tile = 0; tile < dict->nFaces; ++tile ) {
|
|
|
|
const XP_UCHAR* face = dict_getTileString( dict, tile );
|
|
|
|
int thisLen = utf8_len( face );
|
|
|
|
/* XP_LOGF( "%s(): looking at face '%s' with len %d", __func__, */
|
|
|
|
/* face, thisLen ); */
|
|
|
|
if ( thisLen > maxLen ) {
|
|
|
|
maxLen = thisLen;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* XP_LOGF( "%s(): width = %d", __func__, maxLen ); */
|
|
|
|
*fontWidthP = maxLen;
|
|
|
|
*fontHtP = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
setupBoard( CursesBoardGlobals* bGlobals )
|
|
|
|
{
|
|
|
|
LOG_FUNC();
|
|
|
|
/* positionSizeStuff( bGlobals ); */
|
|
|
|
CommonGlobals* cGlobals = &bGlobals->cGlobals;
|
|
|
|
BoardCtxt* board = cGlobals->game.board;
|
|
|
|
const int width = bGlobals->winWidth;
|
|
|
|
const int height = bGlobals->winHeight;
|
|
|
|
|
|
|
|
XP_U16 fontWidth, fontHt;
|
|
|
|
getFromDict( cGlobals, &fontWidth, &fontHt );
|
|
|
|
BoardDims dims;
|
|
|
|
board_figureLayout( board, cGlobals->gi,
|
|
|
|
0, 0, width, height, 100,
|
|
|
|
150, 200, /* percents */
|
|
|
|
width*75/100,
|
|
|
|
fontWidth, fontHt,
|
|
|
|
XP_FALSE, &dims );
|
|
|
|
board_applyLayout( board, &dims );
|
|
|
|
XP_LOGF( "%s(): calling board_draw()", __func__ );
|
|
|
|
board_draw( board );
|
|
|
|
}
|
|
|
|
|
|
|
|
static CursesBoardGlobals*
|
|
|
|
initNoDraw( CursesBoardState* cbState, sqlite3_int64 rowid )
|
|
|
|
{
|
|
|
|
LOG_FUNC();
|
|
|
|
CursesBoardGlobals* bGlobals = commonInit( cbState, rowid );
|
|
|
|
CommonGlobals* cGlobals = &bGlobals->cGlobals;
|
|
|
|
if ( -1 == rowid ) {
|
|
|
|
cGlobals->rowid = -1;
|
|
|
|
}
|
|
|
|
linuxOpenGame( cGlobals, &bGlobals->procs );
|
|
|
|
return ref( bGlobals );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
enableDraw( CursesBoardGlobals* bGlobals, int width, int top, int height )
|
|
|
|
{
|
|
|
|
LOG_FUNC();
|
|
|
|
XP_ASSERT( 0 != width );
|
|
|
|
bGlobals->boardWin = newwin( height, width, top, 0 );
|
|
|
|
getmaxyx( bGlobals->boardWin, bGlobals->winHeight, bGlobals->winWidth );
|
|
|
|
|
|
|
|
CommonGlobals* cGlobals = &bGlobals->cGlobals;
|
|
|
|
cGlobals->draw = cursesDrawCtxtMake( bGlobals->boardWin );
|
|
|
|
board_setDraw( cGlobals->game.board, cGlobals->draw );
|
|
|
|
|
|
|
|
setupBoard( bGlobals );
|
|
|
|
|
|
|
|
initMenus( bGlobals );
|
|
|
|
}
|
|
|
|
|
|
|
|
static CursesBoardGlobals*
|
|
|
|
findOrOpen( CursesBoardState* cbState, sqlite3_int64 rowid )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* result = NULL;
|
|
|
|
for ( GSList* iter = cbState->games;
|
|
|
|
rowid != 0 && !!iter && !result; iter = iter->next ) {
|
|
|
|
CursesBoardGlobals* one = (CursesBoardGlobals*)iter->data;
|
|
|
|
if ( one->cGlobals.rowid == rowid ) {
|
|
|
|
result = one;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !result ) {
|
|
|
|
result = initNoDraw( cbState, rowid );
|
|
|
|
setupBoard( result );
|
|
|
|
cbState->games = g_slist_append( cbState->games, result );
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
XP_U16
|
|
|
|
cb_feedBuffer( CursesBoardState* cbState, sqlite3_int64 rowid,
|
|
|
|
const XP_U8* buf, XP_U16 len, const CommsAddrRec* from )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = findOrOpen( cbState, rowid );
|
|
|
|
CommonGlobals* cGlobals = &bGlobals->cGlobals;
|
|
|
|
gameGotBuf( cGlobals, XP_TRUE, buf, len, from );
|
|
|
|
XP_U16 seed = comms_getChannelSeed( cGlobals->game.comms );
|
|
|
|
return seed;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
kill_board( gpointer data )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)data;
|
|
|
|
disposeBoard( bGlobals );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cb_closeAll( CursesBoardState* cbState )
|
|
|
|
{
|
|
|
|
g_slist_free_full( cbState->games, kill_board );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
cursesUserError( CursesBoardGlobals* bGlobals, const char* format, ... )
|
|
|
|
{
|
|
|
|
char buf[512];
|
|
|
|
va_list ap;
|
|
|
|
va_start( ap, format );
|
|
|
|
|
|
|
|
vsprintf( buf, format, ap );
|
|
|
|
|
|
|
|
const char* buttons[] = {"OK"};
|
|
|
|
(void)cursesask( bGlobals->boardWin, buf, VSIZE(buttons), buttons );
|
|
|
|
|
|
|
|
va_end(ap);
|
|
|
|
} /* cursesUserError */
|
|
|
|
|
|
|
|
static void
|
|
|
|
curses_util_notifyPickTileBlank( XW_UtilCtxt* uc, XP_U16 playerNum,
|
|
|
|
XP_U16 XP_UNUSED(col), XP_U16 XP_UNUSED(row),
|
|
|
|
const XP_UCHAR** texts, XP_U16 nTiles )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* globals = (CursesBoardGlobals*)uc->closure;
|
|
|
|
char query[128];
|
|
|
|
char* playerName = globals->cGlobals.gi->players[playerNum].name;
|
|
|
|
|
|
|
|
snprintf( query, sizeof(query),
|
|
|
|
"Pick tile for %s! (Tab or type letter to select "
|
|
|
|
"then hit <cr>.)", playerName );
|
|
|
|
|
|
|
|
/*index = */curses_askLetter( globals->boardWin, query, texts, nTiles );
|
|
|
|
// return index;
|
|
|
|
} /* util_userPickTile */
|
|
|
|
|
|
|
|
static void
|
|
|
|
curses_util_informNeedPickTiles( XW_UtilCtxt* XP_UNUSED(uc),
|
|
|
|
XP_Bool XP_UNUSED(isInitial),
|
|
|
|
XP_U16 XP_UNUSED(player),
|
|
|
|
XP_U16 XP_UNUSED(nToPick),
|
|
|
|
XP_U16 XP_UNUSED(nFaces),
|
|
|
|
const XP_UCHAR** XP_UNUSED(faces),
|
|
|
|
const XP_U16* XP_UNUSED(counts) )
|
|
|
|
{
|
|
|
|
/* CursesBoardGlobals* globals = (CursesBoardGlobals*)uc->closure; */
|
|
|
|
/* char query[128]; */
|
|
|
|
/* XP_S16 index; */
|
|
|
|
/* char* playerName = globals->cGlobals.gi->players[playerNum].name; */
|
|
|
|
|
|
|
|
/* snprintf( query, sizeof(query), */
|
|
|
|
/* "Pick tile for %s! (Tab or type letter to select " */
|
|
|
|
/* "then hit <cr>.)", playerName ); */
|
|
|
|
|
|
|
|
/* index = curses_askLetter( globals, query, texts, nTiles ); */
|
|
|
|
/* return index; */
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
curses_util_userError( XW_UtilCtxt* uc, UtilErrID id )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* globals = (CursesBoardGlobals*)uc->closure;
|
|
|
|
XP_Bool silent;
|
|
|
|
const XP_UCHAR* message = linux_getErrString( id, &silent );
|
|
|
|
|
|
|
|
if ( silent ) {
|
|
|
|
XP_LOGF( "silent userError: %s", message );
|
|
|
|
} else {
|
|
|
|
cursesUserError( globals, message );
|
|
|
|
}
|
|
|
|
} /* curses_util_userError */
|
|
|
|
|
|
|
|
static gint
|
|
|
|
ask_move( gpointer data )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)data;
|
|
|
|
CommonGlobals* cGlobals = &bGlobals->cGlobals;
|
|
|
|
const char* answers[] = {"Ok", "Cancel", NULL};
|
|
|
|
|
|
|
|
if (0 == cursesask(bGlobals->boardWin, cGlobals->question,
|
|
|
|
VSIZE(answers)-1, answers) ) {
|
|
|
|
BoardCtxt* board = cGlobals->game.board;
|
|
|
|
if ( board_commitTurn( board, XP_TRUE, XP_TRUE, NULL ) ) {
|
|
|
|
board_draw( board );
|
|
|
|
linuxSaveGame( &bGlobals->cGlobals );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
curses_util_informNeedPassword( XW_UtilCtxt* XP_UNUSED(uc),
|
|
|
|
XP_U16 XP_UNUSED_DBG(playerNum),
|
|
|
|
const XP_UCHAR* XP_UNUSED_DBG(name) )
|
|
|
|
{
|
|
|
|
XP_WARNF( "curses_util_informNeedPassword(num=%d, name=%s", playerNum, name );
|
|
|
|
} /* curses_util_askPassword */
|
|
|
|
|
|
|
|
static void
|
|
|
|
curses_util_yOffsetChange( XW_UtilCtxt* XP_UNUSED(uc),
|
|
|
|
XP_U16 XP_UNUSED(maxOffset),
|
|
|
|
XP_U16 XP_UNUSED(oldOffset), XP_U16 XP_UNUSED(newOffset) )
|
|
|
|
{
|
|
|
|
/* if ( oldOffset != newOffset ) { */
|
|
|
|
/* XP_WARNF( "curses_util_yOffsetChange(%d,%d,%d) not implemented", */
|
|
|
|
/* maxOffset, oldOffset, newOffset ); */
|
|
|
|
/* } */
|
|
|
|
} /* curses_util_yOffsetChange */
|
|
|
|
|
|
|
|
#ifdef XWFEATURE_TURNCHANGENOTIFY
|
|
|
|
static void
|
|
|
|
curses_util_turnChanged( XW_UtilCtxt* XP_UNUSED(uc), XP_S16 XP_UNUSED_DBG(newTurn) )
|
|
|
|
{
|
|
|
|
XP_LOGF( "%s(turn=%d)", __func__, newTurn );
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void
|
|
|
|
curses_util_notifyIllegalWords( XW_UtilCtxt* XP_UNUSED(uc),
|
|
|
|
BadWordInfo* XP_UNUSED(bwi),
|
|
|
|
XP_U16 XP_UNUSED(player),
|
|
|
|
XP_Bool XP_UNUSED(turnLost) )
|
|
|
|
{
|
|
|
|
XP_WARNF( "curses_util_notifyIllegalWords not implemented" );
|
|
|
|
} /* curses_util_notifyIllegalWord */
|
|
|
|
|
|
|
|
/* this needs to change!!! */
|
|
|
|
static void
|
|
|
|
curses_util_notifyMove( XW_UtilCtxt* uc, XWStreamCtxt* stream )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* globals = (CursesBoardGlobals*)uc->closure;
|
|
|
|
CommonGlobals* cGlobals = &globals->cGlobals;
|
|
|
|
XP_U16 len = stream_getSize( stream );
|
|
|
|
XP_ASSERT( len <= VSIZE(cGlobals->question) );
|
|
|
|
stream_getBytes( stream, cGlobals->question, len );
|
|
|
|
(void)ADD_ONETIME_IDLE( ask_move, globals );
|
|
|
|
} /* curses_util_not */
|
|
|
|
|
|
|
|
static gint
|
|
|
|
ask_trade( gpointer data )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)data;
|
|
|
|
CommonGlobals* cGlobals = &bGlobals->cGlobals;
|
|
|
|
|
|
|
|
const char* buttons[] = { "Ok", "Cancel" };
|
|
|
|
if (0 == cursesask( bGlobals->boardWin, cGlobals->question,
|
|
|
|
VSIZE(buttons), buttons ) ) {
|
|
|
|
BoardCtxt* board = cGlobals->game.board;
|
|
|
|
if ( board_commitTurn( board, XP_TRUE, XP_TRUE, NULL ) ) {
|
|
|
|
board_draw( board );
|
|
|
|
linuxSaveGame( cGlobals );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
curses_util_notifyTrade( XW_UtilCtxt* uc, const XP_UCHAR** tiles, XP_U16 nTiles )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* globals = (CursesBoardGlobals*)uc->closure;
|
|
|
|
formatConfirmTrade( &globals->cGlobals, tiles, nTiles );
|
|
|
|
(void)ADD_ONETIME_IDLE( ask_trade, globals );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
curses_util_trayHiddenChange( XW_UtilCtxt* XP_UNUSED(uc),
|
|
|
|
XW_TrayVisState XP_UNUSED(state),
|
|
|
|
XP_U16 XP_UNUSED(nVisibleRows) )
|
|
|
|
{
|
|
|
|
/* nothing to do if we don't have a scrollbar */
|
|
|
|
} /* curses_util_trayHiddenChange */
|
|
|
|
|
|
|
|
static void
|
|
|
|
cursesShowFinalScores( CursesBoardGlobals* bGlobals )
|
|
|
|
{
|
|
|
|
XWStreamCtxt* stream;
|
|
|
|
XP_UCHAR* text;
|
|
|
|
|
|
|
|
CommonGlobals* cGlobals = &bGlobals->cGlobals;
|
|
|
|
stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
|
|
|
|
cGlobals->params->vtMgr );
|
|
|
|
server_writeFinalScores( cGlobals->game.server, stream );
|
|
|
|
|
|
|
|
text = strFromStream( stream );
|
|
|
|
|
|
|
|
const char* buttons[] = { "Ok" };
|
|
|
|
(void)cursesask( bGlobals->boardWin, text, VSIZE(buttons), buttons );
|
|
|
|
|
|
|
|
free( text );
|
|
|
|
stream_destroy( stream );
|
|
|
|
} /* cursesShowFinalScores */
|
|
|
|
|
|
|
|
static void
|
|
|
|
curses_util_informMove( XW_UtilCtxt* uc, XP_S16 XP_UNUSED(turn),
|
|
|
|
XWStreamCtxt* expl, XWStreamCtxt* XP_UNUSED(words))
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)uc->closure;
|
|
|
|
char* question = strFromStream( expl );
|
|
|
|
const char* buttons[] = { "Ok" };
|
|
|
|
(void)cursesask( bGlobals->boardWin, question, VSIZE(buttons), buttons );
|
|
|
|
free( question );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
curses_util_informUndo( XW_UtilCtxt* XP_UNUSED(uc))
|
|
|
|
{
|
|
|
|
LOG_FUNC();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
curses_util_notifyGameOver( XW_UtilCtxt* uc, XP_S16 quitter )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)uc->closure;
|
|
|
|
CommonGlobals* cGlobals = &bGlobals->cGlobals;
|
|
|
|
LaunchParams* params = cGlobals->params;
|
|
|
|
board_draw( cGlobals->game.board );
|
|
|
|
|
|
|
|
/* game belongs in cGlobals... */
|
|
|
|
if ( params->printHistory ) {
|
|
|
|
catGameHistory( cGlobals );
|
|
|
|
}
|
|
|
|
|
|
|
|
catFinalScores( cGlobals, quitter );
|
|
|
|
|
|
|
|
if ( params->quitAfter >= 0 ) {
|
|
|
|
sleep( params->quitAfter );
|
|
|
|
handleQuit( bGlobals->cbState->aGlobals, 0 );
|
|
|
|
} else if ( params->undoWhenDone ) {
|
|
|
|
server_handleUndo( cGlobals->game.server, 0 );
|
|
|
|
} else if ( !params->skipGameOver && !!bGlobals->boardWin ) {
|
|
|
|
/* This is modal. Don't show if quitting */
|
|
|
|
cursesShowFinalScores( bGlobals );
|
|
|
|
}
|
|
|
|
} /* curses_util_notifyGameOver */
|
|
|
|
|
|
|
|
static void
|
|
|
|
curses_util_informNetDict( XW_UtilCtxt* uc, XP_LangCode XP_UNUSED(lang),
|
|
|
|
const XP_UCHAR* XP_UNUSED_DBG(oldName),
|
|
|
|
const XP_UCHAR* XP_UNUSED_DBG(newName),
|
|
|
|
const XP_UCHAR* XP_UNUSED_DBG(newSum),
|
|
|
|
XWPhoniesChoice phoniesAction )
|
|
|
|
{
|
|
|
|
XP_USE(uc);
|
|
|
|
XP_USE(phoniesAction);
|
|
|
|
XP_LOGF( "%s: %s => %s (cksum: %s)", __func__, oldName, newName, newSum );
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
|
|
|
static XP_Bool
|
|
|
|
curses_util_getTraySearchLimits( XW_UtilCtxt* XP_UNUSED(uc),
|
|
|
|
XP_U16* XP_UNUSED(min), XP_U16* XP_UNUSED(max) )
|
|
|
|
{
|
|
|
|
XP_ASSERT(0);
|
|
|
|
return XP_TRUE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef XWFEATURE_HILITECELL
|
|
|
|
static XP_Bool
|
|
|
|
curses_util_hiliteCell( XW_UtilCtxt* uc,
|
|
|
|
XP_U16 XP_UNUSED(col), XP_U16 XP_UNUSED(row) )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* globals = (CursesBoardGlobals*)uc->closure;
|
|
|
|
if ( globals->cGlobals.params->sleepOnAnchor ) {
|
|
|
|
usleep( 10000 );
|
|
|
|
}
|
|
|
|
return XP_TRUE;
|
|
|
|
} /* curses_util_hiliteCell */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static XP_Bool
|
|
|
|
curses_util_engineProgressCallback( XW_UtilCtxt* XP_UNUSED(uc) )
|
|
|
|
{
|
|
|
|
return XP_TRUE;
|
|
|
|
} /* curses_util_engineProgressCallback */
|
|
|
|
|
|
|
|
#ifdef USE_GLIBLOOP
|
|
|
|
static gboolean
|
|
|
|
timerFired( gpointer data )
|
|
|
|
{
|
|
|
|
TimerInfo* ti = (TimerInfo*)data;
|
|
|
|
CommonGlobals* globals = ti->globals;
|
|
|
|
XWTimerReason why = ti - globals->timerInfo;
|
|
|
|
if ( linuxFireTimer( globals, why ) ) {
|
|
|
|
board_draw( globals->game.board );
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void
|
|
|
|
curses_util_setTimer( XW_UtilCtxt* uc, XWTimerReason why, XP_U16 when,
|
|
|
|
XWTimerProc proc, void* closure )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* globals = (CursesBoardGlobals*)uc->closure;
|
|
|
|
TimerInfo* ti = &globals->cGlobals.timerInfo[why];
|
|
|
|
|
|
|
|
ti->proc = proc;
|
|
|
|
ti->closure = closure;
|
|
|
|
|
|
|
|
#ifdef USE_GLIBLOOP
|
|
|
|
ti->globals = &globals->cGlobals;
|
|
|
|
(void)g_timeout_add_seconds( when, timerFired, ti );
|
|
|
|
#else
|
|
|
|
ti->when = util_getCurSeconds(uc) + when;
|
|
|
|
#endif
|
|
|
|
} /* curses_util_setTimer */
|
|
|
|
|
|
|
|
static void
|
|
|
|
curses_util_clearTimer( XW_UtilCtxt* uc, XWTimerReason why )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* globals = (CursesBoardGlobals*)uc->closure;
|
|
|
|
globals->cGlobals.timerInfo[why].proc = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef USE_GLIBLOOP
|
|
|
|
static gboolean
|
|
|
|
onetime_idle( gpointer data )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)data;
|
|
|
|
XP_LOGF( "%s(): passed bGlobals %p", __func__, bGlobals );
|
|
|
|
XWGame* game = &bGlobals->cGlobals.game;
|
|
|
|
if ( !!game->server && server_do( game->server ) ) {
|
|
|
|
if ( !!game->board ) {
|
|
|
|
board_draw( game->board );
|
|
|
|
}
|
|
|
|
linuxSaveGame( &bGlobals->cGlobals );
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void
|
|
|
|
curses_util_requestTime( XW_UtilCtxt* uc )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* globals = (CursesBoardGlobals*)uc->closure;
|
|
|
|
#if 1
|
|
|
|
(void)ADD_ONETIME_IDLE( onetime_idle, globals );
|
|
|
|
#else
|
|
|
|
(void)g_timeout_add( 1,// interval,
|
|
|
|
onetime_idle, globals );
|
|
|
|
#endif
|
|
|
|
} /* curses_util_requestTime */
|
|
|
|
|
|
|
|
static XP_Bool
|
|
|
|
curses_util_altKeyDown( XW_UtilCtxt* XP_UNUSED(uc) )
|
|
|
|
{
|
|
|
|
return XP_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
curses_util_remSelected( XW_UtilCtxt* uc )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)uc->closure;
|
|
|
|
CommonGlobals* cGlobals = (CommonGlobals*)uc->closure;
|
|
|
|
XWStreamCtxt* stream;
|
|
|
|
XP_UCHAR* text;
|
|
|
|
|
|
|
|
stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
|
|
|
|
cGlobals->params->vtMgr );
|
|
|
|
board_formatRemainingTiles( cGlobals->game.board, stream );
|
|
|
|
|
|
|
|
text = strFromStream( stream );
|
|
|
|
|
|
|
|
const char* buttons[] = { "Ok" };
|
|
|
|
(void)cursesask( bGlobals->boardWin, text, VSIZE(buttons), buttons );
|
|
|
|
|
|
|
|
free( text );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
curses_util_bonusSquareHeld( XW_UtilCtxt* uc, XWBonusType bonus )
|
|
|
|
{
|
|
|
|
LOG_FUNC();
|
|
|
|
XP_USE( uc );
|
|
|
|
XP_USE( bonus );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
curses_util_playerScoreHeld( XW_UtilCtxt* uc, XP_U16 player )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)uc->closure;
|
|
|
|
LastMoveInfo lmi;
|
|
|
|
if ( model_getPlayersLastScore( bGlobals->cGlobals.game.model,
|
|
|
|
player, &lmi ) ) {
|
|
|
|
XP_UCHAR buf[128];
|
|
|
|
formatLMI( &lmi, buf, VSIZE(buf) );
|
|
|
|
const char* buttons[] = { "Ok" };
|
|
|
|
(void)cursesask( bGlobals->boardWin, buf, VSIZE(buttons), buttons );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef XWFEATURE_BOARDWORDS
|
|
|
|
static void
|
|
|
|
curses_util_cellSquareHeld( XW_UtilCtxt* uc, XWStreamCtxt* words )
|
|
|
|
{
|
|
|
|
XP_USE( uc );
|
|
|
|
catOnClose( words, NULL );
|
|
|
|
fprintf( stderr, "\n" );
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
|
|
static XWStreamCtxt*
|
|
|
|
curses_util_makeStreamFromAddr(XW_UtilCtxt* uc, XP_PlayerAddr channelNo )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* globals = (CursesBoardGlobals*)uc->closure;
|
|
|
|
LaunchParams* params = globals->cGlobals.params;
|
|
|
|
|
|
|
|
XWStreamCtxt* stream = mem_stream_make( MPPARM(uc->mpool) params->vtMgr,
|
|
|
|
&globals->cGlobals, channelNo,
|
|
|
|
sendOnClose );
|
|
|
|
return stream;
|
|
|
|
} /* curses_util_makeStreamFromAddr */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef XWFEATURE_CHAT
|
|
|
|
static void
|
|
|
|
curses_util_showChat( XW_UtilCtxt* uc,
|
|
|
|
const XP_UCHAR* const XP_UNUSED_DBG(msg),
|
|
|
|
XP_S16 XP_UNUSED_DBG(from), XP_U32 XP_UNUSED(timestamp) )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* globals = (CursesBoardGlobals*)uc->closure;
|
|
|
|
globals->nChatsSent = 0;
|
|
|
|
# ifdef DEBUG
|
|
|
|
const XP_UCHAR* name = "<unknown>";
|
|
|
|
if ( 0 <= from ) {
|
|
|
|
CommonGlobals* cGlobals = &globals->cGlobals;
|
|
|
|
name = cGlobals->gi->players[from].name;
|
|
|
|
}
|
|
|
|
XP_LOGF( "%s: got \"%s\" from %s", __func__, msg, name );
|
|
|
|
# endif
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void
|
|
|
|
setupCursesUtilCallbacks( CursesBoardGlobals* bGlobals, XW_UtilCtxt* util )
|
|
|
|
{
|
|
|
|
util->closure = bGlobals;
|
|
|
|
#define SET_PROC(NAM) util->vtable->m_util_##NAM = curses_util_##NAM
|
|
|
|
SET_PROC(makeStreamFromAddr);
|
|
|
|
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
|
|
|
|
SET_PROC(turnChanged);
|
|
|
|
#endif
|
|
|
|
SET_PROC(informMove);
|
|
|
|
SET_PROC(informUndo);
|
|
|
|
SET_PROC(informNetDict);
|
|
|
|
SET_PROC(notifyGameOver);
|
|
|
|
#ifdef XWFEATURE_HILITECELL
|
|
|
|
SET_PROC(hiliteCell);
|
|
|
|
#endif
|
|
|
|
SET_PROC(engineProgressCallback);
|
|
|
|
SET_PROC(setTimer);
|
|
|
|
SET_PROC(clearTimer);
|
|
|
|
SET_PROC(requestTime);
|
|
|
|
SET_PROC(altKeyDown); /* ?? */
|
|
|
|
SET_PROC(notifyIllegalWords);
|
|
|
|
SET_PROC(remSelected);
|
|
|
|
#ifndef XWFEATURE_MINIWIN
|
|
|
|
SET_PROC(bonusSquareHeld);
|
|
|
|
SET_PROC(playerScoreHeld);
|
|
|
|
#endif
|
|
|
|
#ifdef XWFEATURE_BOARDWORDS
|
|
|
|
SET_PROC(cellSquareHeld);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
|
|
|
SET_PROC(getTraySearchLimits);
|
|
|
|
#endif
|
|
|
|
#ifdef XWFEATURE_CHAT
|
|
|
|
SET_PROC(showChat);
|
|
|
|
#endif
|
|
|
|
#undef SET_PROC
|
|
|
|
|
|
|
|
assertAllCallbacksSet( util );
|
|
|
|
} /* setupCursesUtilCallbacks */
|
|
|
|
|
|
|
|
static bool
|
|
|
|
handleFlip( void* closure, int XP_UNUSED(key) )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)closure;
|
2020-01-24 18:22:49 +01:00
|
|
|
CommonGlobals* cGlobals = &bGlobals->cGlobals;
|
|
|
|
if ( board_flip( cGlobals->game.board ) ) {
|
|
|
|
board_draw( cGlobals->game.board );
|
|
|
|
}
|
|
|
|
|
2020-01-24 18:05:16 +01:00
|
|
|
return XP_TRUE;
|
|
|
|
} /* handleFlip */
|
|
|
|
|
|
|
|
static bool
|
|
|
|
handleToggleValues( void* closure, int XP_UNUSED(key) )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)closure;
|
|
|
|
bGlobals->doDraw = board_toggle_showValues( bGlobals->cGlobals.game.board );
|
|
|
|
return XP_TRUE;
|
|
|
|
} /* handleToggleValues */
|
|
|
|
|
|
|
|
static bool
|
|
|
|
handleBackspace( void* closure, int XP_UNUSED(key) )
|
|
|
|
{
|
|
|
|
XP_Bool handled;
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)closure;
|
|
|
|
bGlobals->doDraw = board_handleKey( bGlobals->cGlobals.game.board,
|
|
|
|
XP_CURSOR_KEY_DEL, &handled );
|
|
|
|
return XP_TRUE;
|
|
|
|
} /* handleBackspace */
|
|
|
|
|
|
|
|
static bool
|
|
|
|
handleUndo( void* closure, int XP_UNUSED(key) )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)closure;
|
|
|
|
bGlobals->doDraw = server_handleUndo( bGlobals->cGlobals.game.server, 0 );
|
|
|
|
return XP_TRUE;
|
|
|
|
} /* handleUndo */
|
|
|
|
|
|
|
|
static bool
|
|
|
|
handleReplace( void* closure, int XP_UNUSED(key) )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)closure;
|
|
|
|
bGlobals->doDraw = board_replaceTiles( bGlobals->cGlobals.game.board );
|
|
|
|
return XP_TRUE;
|
|
|
|
} /* handleReplace */
|
|
|
|
|
|
|
|
static bool
|
|
|
|
handleCommit( void* closure, int XP_UNUSED(key) )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)closure;
|
|
|
|
bGlobals->doDraw = board_commitTurn( bGlobals->cGlobals.game.board,
|
|
|
|
XP_FALSE, XP_FALSE, NULL );
|
|
|
|
return XP_TRUE;
|
|
|
|
} /* handleCommit */
|
|
|
|
|
|
|
|
static bool
|
|
|
|
handleJuggle( void* closure, int XP_UNUSED(key) )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)closure;
|
|
|
|
bGlobals->doDraw = board_juggleTray( bGlobals->cGlobals.game.board );
|
|
|
|
return XP_TRUE;
|
|
|
|
} /* handleJuggle */
|
|
|
|
|
|
|
|
static bool
|
|
|
|
handleHide( void* closure, int XP_UNUSED(key) )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)closure;
|
|
|
|
CommonGlobals* cGlobals = &bGlobals->cGlobals;
|
|
|
|
XW_TrayVisState curState =
|
|
|
|
board_getTrayVisState( cGlobals->game.board );
|
|
|
|
|
|
|
|
if ( curState == TRAY_REVEALED ) {
|
|
|
|
bGlobals->doDraw = board_hideTray( cGlobals->game.board );
|
|
|
|
} else {
|
|
|
|
bGlobals->doDraw = board_showTray( cGlobals->game.board );
|
|
|
|
}
|
|
|
|
|
|
|
|
return XP_TRUE;
|
|
|
|
} /* handleJuggle */
|
|
|
|
|
|
|
|
#ifdef KEYBOARD_NAV
|
|
|
|
static void
|
|
|
|
checkAssignFocus( BoardCtxt* board )
|
|
|
|
{
|
|
|
|
if ( OBJ_NONE == board_getFocusOwner(board) ) {
|
|
|
|
board_focusChanged( board, OBJ_BOARD, XP_TRUE );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static XP_Bool
|
|
|
|
handleFocusKey( CursesBoardGlobals* bGlobals, XP_Key key )
|
|
|
|
{
|
|
|
|
CommonGlobals* cGlobals = &bGlobals->cGlobals;
|
|
|
|
checkAssignFocus( cGlobals->game.board );
|
|
|
|
|
|
|
|
XP_Bool handled;
|
|
|
|
XP_Bool draw = board_handleKey( cGlobals->game.board, key, &handled );
|
|
|
|
if ( !handled ) {
|
|
|
|
BoardObjectType nxt;
|
|
|
|
BoardObjectType order[] = { OBJ_BOARD, OBJ_SCORE, OBJ_TRAY };
|
|
|
|
draw = linShiftFocus( cGlobals, key, order, &nxt ) || draw;
|
|
|
|
if ( nxt != OBJ_NONE ) {
|
|
|
|
changeMenuForFocus( bGlobals, nxt );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( draw ) {
|
|
|
|
board_draw( cGlobals->game.board );
|
|
|
|
}
|
|
|
|
return XP_TRUE;
|
|
|
|
} /* handleFocusKey */
|
|
|
|
|
|
|
|
static bool
|
|
|
|
handleAltLeft( void* closure, int XP_UNUSED(key) )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)closure;
|
|
|
|
return handleFocusKey( bGlobals, XP_CURSOR_KEY_ALTLEFT );
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
handleAltRight( void* closure, int XP_UNUSED(key) )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)closure;
|
|
|
|
return handleFocusKey( bGlobals, XP_CURSOR_KEY_ALTRIGHT );
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
handleAltUp( void* closure, int XP_UNUSED(key) )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)closure;
|
|
|
|
return handleFocusKey( bGlobals, XP_CURSOR_KEY_ALTUP );
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
handleAltDown( void* closure, int XP_UNUSED(key) )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)closure;
|
|
|
|
return handleFocusKey( bGlobals, XP_CURSOR_KEY_ALTDOWN );
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
handleHint( void* closure, int XP_UNUSED(key) )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)closure;
|
|
|
|
CommonGlobals* cGlobals = &bGlobals->cGlobals;
|
|
|
|
XP_Bool redo;
|
|
|
|
XP_Bool draw = board_requestHint( cGlobals->game.board,
|
|
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
|
|
|
XP_FALSE,
|
|
|
|
#endif
|
|
|
|
XP_FALSE, &redo );
|
|
|
|
if ( draw ) {
|
|
|
|
board_draw( cGlobals->game.board );
|
|
|
|
}
|
|
|
|
return XP_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
handleLeft( void* closure, int XP_UNUSED(key) )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)closure;
|
|
|
|
return handleFocusKey( bGlobals, XP_CURSOR_KEY_LEFT );
|
|
|
|
} /* handleLeft */
|
|
|
|
|
|
|
|
static bool
|
|
|
|
handleRight( void* closure, int XP_UNUSED(key) )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)closure;
|
|
|
|
return handleFocusKey( bGlobals, XP_CURSOR_KEY_RIGHT );
|
|
|
|
} /* handleRight */
|
|
|
|
|
|
|
|
static bool
|
|
|
|
handleUp( void* closure, int XP_UNUSED(key) )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)closure;
|
|
|
|
return handleFocusKey( bGlobals, XP_CURSOR_KEY_UP );
|
|
|
|
} /* handleUp */
|
|
|
|
|
|
|
|
static bool
|
|
|
|
handleDown( void* closure, int XP_UNUSED(key) )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)closure;
|
|
|
|
return handleFocusKey( bGlobals, XP_CURSOR_KEY_DOWN );
|
|
|
|
} /* handleDown */
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
idle_close_game( gpointer data )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)data;
|
|
|
|
linuxSaveGame( &bGlobals->cGlobals );
|
|
|
|
unref( bGlobals );
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
handleClose( void* closure, int XP_UNUSED(key) )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)closure;
|
|
|
|
(void)ADD_ONETIME_IDLE( idle_close_game, bGlobals );
|
|
|
|
return XP_TRUE;
|
|
|
|
} /* handleDown */
|
|
|
|
|
|
|
|
static bool
|
|
|
|
handleSpace( void* closure, int XP_UNUSED(key) )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)closure;
|
|
|
|
BoardCtxt* board = bGlobals->cGlobals.game.board;
|
|
|
|
checkAssignFocus( board );
|
|
|
|
XP_Bool handled;
|
|
|
|
(void)board_handleKey( board, XP_RAISEFOCUS_KEY, &handled );
|
|
|
|
board_draw( board );
|
|
|
|
return XP_TRUE;
|
|
|
|
} /* handleSpace */
|
|
|
|
|
|
|
|
static bool
|
|
|
|
handleRet( void* closure, int XP_UNUSED(key) )
|
|
|
|
{
|
|
|
|
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)closure;
|
|
|
|
BoardCtxt* board = bGlobals->cGlobals.game.board;
|
|
|
|
XP_Bool handled;
|
|
|
|
(void)board_handleKey( board, XP_RETURN_KEY, &handled );
|
|
|
|
board_draw( board );
|
|
|
|
return XP_TRUE;
|
|
|
|
} /* handleRet */
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void
|
|
|
|
relay_connd_curses( void* XP_UNUSED(closure), XP_UCHAR* const XP_UNUSED(room),
|
|
|
|
XP_Bool XP_UNUSED(reconnect), XP_U16 XP_UNUSED(devOrder),
|
|
|
|
XP_Bool XP_UNUSED_DBG(allHere),
|
|
|
|
XP_U16 XP_UNUSED_DBG(nMissing) )
|
|
|
|
{
|
|
|
|
XP_LOGF( "%s got allHere: %s; nMissing: %d", __func__,
|
|
|
|
allHere?"true":"false", nMissing );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
relay_error_curses( void* XP_UNUSED(closure), XWREASON XP_UNUSED_DBG(relayErr) )
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
XP_LOGF( "%s(%s)", __func__, XWREASON2Str( relayErr ) );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static XP_Bool
|
|
|
|
relay_sendNoConn_curses( const XP_U8* msg, XP_U16 len,
|
|
|
|
const XP_UCHAR* XP_UNUSED(msgNo),
|
|
|
|
const XP_UCHAR* relayID, void* closure )
|
|
|
|
{
|
|
|
|
XP_Bool success = XP_FALSE;
|
|
|
|
CommonGlobals* cGlobals = (CommonGlobals*)closure;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
return success;
|
|
|
|
|
|
|
|
} /* relay_sendNoConn_curses */
|
|
|
|
|
|
|
|
#ifdef RELAY_VIA_HTTP
|
|
|
|
static void
|
|
|
|
relay_requestJoin_curses( void* closure, const XP_UCHAR* devID, const XP_UCHAR* room,
|
|
|
|
XP_U16 nPlayersHere, XP_U16 nPlayersTotal,
|
|
|
|
XP_U16 seed, XP_U16 lang )
|
|
|
|
{
|
|
|
|
CommonGlobals* cGlobals = (CommonGlobals*)closure;
|
|
|
|
relaycon_join( cGlobals->params, devID, room, nPlayersHere, nPlayersTotal,
|
|
|
|
seed, lang, onJoined, globals );
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void
|
|
|
|
curses_countChanged( void* XP_UNUSED(closure), XP_U16 XP_UNUSED_DBG(newCount) )
|
|
|
|
{
|
|
|
|
XP_LOGF( "%s(newCount=%d)", __func__, newCount );
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef COMMS_XPORT_FLAGSPROC
|
|
|
|
static XP_U32
|
|
|
|
curses_getFlags( void* XP_UNUSED(closure) )
|
|
|
|
{
|
|
|
|
return COMMS_XPORT_FLAGS_HASNOCONN;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void
|
|
|
|
relay_status_curses( void* XP_UNUSED(closure), CommsRelayState XP_UNUSED_DBG(state) )
|
|
|
|
{
|
|
|
|
/* CommonGlobals* cGlobals = (CommonGlobals*)closure; */
|
|
|
|
// bGlobals->commsRelayState = state;
|
|
|
|
XP_LOGF( "%s got status: %s", __func__, CommsRelayState2Str(state) );
|
|
|
|
}
|