diff --git a/xwords4/linux/gamesdb.c b/xwords4/linux/gamesdb.c index 6a48025b6..e3e616cd6 100644 --- a/xwords4/linux/gamesdb.c +++ b/xwords4/linux/gamesdb.c @@ -42,6 +42,7 @@ openGamesDB( const char* dbName ) "CREATE TABLE games ( " "rowid INTEGER PRIMARY KEY AUTOINCREMENT" ",game BLOB" + ",snap BLOB" ",inviteInfo BLOB" ",room VARCHAR(32)" ",connvia VARCHAR(32)" @@ -73,12 +74,11 @@ closeGamesDB( sqlite3* pDb ) } static sqlite3_int64 -writeBlobColumn( XWStreamCtxt* stream, sqlite3* pDb, sqlite3_int64 curRow, - const char* column ) +writeBlobColumnData( const XP_U8* data, gsize len, XP_U16 strVersion, sqlite3* pDb, + sqlite3_int64 curRow, const char* column ) { + XP_LOGF( "%s(col=%s)", __func__, column ); int result; - /* size includes stream version as header */ - XP_U16 len = stream_getSize( stream ); char buf[256]; char* query; @@ -114,12 +114,10 @@ writeBlobColumn( XWStreamCtxt* stream, sqlite3* pDb, sqlite3_int64 curRow, result = sqlite3_blob_open( pDb, "main", "games", column, curRow, 1 /*flags: writeable*/, &blob ); assertPrintResult( pDb, result, SQLITE_OK ); - XP_U16 strVersion = stream_getVersion( stream ); XP_ASSERT( strVersion <= CUR_STREAM_VERS ); - result = sqlite3_blob_write( blob, &strVersion, sizeof(strVersion), 0 ); + result = sqlite3_blob_write( blob, &strVersion, sizeof(strVersion), 0/*offset*/ ); assertPrintResult( pDb, result, SQLITE_OK ); - const XP_U8* ptr = stream_getPtr( stream ); - result = sqlite3_blob_write( blob, ptr, len, sizeof(strVersion) ); + result = sqlite3_blob_write( blob, data, len, sizeof(strVersion) /* offset */ ); assertPrintResult( pDb, result, SQLITE_OK ); result = sqlite3_blob_close( blob ); assertPrintResult( pDb, result, SQLITE_OK ); @@ -127,13 +125,24 @@ writeBlobColumn( XWStreamCtxt* stream, sqlite3* pDb, sqlite3_int64 curRow, sqlite3_finalize( stmt ); } + LOG_RETURNF( "%lld", curRow ); return curRow; } +static sqlite3_int64 +writeBlobColumnStream( XWStreamCtxt* stream, sqlite3* pDb, sqlite3_int64 curRow, + const char* column ) +{ + XP_U16 strVersion = stream_getVersion( stream ); + const XP_U8* data = stream_getPtr( stream ); + gsize len = stream_getSize( stream ); + return writeBlobColumnData( data, len, strVersion, pDb, curRow, column ); +} + sqlite3_int64 writeNewGameToDB( XWStreamCtxt* stream, sqlite3* pDb ) { - sqlite3_int64 newRow = writeBlobColumn( stream, pDb, -1, "game" ); + sqlite3_int64 newRow = writeBlobColumnStream( stream, pDb, -1, "game" ); return newRow; } @@ -145,7 +154,7 @@ writeToDB( XWStreamCtxt* stream, void* closure ) sqlite3* pDb = cGlobals->pDb; XP_Bool newGame = -1 == selRow; - selRow = writeBlobColumn( stream, pDb, selRow, "game" ); + selRow = writeBlobColumnStream( stream, pDb, selRow, "game" ); if ( newGame ) { /* new row; need to insert blob first */ cGlobals->selRow = selRow; @@ -154,6 +163,29 @@ writeToDB( XWStreamCtxt* stream, void* closure ) (*cGlobals->onSave)( cGlobals->onSaveClosure, selRow, newGame ); } +static void +addSnap( sqlite3* pDb, sqlite3_int64 curRow ) +{ + LOG_FUNC(); + + char* filename = "./red.png"; + GError *error = NULL; + GdkPixbuf* snap = gdk_pixbuf_new_from_file_at_size( filename, 80, 80, &error ); + XP_ASSERT( !!snap ); + + gchar* buffer; + gsize buffer_size; + gboolean worked = gdk_pixbuf_save_to_buffer( snap, &buffer, &buffer_size, + "png", &error, NULL ); + XP_ASSERT( worked ); + sqlite3_int64 newRow = writeBlobColumnData( (const XP_U8*)buffer, buffer_size, + CUR_STREAM_VERS, pDb, curRow, "snap" ); + XP_ASSERT( curRow == newRow ); + + g_free( buffer ); + g_object_unref( snap ); +} + void summarize( CommonGlobals* cGlobals ) { @@ -222,6 +254,8 @@ summarize( CommonGlobals* cGlobals ) } sqlite3_finalize( stmt ); XP_USE( result ); + + addSnap( cGlobals->pDb, cGlobals->selRow ); } GSList* @@ -262,7 +296,7 @@ getGameInfo( sqlite3* pDb, sqlite3_int64 rowid, GameInfo* gib ) { XP_Bool success = XP_FALSE; const char* fmt = "SELECT room, ended, turn, nmoves, ntotal, nmissing, " - "seed, connvia, gameid, lastMoveTime " + "seed, connvia, gameid, lastMoveTime, snap " "FROM games WHERE rowid = %lld"; XP_UCHAR query[256]; snprintf( query, sizeof(query), fmt, rowid ); @@ -284,8 +318,27 @@ getGameInfo( sqlite3* pDb, sqlite3_int64 rowid, GameInfo* gib ) gib->gameID = sqlite3_column_int( ppStmt, 8 ); gib->lastMoveTime = sqlite3_column_int( ppStmt, 9 ); snprintf( gib->name, sizeof(gib->name), "Game %lld", rowid ); + + /* Load the snapshot */ + const XP_U8* ptr = sqlite3_column_blob( ppStmt, 10 ); + int size = sqlite3_column_bytes( ppStmt, 10 ); + /* Skip the version that's written in */ + ptr += sizeof(XP_U16); size -= sizeof(XP_U16); + GInputStream* istr = g_memory_input_stream_new_from_data( ptr, size, NULL ); + GError *error = NULL; + GdkPixbuf* snap = gdk_pixbuf_new_from_stream( istr, NULL, &error ); + g_object_unref( istr ); + + if ( !snap ) { + XP_LOGF( "%s(): error from gdk_pixbuf_new_from_stream(): %s", + __func__, error->message ); + g_error_free( error ); + } + XP_ASSERT( !!snap ); + gib->snap = snap; } sqlite3_finalize( ppStmt ); + return success; } @@ -354,7 +407,7 @@ loadGame( XWStreamCtxt* stream, sqlite3* pDb, sqlite3_int64 rowid ) void saveInviteAddrs( XWStreamCtxt* stream, sqlite3* pDb, sqlite3_int64 rowid ) { - sqlite3_int64 row = writeBlobColumn( stream, pDb, rowid, "inviteInfo" ); + sqlite3_int64 row = writeBlobColumnStream( stream, pDb, rowid, "inviteInfo" ); assert( row == rowid ); } diff --git a/xwords4/linux/gamesdb.h b/xwords4/linux/gamesdb.h index 64417357d..a821b1355 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[128]; + GdkPixbuf* snap; XP_U32 gameID; XP_S16 nMoves; XP_Bool gameOver; diff --git a/xwords4/linux/gtkmain.c b/xwords4/linux/gtkmain.c index 104a7c5ac..4c39b91be 100644 --- a/xwords4/linux/gtkmain.c +++ b/xwords4/linux/gtkmain.c @@ -75,8 +75,9 @@ findOpenGame( const GtkAppGlobals* apg, sqlite3_int64 rowid ) return result; } -enum { ROW_ITEM, NAME_ITEM, ROOM_ITEM, GAMEID_ITEM, SEED_ITEM, CONN_ITEM, OVER_ITEM, TURN_ITEM, - NMOVES_ITEM, NTOTAL_ITEM, MISSING_ITEM, LASTTURN_ITEM, N_ITEMS }; +enum { ROW_ITEM, ROW_THUMB, NAME_ITEM, ROOM_ITEM, GAMEID_ITEM, SEED_ITEM, + CONN_ITEM, OVER_ITEM, TURN_ITEM, NMOVES_ITEM, NTOTAL_ITEM, MISSING_ITEM, + LASTTURN_ITEM, N_ITEMS }; static void foreachProc( GtkTreeModel* model, GtkTreePath* XP_UNUSED(path), @@ -143,12 +144,23 @@ addTextColumn( GtkWidget* list, const gchar* title, int item ) gtk_tree_view_append_column( GTK_TREE_VIEW(list), column ); } +static void +addImageColumn( GtkWidget* list, const gchar* title, int item ) +{ + GtkCellRenderer* renderer = gtk_cell_renderer_pixbuf_new(); + GtkTreeViewColumn* column = + gtk_tree_view_column_new_with_attributes( title, renderer, + "pixbuf", item, NULL ); + gtk_tree_view_append_column( GTK_TREE_VIEW(list), column ); +} + static GtkWidget* init_games_list( GtkAppGlobals* apg ) { GtkWidget* list = gtk_tree_view_new(); addTextColumn( list, "Row", ROW_ITEM ); + addImageColumn( list, "Snap", ROW_THUMB ); addTextColumn( list, "Name", NAME_ITEM ); addTextColumn( list, "Room", ROOM_ITEM ); addTextColumn( list, "GameID", GAMEID_ITEM ); @@ -163,6 +175,7 @@ init_games_list( GtkAppGlobals* apg ) GtkListStore* store = gtk_list_store_new( N_ITEMS, G_TYPE_INT64, /* ROW_ITEM */ + GDK_TYPE_PIXBUF,/* ROW_THUMB */ G_TYPE_STRING, /* NAME_ITEM */ G_TYPE_STRING, /* ROOM_ITEM */ G_TYPE_INT, /* GAMEID_ITEM */ @@ -212,6 +225,7 @@ add_to_list( GtkWidget* list, sqlite3_int64 rowid, XP_Bool isNew, } gtk_list_store_set( store, &iter, ROW_ITEM, rowid, + ROW_THUMB, gib->snap, NAME_ITEM, gib->name, ROOM_ITEM, gib->room, GAMEID_ITEM, gib->gameID, @@ -385,6 +399,7 @@ handle_delete_button( GtkWidget* XP_UNUSED(widget), void* closure ) } else { XP_LOGF( "%s: not calling relaycon_deleted: no relayID", __func__ ); } + g_object_unref( gib.snap ); } apg->selRows = g_array_set_size( apg->selRows, 0 ); updateButtons( apg ); @@ -531,6 +546,7 @@ onNewData( GtkAppGlobals* apg, sqlite3_int64 rowid, XP_Bool isNew ) GameInfo gib; if ( getGameInfo( apg->params->pDb, rowid, &gib ) ) { add_to_list( apg->listWidget, rowid, isNew, &gib ); + g_object_unref( gib.snap ); } } diff --git a/xwords4/linux/red.png b/xwords4/linux/red.png new file mode 100644 index 000000000..6b36c60af Binary files /dev/null and b/xwords4/linux/red.png differ