diff --git a/xwords4/linux/cursesmain.c b/xwords4/linux/cursesmain.c index 5a6055297..908491571 100644 --- a/xwords4/linux/cursesmain.c +++ b/xwords4/linux/cursesmain.c @@ -1776,7 +1776,7 @@ cursesGotBuf( void* closure, const XP_U8* buf, XP_U16 len ) rowidFromToken( XP_NTOHL( clientToken ), &ignore, &seed ); XP_ASSERT( seed == comms_getChannelSeed( globals->cGlobals.game.comms ) ); if ( seed == comms_getChannelSeed( globals->cGlobals.game.comms ) ) { - gameGotBuf( &globals->cGlobals, XP_TRUE, buf, len ); + gameGotBuf( &globals->cGlobals, XP_TRUE, buf, len, NULL ); } else { XP_LOGF( "%s: dropping packet; meant for a different device", __func__ ); diff --git a/xwords4/linux/gamesdb.c b/xwords4/linux/gamesdb.c index df60ffac2..62bb42af6 100644 --- a/xwords4/linux/gamesdb.c +++ b/xwords4/linux/gamesdb.c @@ -42,6 +42,7 @@ openGamesDB( const char* dbName ) ",turn INT(2)" ",nmoves INT" ",seed INT" + ",gameid INT" ",nmissing INT(2)" ")"; result = sqlite3_exec( pDb, createGamesStr, NULL, NULL, NULL ); @@ -121,6 +122,8 @@ summarize( CommonGlobals* cGlobals ) XP_S16 turn = server_getCurrentTurn( cGlobals->game.server ); XP_U16 seed = 0; XP_S16 nMissing = 0; + XP_U32 gameID = cGlobals->gi->gameID; + XP_ASSERT( 0 != gameID ); CommsAddrRec addr = {0}; gchar* room = ""; @@ -148,11 +151,11 @@ summarize( CommonGlobals* cGlobals ) } const char* fmt = "UPDATE games " - " SET room='%s', ended=%d, turn=%d, nmissing=%d, nmoves=%d, seed=%d, connvia='%s'" + " SET room='%s', ended=%d, turn=%d, nmissing=%d, nmoves=%d, seed=%d, gameid=%d, connvia='%s'" " WHERE rowid=%lld"; XP_UCHAR buf[256]; snprintf( buf, sizeof(buf), fmt, room, gameOver?1:0, turn, nMissing, nMoves, - seed, connvia, cGlobals->selRow ); + seed, gameID, connvia, cGlobals->selRow ); XP_LOGF( "query: %s", buf ); sqlite3_stmt* stmt = NULL; int result = sqlite3_prepare_v2( cGlobals->pDb, buf, -1, &stmt, NULL ); @@ -200,7 +203,7 @@ XP_Bool getGameInfo( sqlite3* pDb, sqlite3_int64 rowid, GameInfo* gib ) { XP_Bool success = XP_FALSE; - const char* fmt = "SELECT room, ended, turn, nmoves, nmissing, seed, connvia " + const char* fmt = "SELECT room, ended, turn, nmoves, nmissing, seed, connvia, gameid " "FROM games WHERE rowid = %lld"; XP_UCHAR query[256]; snprintf( query, sizeof(query), fmt, rowid ); @@ -218,12 +221,38 @@ getGameInfo( sqlite3* pDb, sqlite3_int64 rowid, GameInfo* gib ) gib->nMissing = sqlite3_column_int( ppStmt, 4 ); gib->seed = sqlite3_column_int( ppStmt, 5 ); getColumnText( ppStmt, 6, gib->conn, sizeof(gib->conn) ); + gib->gameID = sqlite3_column_int( ppStmt, 7 ); snprintf( gib->name, sizeof(gib->name), "Game %lld", rowid ); } sqlite3_finalize( ppStmt ); return success; } +void +getRowsForGameID( sqlite3* pDb, XP_U32 gameID, sqlite3_int64* rowids, + int* nRowIDs ) +{ + int maxRowIDs = *nRowIDs; + *nRowIDs = 0; + + char buf[256]; + snprintf( buf, sizeof(buf), "SELECT rowid from games WHERE gameid = %ld LIMIT %d", + gameID, maxRowIDs ); + sqlite3_stmt *ppStmt; + int result = sqlite3_prepare_v2( pDb, buf, -1, &ppStmt, NULL ); + XP_ASSERT( SQLITE_OK == result ); + int ii; + for ( ii = 0; ii < maxRowIDs; ++ii ) { + result = sqlite3_step( ppStmt ); + if ( SQLITE_ROW != result ) { + break; + } + rowids[ii] = sqlite3_column_int64( ppStmt, 0 ); + ++*nRowIDs; + } + sqlite3_finalize( ppStmt ); +} + XP_Bool loadGame( XWStreamCtxt* stream, sqlite3* pDb, sqlite3_int64 rowid ) { diff --git a/xwords4/linux/gamesdb.h b/xwords4/linux/gamesdb.h index ab263ca37..acf8893f1 100644 --- a/xwords4/linux/gamesdb.h +++ b/xwords4/linux/gamesdb.h @@ -31,6 +31,7 @@ typedef struct _GameInfo { XP_UCHAR name[128]; XP_UCHAR room[128]; XP_UCHAR conn[8]; + XP_U32 gameID; XP_S16 nMoves; XP_Bool gameOver; XP_S16 turn; @@ -48,6 +49,8 @@ void summarize( CommonGlobals* cGlobals ); /* Return GSList whose data is (ptrs to) rowids */ GSList* listGames( sqlite3* dbp ); XP_Bool getGameInfo( sqlite3* dbp, sqlite3_int64 rowid, GameInfo* gib ); +void getRowsForGameID( sqlite3* dbp, XP_U32 gameID, sqlite3_int64* rowids, + int* nRowIDs ); XP_Bool loadGame( XWStreamCtxt* stream, sqlite3* pDb, sqlite3_int64 rowid ); void deleteGame( sqlite3* pDb, sqlite3_int64 rowid ); diff --git a/xwords4/linux/gtkaskm.c b/xwords4/linux/gtkaskm.c index a0c449d7f..c251eb8a0 100644 --- a/xwords4/linux/gtkaskm.c +++ b/xwords4/linux/gtkaskm.c @@ -12,7 +12,7 @@ * 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. - * +n * * 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. @@ -53,7 +53,7 @@ gtkaskm( const gchar* message, AskMInfo* infos, int nInfos ) GtkWidget* vbox = gtk_vbox_new( FALSE, 0 ); for ( ii = 0; ii < nInfos; ++ii ) { AskMInfo* info = &infos[ii]; - GtkWidget* row = makeLabeledField( info->label, &fields[ii] ); + GtkWidget* row = makeLabeledField( info->label, &fields[ii], *info->result ); gtk_box_pack_start( GTK_BOX(vbox), row, FALSE, TRUE, 0 ); gtk_widget_show( row ); } diff --git a/xwords4/linux/gtkboard.c b/xwords4/linux/gtkboard.c index 94ae57d96..a4872dfcd 100644 --- a/xwords4/linux/gtkboard.c +++ b/xwords4/linux/gtkboard.c @@ -506,9 +506,9 @@ createOrLoadObjects( GtkGameGlobals* globals ) &cGlobals->cp, &procs, params->gameSeed ); // addr.conType = params->conType; - if ( 0 ) { + switch( addr.conType ) { #ifdef XWFEATURE_RELAY - } else if ( addr.conType == COMMS_CONN_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; */ @@ -517,26 +517,32 @@ createOrLoadObjects( GtkGameGlobals* globals ) /* 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 - } else if ( addr.conType == COMMS_CONN_BT ) { + 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 #ifdef XWFEATURE_IP_DIRECT - } else if ( addr.conType == COMMS_CONN_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 - } else if ( addr.conType == COMMS_CONN_SMS ) { + case COMMS_CONN_SMS: 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 */ @@ -556,7 +562,6 @@ createOrLoadObjects( GtkGameGlobals* globals ) cGlobals->gi->allowHintRect = params->allowHintRect; #endif - if ( params->needsNewGame ) { new_game_impl( globals, XP_FALSE ); #ifndef XWFEATURE_STANDALONE_ONLY @@ -587,6 +592,7 @@ createOrLoadObjects( GtkGameGlobals* globals ) } #endif server_do( globals->cGlobals.game.server ); + saveGame( cGlobals ); /* again, to include address etc. */ disenable_buttons( globals ); } /* createOrLoadObjects */ @@ -1462,24 +1468,24 @@ handle_invite_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals ) switch ( comms_getConType( comms ) ) { #ifdef XWFEATURE_SMS case COMMS_CONN_SMS: { - gchar* phone = NULL; - gchar* portstr = NULL; - AskMInfo infos[] = { - { "Remote phone#", &phone }, - { "Remote port", &portstr }, - }; - if ( gtkaskm( "Invite whom?", infos, VSIZE(infos) ) ) { - int port = atoi( portstr ); - XP_LOGF( "need to invite using number %s and port %d", phone, port ); - XP_ASSERT( 0 != port ); - const CurGameInfo* gi = globals->cGlobals.gi; - gchar gameName[64]; - snprintf( gameName, VSIZE(gameName), "Game %ld", gi->gameID ); - linux_sms2_invite( globals->cGlobals.params, gi, gameName, 1, - phone, port ); - } - g_free( phone ); - g_free( portstr ); + gchar* phone = NULL; + gchar* portstr = NULL; + AskMInfo infos[] = { + { "Remote phone#", &phone }, + { "Remote port", &portstr }, + }; + if ( gtkaskm( "Invite whom?", infos, VSIZE(infos) ) ) { + int port = atoi( portstr ); + XP_LOGF( "need to invite using number %s and port %d", phone, port ); + XP_ASSERT( 0 != port ); + const CurGameInfo* gi = globals->cGlobals.gi; + gchar gameName[64]; + snprintf( gameName, VSIZE(gameName), "Game %ld", gi->gameID ); + linux_sms2_invite( globals->cGlobals.params, gi, gameName, 1, + phone, port ); + } + g_free( phone ); + g_free( portstr ); } break; #endif diff --git a/xwords4/linux/gtkconnsdlg.c b/xwords4/linux/gtkconnsdlg.c index f094d2ba2..52732b793 100644 --- a/xwords4/linux/gtkconnsdlg.c +++ b/xwords4/linux/gtkconnsdlg.c @@ -171,7 +171,7 @@ makeRelayPage( GtkConnsState* state ) gtk_box_pack_start( GTK_BOX(vbox), gtk_label_new( hint ), FALSE, TRUE, 0 ); - GtkWidget* hbox = makeLabeledField( "Room", &state->invite ); + GtkWidget* hbox = makeLabeledField( "Room", &state->invite, NULL ); if ( COMMS_CONN_RELAY == state->addr->conType ) { gtk_entry_set_text( GTK_ENTRY(state->invite), state->addr->u.ip_relay.invite ); @@ -179,7 +179,7 @@ makeRelayPage( GtkConnsState* state ) gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, TRUE, 0 ); gtk_widget_set_sensitive( state->invite, !state->readOnly ); - hbox = makeLabeledField( "Relay address", &state->hostName ); + hbox = makeLabeledField( "Relay address", &state->hostName, NULL ); if ( COMMS_CONN_RELAY == state->addr->conType ) { gtk_entry_set_text( GTK_ENTRY(state->hostName), state->addr->u.ip_relay.hostName ); @@ -187,7 +187,7 @@ makeRelayPage( GtkConnsState* state ) gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, TRUE, 0 ); gtk_widget_set_sensitive( state->hostName, !state->readOnly ); - hbox = makeLabeledField( "Relay port", &state->port ); + hbox = makeLabeledField( "Relay port", &state->port, NULL ); if ( COMMS_CONN_RELAY == state->addr->conType ) { char buf[16]; snprintf( buf, sizeof(buf), "%d", state->addr->u.ip_relay.port ); @@ -206,7 +206,7 @@ makeBTPage( GtkConnsState* state ) { GtkWidget* vbox = gtk_vbox_new( FALSE, 0 ); - GtkWidget* hbox = makeLabeledField( "Host device", &state->bthost ); + GtkWidget* hbox = makeLabeledField( "Host device", &state->bthost, NULL ); if ( COMMS_CONN_BT == state->addr->conType ) { gtk_entry_set_text( GTK_ENTRY(state->bthost), state->addr->u.bt.hostName ); } @@ -227,19 +227,18 @@ makeSMSPage( GtkConnsState* state ) { GtkWidget* vbox = gtk_vbox_new( FALSE, 0 ); - GtkWidget* hbox = makeLabeledField( "Host phone", &state->smsphone ); - if ( COMMS_CONN_SMS == state->addr->conType ) { - gtk_entry_set_text( GTK_ENTRY(state->smsphone), state->addr->u.sms.phone ); - } + const gchar* phone = COMMS_CONN_SMS == state->addr->conType ? + state->addr->u.sms.phone : state->globals->cGlobals.params->connInfo.sms.phone; + GtkWidget* hbox = makeLabeledField( "Host phone", &state->smsphone, phone ); gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, TRUE, 0 ); gtk_widget_set_sensitive( state->smsphone, !state->readOnly ); - hbox = makeLabeledField( "Host port", &state->smsport ); - if ( COMMS_CONN_SMS == state->addr->conType ) { - gchar port[32]; - snprintf( port, sizeof(port), "%d", state->addr->u.sms.port ); - gtk_entry_set_text( GTK_ENTRY(state->smsport), port ); - } + int portVal = COMMS_CONN_SMS == state->addr->conType + ? state->addr->u.sms.port + : state->globals->cGlobals.params->connInfo.sms.port; + gchar port[32]; + snprintf( port, sizeof(port), "%d", portVal ); + hbox = makeLabeledField( "Host port", &state->smsport, port ); gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, TRUE, 0 ); gtk_widget_set_sensitive( state->smsport, !state->readOnly ); diff --git a/xwords4/linux/gtkmain.c b/xwords4/linux/gtkmain.c index 8d192d518..701755312 100644 --- a/xwords4/linux/gtkmain.c +++ b/xwords4/linux/gtkmain.c @@ -75,7 +75,7 @@ findOpenGame( const GtkAppGlobals* apg, sqlite3_int64 rowid ) return result; } -enum { ROW_ITEM, NAME_ITEM, ROOM_ITEM, SEED_ITEM, CONN_ITEM, OVER_ITEM, TURN_ITEM, +enum { ROW_ITEM, NAME_ITEM, ROOM_ITEM, GAMEID_ITEM, SEED_ITEM, CONN_ITEM, OVER_ITEM, TURN_ITEM, NMOVES_ITEM, MISSING_ITEM, N_ITEMS }; static void @@ -136,6 +136,7 @@ init_games_list( GtkAppGlobals* apg ) addTextColumn( list, "Row", ROW_ITEM ); addTextColumn( list, "Name", NAME_ITEM ); addTextColumn( list, "Room", ROOM_ITEM ); + addTextColumn( list, "GameID", GAMEID_ITEM ); addTextColumn( list, "Seed", SEED_ITEM ); addTextColumn( list, "Conn. via", CONN_ITEM ); addTextColumn( list, "Ended", OVER_ITEM ); @@ -147,6 +148,7 @@ init_games_list( GtkAppGlobals* apg ) G_TYPE_INT64, /* ROW_ITEM */ G_TYPE_STRING, /* NAME_ITEM */ G_TYPE_STRING, /* ROOM_ITEM */ + G_TYPE_INT, /* GAMEID_ITEM */ G_TYPE_INT, /* SEED_ITEM */ G_TYPE_STRING, /* CONN_ITEM */ G_TYPE_BOOLEAN, /* OVER_ITEM */ @@ -190,6 +192,7 @@ add_to_list( GtkWidget* list, sqlite3_int64 rowid, XP_Bool isNew, ROW_ITEM, rowid, NAME_ITEM, gib->name, ROOM_ITEM, gib->room, + GAMEID_ITEM, gib->gameID, SEED_ITEM, gib->seed, CONN_ITEM, gib->conn, OVER_ITEM, gib->gameOver, @@ -323,11 +326,8 @@ makeGamesWindow( GtkAppGlobals* apg ) strcat( title, params->dbName ); } #ifdef XWFEATURE_SMS - gchar buf[32]; - if ( db_fetch( params->pDb, KEY_SMSPHONE, buf, VSIZE(buf) ) ) { - int len = strlen( title ); - snprintf( &title[len], VSIZE(title) - len, " (phone: %s)", buf ); - } + int len = strlen( title ); + snprintf( &title[len], VSIZE(title) - len, " (phone: %s)", params->connInfo.sms.phone ); #endif gtk_window_set_title( GTK_WINDOW(window), title ); @@ -390,6 +390,28 @@ onNewData( GtkAppGlobals* apg, sqlite3_int64 rowid, XP_Bool isNew ) } } +static XP_U16 +feedBuffer( GtkAppGlobals* apg, sqlite3_int64 rowid, + const XP_U8* buf, XP_U16 len, const CommsAddrRec* from ) +{ + XP_U16 seed = 0; + GtkGameGlobals* globals = findOpenGame( apg, rowid ); + + if ( !!globals ) { + gameGotBuf( &globals->cGlobals, XP_TRUE, buf, len, from ); + seed = comms_getChannelSeed( globals->cGlobals.game.comms ); + } else { + GtkGameGlobals tmpGlobals; + if ( loadGameNoDraw( &tmpGlobals, apg->params, rowid ) ) { + gameGotBuf( &tmpGlobals.cGlobals, XP_FALSE, buf, len, from ); + seed = comms_getChannelSeed( tmpGlobals.cGlobals.game.comms ); + saveGame( &tmpGlobals.cGlobals ); + } + freeGlobals( &tmpGlobals ); + } + return seed; +} + static gboolean gtk_app_socket_proc( GIOChannel* source, GIOCondition condition, gpointer data ) { @@ -480,20 +502,7 @@ gtkGotBuf( void* closure, const XP_U8* buf, XP_U16 len ) XP_U16 gotSeed; rowidFromToken( ntohl( clientToken ), &rowid, &gotSeed ); - XP_U16 seed = 0; - GtkGameGlobals* globals = findOpenGame( apg, rowid ); - if ( !!globals ) { - gameGotBuf( &globals->cGlobals, XP_TRUE, buf, len ); - seed = comms_getChannelSeed( globals->cGlobals.game.comms ); - } else { - GtkGameGlobals tmpGlobals; - if ( loadGameNoDraw( &tmpGlobals, apg->params, rowid ) ) { - gameGotBuf( &tmpGlobals.cGlobals, XP_FALSE, buf, len ); - seed = comms_getChannelSeed( tmpGlobals.cGlobals.game.comms ); - saveGame( &tmpGlobals.cGlobals ); - } - freeGlobals( &tmpGlobals ); - } + XP_U16 seed = feedBuffer( apg, rowid, buf, len, NULL ); XP_ASSERT( seed == 0 || gotSeed == seed ); XP_USE( seed ); } @@ -521,14 +530,14 @@ gtkNoticeRcvd( void* closure ) } static void -gtkInviteReceived( void* closure, const XP_UCHAR* gameName, XP_U32 gameID, +smsInviteReceived( void* closure, const XP_UCHAR* gameName, XP_U32 gameID, XP_U16 dictLang, const XP_UCHAR* dictName, XP_U16 nPlayers, - XP_U16 nHere ) + XP_U16 nHere, const CommsAddrRec* returnAddr ) { GtkAppGlobals* apg = (GtkAppGlobals*)closure; LaunchParams* params = apg->params; - XP_LOGF( "%s(gameName=%s, dictName=%s, nPlayers=%d, nHere=%d)", __func__, - gameName, dictName, nPlayers, nHere ); + XP_LOGF( "%s(gameName=%s, gameID=%ld, dictName=%s, nPlayers=%d, nHere=%d)", + __func__, gameName, gameID, dictName, nPlayers, nHere ); CurGameInfo gi = {0}; gi_copy( MPPARM(params->mpool) &gi, ¶ms->pgi ); @@ -541,9 +550,7 @@ gtkInviteReceived( void* closure, const XP_UCHAR* gameName, XP_U32 gameID, GtkGameGlobals* globals = malloc( sizeof(*globals) ); params->needsNewGame = XP_FALSE; initGlobals( globals, params, &gi ); - - /* if ( !makeNewGame( globals ) ) { */ - /* } */ + globals->cGlobals.addr = *returnAddr; GtkWidget* gameWindow = globals->window; globals->cGlobals.pDb = apg->params->pDb; @@ -552,6 +559,23 @@ gtkInviteReceived( void* closure, const XP_UCHAR* gameName, XP_U32 gameID, gtk_widget_show( gameWindow ); } +static void +smsMsgReceived( void* closure, XP_U32 gameID, const XP_U8* buf, XP_U16 len, + const CommsAddrRec* from ) +{ + LOG_FUNC(); + GtkAppGlobals* apg = (GtkAppGlobals*)closure; + LaunchParams* params = apg->params; + + sqlite3_int64 rowids[4]; + int nRowIDs = VSIZE(rowids); + getRowsForGameID( params->pDb, gameID, rowids, &nRowIDs ); + int ii; + for ( ii = 0; ii < nRowIDs; ++ii ) { + feedBuffer( apg, rowids[ii], buf, len, from ); + } +} + static gboolean keepalive_timer( gpointer data ) { @@ -660,12 +684,13 @@ gtkmain( LaunchParams* params ) if ( !!phone ) { db_store( params->pDb, KEY_SMSPHONE, phone ); } else if ( !phone && db_fetch( params->pDb, KEY_SMSPHONE, buf, VSIZE(buf) ) ) { - phone = buf; + params->connInfo.sms.phone = phone = buf; } if ( !!phone ) { SMSProcs smsProcs = { .socketChanged = gtkSocketChanged, - .inviteReceived = gtkInviteReceived, + .inviteReceived = smsInviteReceived, + .msgReceived = smsMsgReceived, }; linux_sms2_init( params, phone, &smsProcs, &apg ); } else { diff --git a/xwords4/linux/gtkutils.c b/xwords4/linux/gtkutils.c index 43dc0db71..0ed24d55e 100644 --- a/xwords4/linux/gtkutils.c +++ b/xwords4/linux/gtkutils.c @@ -35,13 +35,17 @@ makeButton( char* text, GCallback func, gpointer data ) GtkWidget* -makeLabeledField( const char* labelText, GtkWidget** field ) +makeLabeledField( const char* labelText, GtkWidget** field, + const gchar* initialVal ) { char buf[64]; snprintf( buf, sizeof(buf), "%s:", labelText ); GtkWidget* label = gtk_label_new( buf ); *field = gtk_entry_new(); + if ( !!initialVal ) { + gtk_entry_set_text( GTK_ENTRY(*field), initialVal ); + } GtkWidget* hbox = gtk_hbox_new( FALSE, 0 ); gtk_box_pack_start( GTK_BOX(hbox), label, FALSE, TRUE, 0 ); diff --git a/xwords4/linux/gtkutils.h b/xwords4/linux/gtkutils.h index e80f8c3b5..07950bb1d 100644 --- a/xwords4/linux/gtkutils.h +++ b/xwords4/linux/gtkutils.h @@ -27,7 +27,8 @@ #include "gtkboard.h" GtkWidget* makeButton( char* text, GCallback func, gpointer data ); -GtkWidget* makeLabeledField( const char* labelText, GtkWidget** field ); +GtkWidget* makeLabeledField( const char* labelText, GtkWidget** field, + const gchar* initialVal ); #endif /* _GTKUTILS_H_ */ #endif /* PLATFORM_GTK */ diff --git a/xwords4/linux/linuxmain.c b/xwords4/linux/linuxmain.c index 1de66e00c..b699224e2 100644 --- a/xwords4/linux/linuxmain.c +++ b/xwords4/linux/linuxmain.c @@ -174,14 +174,14 @@ makeDictForStream( CommonGlobals* cGlobals, XWStreamCtxt* stream ) void gameGotBuf( CommonGlobals* cGlobals, XP_Bool hasDraw, const XP_U8* buf, - XP_U16 len ) + XP_U16 len, const CommsAddrRec* from ) { XP_LOGF( "%s(hasDraw=%d)", __func__, hasDraw ); XP_Bool redraw = XP_FALSE; XWGame* game = &cGlobals->game; XWStreamCtxt* stream = stream_from_msgbuf( cGlobals, buf, len ); if ( !!stream ) { - if ( comms_checkIncomingStream( game->comms, stream, NULL ) ) { + if ( comms_checkIncomingStream( game->comms, stream, from ) ) { redraw = server_receiveMessage( game->server, stream ); if ( redraw ) { saveGame( cGlobals ); @@ -1134,7 +1134,7 @@ linux_reset( void* closure ) XP_S16 linux_send( const XP_U8* buf, XP_U16 buflen, const CommsAddrRec* addrRec, - XP_U32 XP_UNUSED(gameID), void* closure ) + XP_U32 gameID, void* closure ) { XP_S16 nSent = -1; CommonGlobals* cGlobals = (CommonGlobals*)closure; @@ -1180,7 +1180,8 @@ linux_send( const XP_U8* buf, XP_U16 buflen, const CommsAddrRec* addrRec, addrRec = &addr; } nSent = linux_sms2_send( cGlobals->params, buf, buflen, - addrRec->u.sms.phone, addrRec->u.sms.port ); + addrRec->u.sms.phone, addrRec->u.sms.port, + gameID ); #endif } else { XP_ASSERT(0); @@ -1406,12 +1407,14 @@ linux_util_addrChange( XW_UtilCtxt* uc, #endif #ifdef XWFEATURE_SMS case COMMS_CONN_SMS: - XP_ASSERT(0); + /* nothing to do??? */ + // XP_ASSERT(0); // linux_sms_init( cGlobals, newAddr ); break; #endif default: - XP_ASSERT(0); + // XP_ASSERT(0); + break; } } diff --git a/xwords4/linux/linuxmain.h b/xwords4/linux/linuxmain.h index 27c64f89a..916bf5a23 100644 --- a/xwords4/linux/linuxmain.h +++ b/xwords4/linux/linuxmain.h @@ -103,7 +103,7 @@ void linuxChangeRoles( CommonGlobals* cGlobals ); void sendRelayReg( LaunchParams* params, sqlite3* pDb ); void gameGotBuf( CommonGlobals* globals, XP_Bool haveDraw, - const XP_U8* buf, XP_U16 len ); + const XP_U8* buf, XP_U16 len, const CommsAddrRec* from ); gboolean app_socket_proc( GIOChannel* source, GIOCondition condition, gpointer data ); const XP_UCHAR* linux_getDevID( LaunchParams* params, DevIDType* typ ); diff --git a/xwords4/linux/linuxsms.c b/xwords4/linux/linuxsms.c index 9d6b9911c..561b5788c 100644 --- a/xwords4/linux/linuxsms.c +++ b/xwords4/linux/linuxsms.c @@ -59,214 +59,6 @@ makeQueuePath( const XP_UCHAR* phone, XP_U16 port, snprintf( path, pathlen, "%s/%s_%d", SMS_DIR, phone, port ); } - -#if 0 -struct LinSMSData { - XP_UCHAR myPhone[MAX_PHONE_LEN+1]; - XP_UCHAR myQueue[256]; - FILE* lock; - int wd, fd; /* for inotify */ - void* storage; - XP_U16 count; - XP_U16 port; - XP_Bool amServer; -}; - -static void -lock_queue( LinSMSData* data ) -{ - char lock[256]; - snprintf( lock, sizeof(lock), "%s/%s", data->myQueue, LOCK_FILE ); - FILE* fp = fopen( lock, "w" ); - XP_ASSERT( NULL == data->lock ); - data->lock = fp; -} - -static void -unlock_queue( LinSMSData* data ) -{ - XP_ASSERT( NULL != data->lock ); - fclose( data->lock ); - data->lock = NULL; -} - -void -linux_sms_init( CommonGlobals* globals, const CommsAddrRec* addr ) -{ - LinSMSData* data = globals->smsData; - if ( !data ) { - data = XP_MALLOC( globals->util->mpool, sizeof(*data) ); - XP_ASSERT( !!data ); - XP_MEMSET( data, 0, sizeof(*data) ); - globals->smsData = data; - - data->amServer = comms_getIsServer( globals->game.comms ); - - if ( data->amServer ) { - XP_STRNCPY( data->myPhone, addr->u.sms.phone, - sizeof(data->myPhone) ); - } else { - snprintf( data->myPhone, sizeof(data->myPhone), "%.6d", getpid() ); - } - - makeQueuePath( data->myPhone, addr->u.sms.port, - data->myQueue, sizeof(data->myQueue) ); - data->port = addr->u.sms.port; - - (void)g_mkdir_with_parents( data->myQueue, 0777 ); - - int fd = inotify_init(); - data->fd = fd; - data->wd = inotify_add_watch( fd, data->myQueue, IN_MODIFY ); - (*globals->socketChanged)( globals, -1, fd, &data->storage ); - } -} /* linux_sms_init */ - -void -linux_sms_close( CommonGlobals* globals ) -{ - LinSMSData* data = globals->smsData; - if ( !!data ) { - XP_FREE( globals->util->mpool, data ); - globals->smsData = NULL; - } -} /* linux_sms_close */ - -XP_S16 -linux_sms_send( CommonGlobals* globals, - const XP_U8* buf, XP_U16 buflen, - const XP_UCHAR* phone, XP_U16 port ) -{ - XP_S16 nSent = -1; - LinSMSData* data = globals->smsData; - XP_ASSERT( !!data ); - char path[256]; - - lock_queue( data ); - -#ifdef DEBUG - gchar* str64 = g_base64_encode( buf, buflen ); -#endif - - makeQueuePath( phone, port, path, sizeof(path) ); - g_mkdir_with_parents( path, 0777 ); /* just in case */ - int len = strlen( path ); - snprintf( &path[len], sizeof(path)-len, "/%d", ++data->count ); - - XP_UCHAR sms[buflen*2]; /* more like (buflen*4/3) */ - XP_U16 smslen = sizeof(sms); - binToSms( sms, &smslen, buf, buflen ); - XP_ASSERT( smslen == strlen(sms) ); - -#ifdef DEBUG - XP_ASSERT( !strcmp( str64, sms ) ); - g_free( str64 ); - - XP_U8 testout[buflen]; - XP_U16 lenout = sizeof( testout ); - XP_ASSERT( smsToBin( testout, &lenout, sms, smslen ) ); - XP_ASSERT( lenout == buflen ); - XP_ASSERT( XP_MEMCMP( testout, buf, smslen ) ); -#endif - - FILE* fp = fopen( path, "w" ); - XP_ASSERT( !!fp ); - (void)fprintf( fp, "from: %s\n", data->myPhone ); - (void)fprintf( fp, "%s\n", sms ); - fclose( fp ); - sync(); - - unlock_queue( data ); - - nSent = buflen; - - return nSent; -} /* linux_sms_send */ - -static XP_S16 -decodeAndDelete( LinSMSData* data, const gchar* name, - XP_U8* buf, XP_U16 buflen, CommsAddrRec* addr ) -{ - XP_S16 nRead = -1; - char path[256]; - snprintf( path, sizeof(path), "%s/%s", data->myQueue, name ); - - gchar* contents; - gsize length; -#ifdef DEBUG - gboolean success = -#endif - g_file_get_contents( path, &contents, &length, NULL ); - XP_ASSERT( success ); - unlink( path ); - - if ( 0 == strncmp( "from: ", contents, 6 ) ) { - gchar* eol = strstr( contents, "\n" ); - *eol = '\0'; - XP_STRNCPY( addr->u.sms.phone, &contents[6], sizeof(addr->u.sms.phone) ); - XP_ASSERT( !*eol ); - ++eol; /* skip NULL */ - *strstr(eol, "\n" ) = '\0'; - - XP_U16 inlen = strlen(eol); /* skip \n */ - XP_U8 out[inlen]; - XP_U16 outlen = sizeof(out); - XP_Bool valid = smsToBin( out, &outlen, eol, inlen ); - - if ( valid && outlen <= buflen ) { - XP_MEMCPY( buf, out, outlen ); - nRead = outlen; - addr->conType = COMMS_CONN_SMS; - addr->u.sms.port = data->port; - } - } - - g_free( contents ); - - return nRead; -} /* decodeAndDelete */ - -XP_S16 -linux_sms_receive( CommonGlobals* globals, int sock, - XP_U8* buf, XP_U16 buflen, CommsAddrRec* addr ) -{ - XP_S16 nRead = -1; - LinSMSData* data = globals->smsData; - - XP_ASSERT( sock == data->fd ); - - lock_queue( data ); - - /* read required or we'll just get the event again. But we don't care - about the result or the buffer contents. */ - XP_U8 buffer[sizeof(struct inotify_event) + 16]; - if ( 0 > read( sock, buffer, sizeof(buffer) ) ) { - } - char shortest[256] = { '\0' }; - GDir* dir = g_dir_open( data->myQueue, 0, NULL ); - for ( ; ; ) { - const gchar* name = g_dir_read_name( dir ); - if ( NULL == name ) { - break; - } else if ( 0 == strcmp( name, LOCK_FILE ) ) { - continue; - } - if ( !shortest[0] || 0 < strcmp( shortest, name ) ) { - snprintf( shortest, sizeof(shortest), "%s", name ); - } - } - g_dir_close( dir ); - - if ( !!shortest[0] ) { - nRead = decodeAndDelete( data, shortest, buf, buflen, addr ); - } - - unlock_queue( data ); - - return nRead; -} /* linux_sms_receive */ -#endif - typedef struct _LinSMS2Data { int fd, wd; XP_UCHAR myQueue[256]; @@ -284,45 +76,50 @@ typedef enum { NONE, INVITE, DATA, DEATH, ACK, } SMS_CMD; static LinSMS2Data* getStorage( LaunchParams* params ); +static void writeHeader( XWStreamCtxt* stream, SMS_CMD cmd ); + static void -lock_queue2( LinSMS2Data* data ) +lock_queue2( LinSMS2Data* storage ) { char lock[256]; - snprintf( lock, sizeof(lock), "%s/%s", data->myQueue, LOCK_FILE ); + snprintf( lock, sizeof(lock), "%s/%s", storage->myQueue, LOCK_FILE ); FILE* fp = fopen( lock, "w" ); - XP_ASSERT( NULL == data->lock ); - data->lock = fp; + XP_ASSERT( NULL == storage->lock ); + storage->lock = fp; } static void -unlock_queue2( LinSMS2Data* data ) +unlock_queue2( LinSMS2Data* storage ) { - XP_ASSERT( NULL != data->lock ); - fclose( data->lock ); - data->lock = NULL; + XP_ASSERT( NULL != storage->lock ); + fclose( storage->lock ); + storage->lock = NULL; } -XP_S16 -linux_sms_send2( LaunchParams* params, const XP_U8* buf, XP_U16 buflen, - const XP_UCHAR* phone, XP_U16 port ) +static XP_S16 +send_sms( LinSMS2Data* storage, XWStreamCtxt* stream, + const XP_UCHAR* phone, XP_U16 port ) { + const XP_U8* buf = stream_getPtr( stream ); + XP_U16 buflen = stream_getSize( stream ); + XP_S16 nSent = -1; - LinSMS2Data* data = getStorage( params ); - XP_ASSERT( !!data ); + XP_ASSERT( !!storage ); char path[256]; - lock_queue2( data ); + lock_queue2( storage ); #ifdef DEBUG gchar* str64 = g_base64_encode( buf, buflen ); #endif makeQueuePath( phone, port, path, sizeof(path) ); + XP_LOGF( "%s: writing to %s", __func__, path ); g_mkdir_with_parents( path, 0777 ); /* just in case */ int len = strlen( path ); - snprintf( &path[len], sizeof(path)-len, "/%d", ++data->count ); + snprintf( &path[len], sizeof(path)-len, "/%d", ++storage->count ); XP_UCHAR sms[buflen*2]; /* more like (buflen*4/3) */ XP_U16 smslen = sizeof(sms); @@ -342,12 +139,12 @@ linux_sms_send2( LaunchParams* params, const XP_U8* buf, XP_U16 buflen, FILE* fp = fopen( path, "w" ); XP_ASSERT( !!fp ); - (void)fprintf( fp, "from: %s\n", data->myPhone ); + (void)fprintf( fp, "from: %s\n", storage->myPhone ); (void)fprintf( fp, "%s\n", sms ); fclose( fp ); sync(); - unlock_queue2( data ); + unlock_queue2( storage ); nSent = buflen; @@ -355,13 +152,13 @@ linux_sms_send2( LaunchParams* params, const XP_U8* buf, XP_U16 buflen, } /* linux_sms_send */ static XP_S16 -decodeAndDelete2( LinSMS2Data* data, const gchar* name, - XP_U8* buf, XP_U16 buflen ) +decodeAndDelete2( LinSMS2Data* storage, const gchar* name, + XP_U8* buf, XP_U16 buflen, CommsAddrRec* addr ) { LOG_FUNC(); XP_S16 nRead = -1; char path[256]; - snprintf( path, sizeof(path), "%s/%s", data->myQueue, name ); + snprintf( path, sizeof(path), "%s/%s", storage->myQueue, name ); gchar* contents; gsize length; @@ -389,8 +186,10 @@ decodeAndDelete2( LinSMS2Data* data, const gchar* name, if ( valid && outlen <= buflen ) { XP_MEMCPY( buf, out, outlen ); nRead = outlen; - /* addr->conType = COMMS_CONN_SMS; */ - /* addr->u.sms.port = data->port; */ + addr->conType = COMMS_CONN_SMS; + XP_STRNCPY( addr->u.sms.phone, phone, sizeof(addr->u.sms.phone) ); + XP_LOGF( "%s: message came from phone: %s", __func__, phone ); + addr->u.sms.port = 1; /* for now */ } } @@ -401,26 +200,43 @@ decodeAndDelete2( LinSMS2Data* data, const gchar* name, } /* decodeAndDelete2 */ static void -dispatch_invite( LinSMS2Data* data, XP_U16 XP_UNUSED(proto), XWStreamCtxt* stream ) +dispatch_invite( LinSMS2Data* storage, XP_U16 XP_UNUSED(proto), + XWStreamCtxt* stream, const CommsAddrRec* addr ) { XP_UCHAR gameName[256]; XP_UCHAR dictName[256]; XP_U32 gameID = stream_getU32( stream ); + XP_LOGF( "%s: got gameID %ld", __func__, gameID ); stringFromStreamHere( stream, gameName, VSIZE(gameName) ); XP_U32 dictLang = stream_getU32( stream ); stringFromStreamHere( stream, dictName, VSIZE(dictName) ); XP_U8 nMissing = stream_getU8( stream ); XP_U8 nPlayers = stream_getU8( stream ); - (*data->procs->inviteReceived)( data->procClosure, gameName, gameID, - dictLang, dictName, nPlayers, nMissing ); + (*storage->procs->inviteReceived)( storage->procClosure, gameName, gameID, + dictLang, dictName, nPlayers, nMissing, + addr ); } static void -parseAndDispatch( LaunchParams* params, uint8_t* buf, int len ) +dispatch_data( LinSMS2Data* storage, XP_U16 XP_UNUSED(proto), + XWStreamCtxt* stream, const CommsAddrRec* addr ) { - LinSMS2Data* data = getStorage( params ); + XP_USE( addr ); + XP_U32 gameID = stream_getU32( stream ); + const XP_U8* data = stream_getPtr( stream ); + XP_U16 len = stream_getSize( stream ); + + (*storage->procs->msgReceived)( storage->procClosure, gameID, + data, len, addr ); +} + +static void +parseAndDispatch( LaunchParams* params, uint8_t* buf, int len, + const CommsAddrRec* addr ) +{ + LinSMS2Data* storage = getStorage( params ); XWStreamCtxt* stream = mem_stream_make( MPPARM(params->mpool) params->vtMgr, NULL, CHANNEL_NONE, NULL ); @@ -431,9 +247,11 @@ parseAndDispatch( LaunchParams* params, uint8_t* buf, int len ) XP_U8 cmd = stream_getU8( stream ); switch( cmd ) { case INVITE: - dispatch_invite( data, proto, stream ); + dispatch_invite( storage, proto, stream, addr ); break; case DATA: + dispatch_data( storage, proto, stream, addr ); + break; case DEATH: case ACK: break; @@ -450,13 +268,13 @@ sms2_receive( void* closure, int socket ) LOG_FUNC(); LaunchParams* params = (LaunchParams*)closure; XP_ASSERT( !!params->sms2Storage ); - LinSMS2Data* data = getStorage( params ); + LinSMS2Data* storage = getStorage( params ); XP_S16 nRead = -1; - XP_ASSERT( socket == data->fd ); + XP_ASSERT( socket == storage->fd ); - lock_queue2( data ); + lock_queue2( storage ); /* read required or we'll just get the event again. But we don't care about the result or the buffer contents. */ @@ -464,7 +282,8 @@ sms2_receive( void* closure, int socket ) if ( 0 > read( socket, buffer, sizeof(buffer) ) ) { } char shortest[256] = { '\0' }; - GDir* dir = g_dir_open( data->myQueue, 0, NULL ); + GDir* dir = g_dir_open( storage->myQueue, 0, NULL ); + XP_LOGF( "%s: opening %s", __func__, storage->myQueue ); for ( ; ; ) { const gchar* name = g_dir_read_name( dir ); if ( NULL == name ) { @@ -479,52 +298,53 @@ sms2_receive( void* closure, int socket ) g_dir_close( dir ); uint8_t buf[256]; + CommsAddrRec fromAddr = {0}; if ( !!shortest[0] ) { - nRead = decodeAndDelete2( data, shortest, buf, sizeof(buf) ); + nRead = decodeAndDelete2( storage, shortest, buf, sizeof(buf), &fromAddr ); } - unlock_queue2( data ); + unlock_queue2( storage ); if ( 0 < nRead ) { - parseAndDispatch( params, buf, nRead ); + parseAndDispatch( params, buf, nRead, &fromAddr ); } -} +} /* sms2_receive */ void linux_sms2_init( LaunchParams* params, const gchar* phone, const SMSProcs* procs, void* procClosure ) { XP_ASSERT( !!phone ); - LinSMS2Data* data = getStorage( params ); - XP_ASSERT( !!data ); - data->myPhone = phone; - data->procs = procs; - data->procClosure = procClosure; + LinSMS2Data* storage = getStorage( params ); + XP_ASSERT( !!storage ); + storage->myPhone = phone; + storage->procs = procs; + storage->procClosure = procClosure; makeQueuePath( phone, params->connInfo.sms.port, - data->myQueue, sizeof(data->myQueue) ); - data->port = params->connInfo.sms.port; + storage->myQueue, sizeof(storage->myQueue) ); + XP_LOGF( "%s: my queue: %s", __func__, storage->myQueue ); + storage->port = params->connInfo.sms.port; - (void)g_mkdir_with_parents( data->myQueue, 0777 ); + (void)g_mkdir_with_parents( storage->myQueue, 0777 ); int fd = inotify_init(); - data->fd = fd; - data->wd = inotify_add_watch( fd, data->myQueue, IN_MODIFY ); + storage->fd = fd; + storage->wd = inotify_add_watch( fd, storage->myQueue, IN_MODIFY ); (*procs->socketChanged)( procClosure, fd, -1, sms2_receive, params ); } /* linux_sms2_init */ void linux_sms2_invite( LaunchParams* params, const CurGameInfo* gi, - const gchar* gameName, XP_U16 nMissing, const gchar* phone, + const gchar* gameName, XP_U16 nMissing, const gchar* phone, int port ) { LOG_FUNC(); XWStreamCtxt* stream; stream = mem_stream_make( MPPARM(params->mpool) params->vtMgr, NULL, CHANNEL_NONE, NULL ); - stream_putU8( stream, SMS_PROTO_VERSION ); - stream_putU8( stream, INVITE ); + writeHeader( stream, INVITE ); stream_putU32( stream, gi->gameID ); stringToStream( stream, gameName ); stream_putU32( stream, gi->dictLang ); @@ -532,21 +352,29 @@ linux_sms2_invite( LaunchParams* params, const CurGameInfo* gi, stream_putU8( stream, nMissing ); stream_putU8( stream, gi->nPlayers ); - linux_sms_send2( params, stream_getPtr( stream ), - stream_getSize( stream ), phone, port ); + LinSMS2Data* storage = getStorage( params ); + send_sms( storage, stream, phone, port ); stream_destroy( stream ); } XP_S16 linux_sms2_send( LaunchParams* params, const XP_U8* buf, - XP_U16 buflen, const XP_UCHAR* phone, XP_U16 port ) + XP_U16 buflen, const XP_UCHAR* phone, XP_U16 port, + XP_U32 gameID ) { - XP_ASSERT(0); - XP_USE( params ); - XP_USE( buf ); - XP_USE( phone ); - XP_USE( port ); + LOG_FUNC(); + XWStreamCtxt* stream = mem_stream_make( MPPARM(params->mpool) params->vtMgr, + NULL, CHANNEL_NONE, NULL ); + writeHeader( stream, DATA ); + stream_putU32( stream, gameID ); + stream_putBytes( stream, buf, buflen ); + + LinSMS2Data* storage = getStorage( params ); + if ( 0 >= send_sms( storage, stream, phone, port ) ) { + buflen = -1; + } + return buflen; } @@ -567,4 +395,11 @@ getStorage( LaunchParams* params ) return storage; } +static void +writeHeader( XWStreamCtxt* stream, SMS_CMD cmd ) +{ + stream_putU8( stream, SMS_PROTO_VERSION ); + stream_putU8( stream, cmd ); +} + #endif /* XWFEATURE_SMS */ diff --git a/xwords4/linux/linuxsms.h b/xwords4/linux/linuxsms.h index 2c6979702..c7de7dc8a 100644 --- a/xwords4/linux/linuxsms.h +++ b/xwords4/linux/linuxsms.h @@ -36,8 +36,9 @@ typedef struct _SMSProcs { void (*inviteReceived)( void* closure, const XP_UCHAR* gameName, XP_U32 gameID, XP_U16 dictLang, const XP_UCHAR* dictName, XP_U16 nPlayers, - XP_U16 nHere ); - void (*msgReceived)( void* closure, const XP_U8* buf, XP_U16 len ); + XP_U16 nHere, const CommsAddrRec* returnAddr ); + void (*msgReceived)( void* closure, XP_U32 gameID, const XP_U8* buf, + XP_U16 len, const CommsAddrRec* from ); void (*msgNoticeReceived)( void* closure ); void (*devIDReceived)( void* closure, const XP_UCHAR* devID, XP_U16 maxInterval ); @@ -51,7 +52,8 @@ typedef struct _SMSProcs { void linux_sms2_init( LaunchParams* params, const gchar* phone, const SMSProcs* procs, void* procClosure ); XP_S16 linux_sms2_send( LaunchParams* params, const XP_U8* buf, - XP_U16 buflen, const XP_UCHAR* phone, XP_U16 port ); + XP_U16 buflen, const XP_UCHAR* phone, XP_U16 port, + XP_U32 gameID ); void linux_sms2_invite( LaunchParams* params, const CurGameInfo* info, const gchar* gameName, XP_U16 nMissing, const gchar* phone, int port );