diff --git a/xwords4/android/XWords4/AndroidManifest.xml b/xwords4/android/XWords4/AndroidManifest.xml index 9bd890a61..30ccb4812 100644 --- a/xwords4/android/XWords4/AndroidManifest.xml +++ b/xwords4/android/XWords4/AndroidManifest.xml @@ -65,7 +65,7 @@ android:screenOrientation="sensor" android:configChanges="keyboardHidden|orientation" > - + diff --git a/xwords4/android/XWords4/jni/xwjni.c b/xwords4/android/XWords4/jni/xwjni.c index 836c0ab99..e6dfc2895 100644 --- a/xwords4/android/XWords4/jni/xwjni.c +++ b/xwords4/android/XWords4/jni/xwjni.c @@ -1255,6 +1255,23 @@ Java_org_eehouse_android_xw4_jni_XwJNI_server_1endGame XWJNI_END(); } +JNIEXPORT jstring JNICALL +Java_org_eehouse_android_xw4_jni_XwJNI_server_1listWordsPlayed +( JNIEnv* env, jclass C, jint gamePtr, jint nMoves ) +{ + jstring result; + XWJNI_START_GLOBALS(); + XP_ASSERT( !!state->game.server ); + XWStreamCtxt* stream = and_empty_stream( MPPARM(mpool) globals ); + server_listWordsPlayed( state->game.server, stream, nMoves ); + result = streamToJString( MPPARM(mpool) env, stream ); + (*env)->DeleteLocalRef( env, result ); + stream_destroy( stream ); + XWJNI_END(); + return result; +} + + JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_server_1sendChat ( JNIEnv* env, jclass C, jint gamePtr, jstring jmsg ) diff --git a/xwords4/android/XWords4/res/layout/wordlist_view.xml b/xwords4/android/XWords4/res/layout/wordlist_view.xml new file mode 100644 index 000000000..2fc096058 --- /dev/null +++ b/xwords4/android/XWords4/res/layout/wordlist_view.xml @@ -0,0 +1,16 @@ + + + + + + + diff --git a/xwords4/android/XWords4/res/values-ca/strings.xml b/xwords4/android/XWords4/res/values-ca/strings.xml index 9e7ed2fec..0b7909193 100644 --- a/xwords4/android/XWords4/res/values-ca/strings.xml +++ b/xwords4/android/XWords4/res/values-ca/strings.xml @@ -18,29 +18,17 @@ Afegeix una partida Configura - Amaga Suprimeix Copia Reinicialitza Nova - Mou amunt - Mou avall - Mou amunt de tot - Mou avall de tot - - Edita la nota - Partida - Crosswords - Partida Diccionaris del Crosswords Configura el %s Configuració del Crosswords - Crosswords - D\'acord Cancel·la @@ -84,7 +72,6 @@ %s quantitat/valors:\n + %d [fitxes romanents] - %d [fitxes al faristol] - El jugador remot ha fet aquest moviment: - %d [temps] mou (des de %s horitzontal) @@ -119,7 +106,6 @@ Jugador remot (s\'ignora) Jugador robot Contrasenya (s\'ignora) - (robot) %s (robot) (jugador visitant) Jugador @@ -132,8 +118,6 @@ Estableix com a predeterminat Detalls - Configura la connexió - Configura la connexió %s Paràmetres de la partida Minuts a la partida @@ -145,8 +129,6 @@ Permet pistes Habilita el temps - Fitxes de colors - Les fitxes es mostren amb el color del jugador que les juga Mostra la fletxa al tauler Les fitxes del faristol es col·locaran sobre la fletxa Explica les jugades del robot @@ -199,7 +181,6 @@ 3L 3P pts - Canvi de fitxes. Pitgeu\n\'Fet\' quan hagueu acabat. Fons de la fitxa Cel·la buida/fons Creu d\'ubicació @@ -209,11 +190,6 @@ URL de baixada de diccionaris Port del repetidor - key_player - Internet/Repetidor - SMS (en desenvolupament) - Bluetooth (en desenvolupament) - Esteu connectat al repetidor. S\'està esperant a %d jugador/s. Error en la connexió @@ -232,8 +208,6 @@ Jugador/s remot/s Un amfitrió ha de tenir com a mínim un jugador remot. - %1$s via repetidor, sala - \"%2$s\" Final de partida %d torns jugats diff --git a/xwords4/android/XWords4/res/values-cs/strings.xml b/xwords4/android/XWords4/res/values-cs/strings.xml index e6298f05e..751c8e51a 100644 --- a/xwords4/android/XWords4/res/values-cs/strings.xml +++ b/xwords4/android/XWords4/res/values-cs/strings.xml @@ -18,29 +18,17 @@ Přidat hru Konfigurovat - Skrýt Vymazat Kopírovat Vynulovat Nová hra podle vzoru - Přesunout výš - Přesunout níž - Přesunout nahoru - Přesunout dolů - - Upravit poznámku - Hrát hru - Crosswords Slovníky Crosswords - Hra Konfigurovat %s Předvolby Crosswords - Crosswords - OK Zrušit Ano @@ -83,7 +71,6 @@ %s počet/hodnota:\n + %d [všechny zbývající kameny] - %d [nepoužité kameny] - Vzdálený hráč provedl tento tah: - %d [čas] tah (od %s napříč) @@ -117,7 +104,6 @@ Hráč klient Hráč robot - (robot) %s (robot) (hráč klient) Heslo @@ -131,8 +117,6 @@ Nastavit jako výchozí Podrobnosti - Konfigurovat připojení - Konfigurovat %s připojení Nastavení hry Minut ve hře @@ -146,8 +130,6 @@ Povolit nápovědu Povolit časovač - Barvy kamenů - Obarvit kameny barvou hráče, který s nimi hrál Ukázat šipku Slovo se začne psát ve směru šipky a od jejího umístění Vysvětlit tahy robota @@ -178,7 +160,6 @@ Trojitá hodnota písmene Dvojitá hodnota slova Trojitá hodnota slova - Výměna kamenů. Zvolte\n\'Ukončit tah\' až budete hotovi. Pozadí kamenů Prázdné pole/pozadí Rozšířené @@ -187,11 +168,6 @@ URL adresa pro nahrávání slovníků Relay port - key_player - Internet/Relay - SMS (nefunkční) - Bluetooth (připravuje se) - Připojen na relay. Čekám na %d hráče. Chyba připojení @@ -211,7 +187,6 @@ Jako Server vyberte jedoho nebo více hráčů kteří se budou moct připojit jako Klient. - %1$s přes Relay, místnost \"%2$s\" Hra skončena Bylo zahráno %d tahů diff --git a/xwords4/android/XWords4/res/values-sk/strings.xml b/xwords4/android/XWords4/res/values-sk/strings.xml index 15c7d90dd..95b635d1e 100644 --- a/xwords4/android/XWords4/res/values-sk/strings.xml +++ b/xwords4/android/XWords4/res/values-sk/strings.xml @@ -17,29 +17,17 @@ Pridať hru Konfigurovať - Skryť Vymazať Kopírovať Vynulovať Nová hra podľa vzoru - Presunúť vyššie - Presunúť nižšie - Presunúť nahor - Presunúť nadol - - Upraviť poznámku - Hrať hru - Crosswords Slovníky Crosswords - Hra Konfigurovať %s Predvoľby Crosswords - Crosswords - OK Zrušiť Áno @@ -82,7 +70,6 @@ %s počet/hodnota:\n + %d [všetky zostávajúce kamene] - %d [nepoužité kamene] - Vzdialený hráč uskutočnil tento ťah: - %d [čas] ťah (od %s naprieč) @@ -116,7 +103,6 @@ Hráč klient Hráč robot - (robot) %s (robot) (hráč klient) Heslo @@ -130,8 +116,6 @@ Nastaviť ako východzí Podrobnosti - Konfigurovať pripojenie - Konfigurovať %s pripojenie Nastavenie hry Minút v hre @@ -145,8 +129,6 @@ Povoliť nápovedu Povoliť časovač - Farby kameňov - Zafarbiť kamene farbou hráča, ktorý s nimi hral Ukázať šipku Slovo sa začne písať v smere šipky a od jejho umiestenia Vysvetliť ťahy robota @@ -176,7 +158,6 @@ Trojitá hodnota písmena Dvojitá hodnota slova Trojitá hodnota slova - Výmena kameňov. Zvoľte\n\'Ukončiť ťah\' až budete hotový. Pozadie kamenňov Prázdne pole/pozadie Rozšírené @@ -185,11 +166,6 @@ URL adresa pre nahrávanie slovníkov Relay port - key_player - Internet/Relay - SMS (nefunkčné) - Bluetooth (pripravuje sa) - Pripojený na relay. Čakám na %d hráčov. @@ -210,7 +186,6 @@ Ako Server vyberte jedného alebo viacerých hráčov ktorí sa budú mocť pripojiť ako Klient. - %1$s cez Relay, miestnosť \"%2$s\" Hra skončená Bolo zahraných %d ťahov diff --git a/xwords4/android/XWords4/res/values/common_rsrc.xml b/xwords4/android/XWords4/res/values/common_rsrc.xml index 079dff5b6..49195e251 100644 --- a/xwords4/android/XWords4/res/values/common_rsrc.xml +++ b/xwords4/android/XWords4/res/values/common_rsrc.xml @@ -73,13 +73,19 @@ key_notagain_newfrom key_notagain_trading + + Crosswords + eehouse.org http://eehouse.org/and_wordlists 4.4 beta 34 - //%1$s/redir.php + //%1$s/newgame.php + + + http://%1$s.thefreedictionary.com/_/dict.aspx?word=%2$s For debugging diff --git a/xwords4/android/XWords4/res/values/strings.xml b/xwords4/android/XWords4/res/values/strings.xml index 8c8f0d239..b05c2b52c 100644 --- a/xwords4/android/XWords4/res/values/strings.xml +++ b/xwords4/android/XWords4/res/values/strings.xml @@ -12,12 +12,28 @@ ############################################################ --> - Crosswords + Crosswords + + + Use the button below to create a + game. Tap an existing game to play it or long-tap it for other + options. + You can hide this and the button + below in the Appearance section of Settings (accessed via your + device\'s menu key). + menuitem in main games-list screen's menu. (The botton can + be hidden in the same way as the above text.) --> Add game + + Use the menu key to add a + game. + + Check relay + + Checking relay for moves + etc... + + + No games found that connect via + the relay. + Delete all @@ -131,6 +156,13 @@ Copy + + + Games that have already connected + to the relay cannot be copied. Use \"New from\" for a + ready-to-play copy with all the same settings. + Rename game @@ -183,6 +215,13 @@ it saves files it downloads. --> Downloads + + Unable to download. Do you have + a web browser installed? + + Select public room + + Fetching public rooms for + %1$d-player games in %2$s. + + + No public rooms found for + %1$d-player games in %2$s. Try refreshing or creating your + own. + Other settings @@ -389,6 +442,9 @@ Confirm save + + Play game 2L @@ -837,7 +897,7 @@ e.g., if the option is "Game Language", "English" in the listing "Game 2 (English)" - The following strings beginning with "game_summary_field" are + The following strings beginning with "game_summary_field_" are possible values --> Include in game listing @@ -1134,7 +1194,7 @@ \u003ca href=\"%1$s\"\u003ETap here to accept\u003c/a\u003E my invitation and join this game.\u003cbr\u003E \u003ca - href=\"http://eehouse.org/market.php\"\u003E + href=\"http://eehouse.org/market_redir.php\"\u003E Tap here to install Crosswords\u003c/a\u003E if you haven\'t already. @@ -1143,7 +1203,7 @@ substituted for "%1$s".--> Accept my invitation and join this game: %1$s . (But install Crosswords first: - http://eehouse.org/market.php ). + http://eehouse.org/market_redir.php ). Clear history + + + + To start a basic networked two-player + game in %s: + + + • Agree on a room name with the other + player. + • Enter the room name in the first box + below. The room names on your two phones must be + identical. + • Optionally, enter player names in the + second box (one per phone) + • Press the \"Play game\" button on both + phones to start the game. + + + Player name + + + Advanced game settings + + + This game cannot connect without a + room name. + + + @@ -1281,104 +1390,93 @@ - - - - For a manual or sourcecode see: - http://xwords.sf.net. To report bugs, suggest features, offer to - help, etc., please email: eehouse@eehouse.org. - - - - Toolbar icons by Sarah Chu; other - credits pending permission. - Downloading Crosswords wordlist %s... - Wordlist not found - Download - Substitute wordlist (wordcount) - Substitute - Close game + + + + Wordlist not found + + A wordlist this game is using has disappeared. (Usually this means it\'s on an external card that is no longer available.) + + + Close game + + Unable to open game \"%1$s\" because no %2$s wordlist found. (It may have been deleted, or stored on an external card that is no longer available.) + + Unable to open game \"%1$s\" because wordlist %2$s not found. (It may have been deleted, or stored on an external card that is no longer available.) You can download a replacement or substitute another %3$s wordlist. + + Download + + + Substitute + + + Substitute wordlist (wordcount) + + + + Password for \"%s\": Game %d - + + Player %d - Fetching public rooms for - %1$d-player games in %2$s. - No public rooms found for - %1$d-player games in %2$s. Try refreshing or creating your - own. - + Moves made New game moves have arrived - Games that have already connected - to the relay cannot be copied. Use \"New from\" for a - ready-to-play copy with all the same settings. - - No games found that connect via - the relay. - - Checking relay for moves - etc... - - - You are entering tile-exchange - mode. Tap tiles to add/remove them from the set to be - exchanged. Use the buttons to commit your turn or exit exchange - mode. - - Tap to select tiles... - - To start a basic networked two-player - game in %s: - - • Agree on a room name with the other - player. - • Enter the room name in the first box - below. The room names on your two phones must be - identical. - • Optionally, enter player names in the - second box (one per phone) - • Press the \"Play game\" button on both - phones to start the game. - - Advanced game settings - - Player name - This game cannot connect without a - room name. - - Use the button below to create a - game. Tap an existing game to play it or long-tap it for other - options. - You can hide this and the button - below in the Appearance section of Settings (accessed via your - device\'s menu key). - Use the menu key to add a - game. - - Unable to download. Do you have - a web browser installed? - + You are entering tile-exchange + mode. Tap tiles to add/remove them from the set to be + exchanged. Use the buttons to commit your turn or exit exchange + mode. + @@ -1631,11 +1735,28 @@ --> Crosswords for Android, Version %1$s, - rev %2$s. + rev %2$s. Copyright (C) 1998-2011 by Eric - House. This free/open source software is released under the GNU Public - License. + House. This free/open source software is released under the GNU Public + License. + + + For a manual or sourcecode see: + http://xwords.sf.net. To report bugs, suggest features, offer to + help, etc., please email: eehouse@eehouse.org. + + + + + + Toolbar icons by Sarah Chu; other + credits pending permission. + + Recent changes @@ -1643,4 +1764,10 @@ dialog --> Recent changes + + Lookup word + Tap to lookup + Done + + diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java index 4190d2817..fe4a7fcf2 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -22,6 +22,7 @@ package org.eehouse.android.xw4; import android.app.Activity; import android.os.Bundle; +import android.text.TextUtils; import android.view.View; import android.view.Menu; import android.view.MenuItem; @@ -39,15 +40,20 @@ import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.DialogInterface; import android.content.DialogInterface.OnDismissListener; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.EditText; +import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import junit.framework.Assert; import android.content.res.Configuration; import android.content.pm.ActivityInfo; +import android.net.Uri; import org.eehouse.android.xw4.jni.*; import org.eehouse.android.xw4.jni.JNIThread.*; @@ -70,6 +76,8 @@ public class BoardActivity extends XWActivity private static final int QUERY_ENDGAME = DLG_OKONLY + 7; private static final int DLG_DELETED = DLG_OKONLY + 8; private static final int DLG_INVITE = DLG_OKONLY + 9; + private static final int DLG_SCORES_BLK = DLG_OKONLY + 10; + private static final int DLG_WORDPICK = DLG_OKONLY + 11; private static final int CHAT_REQUEST = 1; private static final int SCREEN_ON_TIME = 10 * 60 * 1000; // 10 mins @@ -93,6 +101,7 @@ public class BoardActivity extends XWActivity private static final String DLG_BYTES = "DLG_BYTES"; private static final String ROOM = "ROOM"; private static final String TOASTSTR = "TOASTSTR"; + private static final String WORDS = "WORDS"; private BoardView m_view; private int m_jniGamePtr; @@ -133,6 +142,7 @@ public class BoardActivity extends XWActivity private String m_room; private String m_toastStr; + private String[] m_words; private int m_missing; private boolean m_haveInvited = false; @@ -208,6 +218,7 @@ public class BoardActivity extends XWActivity case QUERY_REQUEST_BLK: case QUERY_INFORM_BLK: + case DLG_SCORES_BLK: ab = new AlertDialog.Builder( this ) .setMessage( m_dlgBytes ); if ( 0 != m_dlgTitle ) { @@ -230,6 +241,16 @@ public class BoardActivity extends XWActivity } }; ab.setNegativeButton( R.string.button_no, lstnr ); + } else if ( DLG_SCORES_BLK == id ) { + if ( curLangSupported() ) { + lstnr = new DialogInterface.OnClickListener() { + public void onClick( DialogInterface dialog, + int whichButton ) { + m_jniThread.handle( JNICmd.CMD_WORDS ); + } + }; + ab.setNegativeButton( R.string.button_lookup, lstnr ); + } } dialog = ab.create(); @@ -300,6 +321,31 @@ public class BoardActivity extends XWActivity .create(); } break; + case DLG_WORDPICK: + LinearLayout layout = + (LinearLayout)Utils.inflate( this, R.layout.wordlist_view ); + ListView list = (ListView)layout.findViewById( R.id.words ); + ArrayAdapter adapter = + new ArrayAdapter( this, + //android.R.layout.select_dialog_item, + android.R.layout.simple_list_item_1, + m_words ) ; + list.setAdapter( adapter ); + OnItemClickListener oicl = new OnItemClickListener() { + public void onItemClick(AdapterView parent, + View view, + int position, long id ) { + lookupWord( m_words[position] ); + } + }; + list.setOnItemClickListener( oicl ); + dialog = new AlertDialog.Builder( this ) + .setTitle( R.string.title_lookup ) + .setView( layout ) + .setNegativeButton( R.string.button_done, null ) + .create(); + Utils.setRemoveOnDismiss( this, dialog, id ); + break; default: // just drop it; super.onCreateDialog likely failed break; @@ -387,6 +433,7 @@ public class BoardActivity extends XWActivity outState.putString( DLG_BYTES, m_dlgBytes ); outState.putString( ROOM, m_room ); outState.putString( TOASTSTR, m_toastStr ); + outState.putStringArray( WORDS, m_words ); } private void getBundledData( Bundle bundle ) @@ -397,6 +444,7 @@ public class BoardActivity extends XWActivity m_dlgBytes = bundle.getString( DLG_BYTES ); m_room = bundle.getString( ROOM ); m_toastStr = bundle.getString( TOASTSTR ); + m_words = bundle.getStringArray( WORDS ); } } @@ -1032,7 +1080,8 @@ public class BoardActivity extends XWActivity case UtilCtxt.QUERY_ROBOT_TRADE: m_dlgBytes = query; m_dlgTitle = R.string.info_title; - waitBlockingDialog( QUERY_INFORM_BLK, 0 ); + waitBlockingDialog( QUERY_ROBOT_MOVE == id ? + DLG_SCORES_BLK : QUERY_INFORM_BLK, 0 ); result = true; break; @@ -1230,6 +1279,17 @@ public class BoardActivity extends XWActivity adjustTradeVisibility(); } break; + case JNIThread.GOT_WORDS: + m_words = + TextUtils.split( (String)msg.obj, "\n" ); + if ( 0 == m_words.length ) { + // drop it + } else if ( 1 == m_words.length ) { + lookupWord( m_words[0] ); + } else { + showDialog( DLG_WORDPICK ); + } + break; } } }; @@ -1501,4 +1561,40 @@ public class BoardActivity extends XWActivity which ); } } + + private void lookupWord( String word ) + { + String fmt = getString( R.string.word_lookupf ); + String dict_url = String.format( fmt, curLangCode(), word ); + Uri uri = Uri.parse( dict_url ); + Intent intent = new Intent( Intent.ACTION_VIEW, uri ); + intent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK ); + + try { + startActivity( intent ); + } catch ( android.content.ActivityNotFoundException anfe ) { + Utils.logf( "%s", anfe.toString() ); + } + } + + private String curLangCode() + { + // from string-array name="language_names" in common_rsrc.xml + switch( m_gi.dictLang ) { + case 1: + return "en"; + case 2: + return "fr"; + case 3: + return "de"; + default: + return null; + } + } + + private boolean curLangSupported() + { + return null != curLangCode(); + } + } // class BoardActivity diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java index 8f327f55b..43d605753 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java @@ -55,6 +55,7 @@ public class BoardView extends View implements DrawCtx, BoardHandler, private Paint m_tileStrokePaint; private int m_jniGamePtr; private CurGameInfo m_gi; + private CommonPrefs m_prefs; private int m_layoutWidth; private int m_layoutHeight; private Canvas m_canvas; // owns the bitmap @@ -151,10 +152,10 @@ public class BoardView extends View implements DrawCtx, BoardHandler, m_boundsScratch = new Rect(); - CommonPrefs prefs = CommonPrefs.get(context); - m_playerColors = prefs.playerColors; - m_bonusColors = prefs.bonusColors; - m_otherColors = prefs.otherColors; + m_prefs = CommonPrefs.get(context); + m_playerColors = m_prefs.playerColors; + m_bonusColors = m_prefs.bonusColors; + m_otherColors = m_prefs.otherColors; m_bonusSummaries = new String[5]; int[] ids = { R.string.bonus_l2x_summary, @@ -427,7 +428,7 @@ public class BoardView extends View implements DrawCtx, BoardHandler, } String[] texts = m_scores[dsi.playerNum]; int color = m_playerColors[dsi.playerNum]; - if ( !CommonPrefs.get(m_context).allowPeek ) { + if ( !m_prefs.allowPeek ) { color = adjustColor( color ); } m_fillPaint.setColor( color ); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameConfig.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameConfig.java index 49e161dfc..6a9f78415 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameConfig.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameConfig.java @@ -922,40 +922,40 @@ public class GameConfig extends XWActivity return 0; } - private int titleForDlg( int id ) - { - switch( id ) { - // case ROLE_EDIT_RELAY: - // return R.string.tab_relay; - // case ROLE_EDIT_SMS: - // return R.string.tab_sms; - // case ROLE_EDIT_BT: - // return R.string.tab_bluetooth; - } - Assert.fail(); - return -1; - } + // private int titleForDlg( int id ) + // { + // switch( id ) { + // // case ROLE_EDIT_RELAY: + // // return R.string.tab_relay; + // // case ROLE_EDIT_SMS: + // // return R.string.tab_sms; + // // case ROLE_EDIT_BT: + // // return R.string.tab_bluetooth; + // } + // Assert.fail(); + // return -1; + // } - private String[] makeXportStrings() - { - ArrayList strings = new ArrayList(); - ArrayList types - = new ArrayList(); + // private String[] makeXportStrings() + // { + // ArrayList strings = new ArrayList(); + // ArrayList types + // = new ArrayList(); - strings.add( getString(R.string.tab_relay) ); - types.add( CommsAddrRec.CommsConnType.COMMS_CONN_RELAY ); + // strings.add( getString(R.string.tab_relay) ); + // types.add( CommsAddrRec.CommsConnType.COMMS_CONN_RELAY ); - if ( m_canDoSMS ) { - strings.add( getString(R.string.tab_sms) ); - types.add( CommsAddrRec.CommsConnType.COMMS_CONN_SMS ); - } - if ( m_canDoBT ) { - strings.add( getString(R.string.tab_bluetooth) ); - types.add( CommsAddrRec.CommsConnType.COMMS_CONN_BT ); - } - m_types = types.toArray( new CommsAddrRec.CommsConnType[types.size()] ); - return strings.toArray( new String[strings.size()] ); - } + // if ( m_canDoSMS ) { + // strings.add( getString(R.string.tab_sms) ); + // types.add( CommsAddrRec.CommsConnType.COMMS_CONN_SMS ); + // } + // if ( m_canDoBT ) { + // strings.add( getString(R.string.tab_bluetooth) ); + // types.add( CommsAddrRec.CommsConnType.COMMS_CONN_BT ); + // } + // m_types = types.toArray( new CommsAddrRec.CommsConnType[types.size()] ); + // return strings.toArray( new String[strings.size()] ); + // } private void saveChanges() { diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java index 15a1d5b76..c95ac244f 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java @@ -77,6 +77,7 @@ public class JNIThread extends Thread { CMD_RESEND, CMD_HISTORY, CMD_FINAL, + CMD_WORDS, CMD_ENDGAME, CMD_POST_OVER, CMD_SENDCHAT, @@ -88,6 +89,7 @@ public class JNIThread extends Thread { public static final int DIALOG = 3; public static final int QUERY_ENDGAME = 4; public static final int TOOLBAR_STATES = 5; + public static final int GOT_WORDS = 6; public class GameStateInfo implements Cloneable { public int visTileCount; @@ -490,6 +492,11 @@ public class JNIThread extends Thread { } break; + case CMD_WORDS: + String words = XwJNI.server_listWordsPlayed( m_jniGamePtr, 1 ); + Message.obtain( m_handler, GOT_WORDS, words ).sendToTarget(); + break; + case CMD_ENDGAME: XwJNI.server_endGame( m_jniGamePtr ); draw = true; diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/XwJNI.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/XwJNI.java index 1ff31b0b2..1d3d951e1 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/XwJNI.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/XwJNI.java @@ -221,6 +221,8 @@ public class XwJNI { public static native void server_initClientConnection( int gamePtr ); public static native void server_endGame( int gamePtr ); public static native void server_sendChat( int gamePtr, String msg ); + public static native String server_listWordsPlayed( int gamePtr, + int nMoves ); // hybrid to save work public static native boolean board_server_prefsChanged( int gamePtr, diff --git a/xwords4/android/scripts/market.php b/xwords4/android/scripts/market_redir.php similarity index 100% rename from xwords4/android/scripts/market.php rename to xwords4/android/scripts/market_redir.php diff --git a/xwords4/android/scripts/redir.php b/xwords4/android/scripts/newgame.php similarity index 100% rename from xwords4/android/scripts/redir.php rename to xwords4/android/scripts/newgame.php diff --git a/xwords4/android/scripts/strings_check.sh b/xwords4/android/scripts/strings_check.sh index 6b648c9f8..a16f37d8b 100755 --- a/xwords4/android/scripts/strings_check.sh +++ b/xwords4/android/scripts/strings_check.sh @@ -22,10 +22,9 @@ list_ids() { list_pairs() { XML_FILE=$1 - #xmlstarlet sel -t -m "//string" -v @name -o ':' -v . -n $XML_FILE | tr -d '\n' | sed 's, *, ,g' for NAME in $(list_ids $XML_FILE); do - xmlstarlet sel -t -m "//string[@name='$NAME']" -v @name -o ':' -v . $XML_FILE \ - | tr -d '\n' | sed 's, *, ,g' + xmlstarlet sel -t -m "//string[@name='$NAME']" -v @name -o ':' \ + -v . $XML_FILE | tr -d '\n' | sed 's, *, ,g' echo "" done } diff --git a/xwords4/common/comms.c b/xwords4/common/comms.c index 14a1b4b0c..904d1b41b 100644 --- a/xwords4/common/comms.c +++ b/xwords4/common/comms.c @@ -2136,25 +2136,20 @@ send_via_relay( CommsCtxt* comms, XWRELAY_Cmd cmd, XWHostID destID, XP_U8* buf; len = stream_getSize( tmpStream ); - buf = XP_MALLOC( comms->mpool, len ); - if ( buf != NULL ) { - stream_getBytes( tmpStream, buf, len ); - } - stream_destroy( tmpStream ); - if ( buf != NULL ) { + if ( 0 < len ) { XP_U16 result; CommsAddrRec addr; comms_getAddr( comms, &addr ); XP_LOGF( "%s: passing %d bytes to sendproc", __func__, len ); - result = (*comms->procs.send)( buf, len, &addr, - comms->procs.closure ); + result = (*comms->procs.send)( stream_getPtr(tmpStream), len, + &addr, comms->procs.closure ); success = result == len; if ( success ) { setHeartbeatTimer( comms ); } } - XP_FREE( comms->mpool, buf ); + stream_destroy( tmpStream ); } return success; } /* send_via_relay */ diff --git a/xwords4/common/memstream.c b/xwords4/common/memstream.c index ec64b2439..569a04395 100644 --- a/xwords4/common/memstream.c +++ b/xwords4/common/memstream.c @@ -301,7 +301,7 @@ mem_stream_copyFromStream( XWStreamCtxt* p_sctx, XWStreamCtxt* src, if ( nBytes < len ) { len = nBytes; } - stream_getBytes( src, buf, len ); + stream_getBytes( src, buf, len );// fix to use stream_getPtr()? stream_putBytes( p_sctx, buf, len ); nBytes -= len; } @@ -338,6 +338,13 @@ mem_stream_getSize( const XWStreamCtxt* p_sctx ) return size; } /* mem_stream_getSize */ +static const XP_U8* +mem_stream_getPtr( const XWStreamCtxt* p_sctx ) +{ + MemStreamCtxt* stream = (MemStreamCtxt*)p_sctx; + return stream->buf; +} /* mem_stream_getPtr */ + static XP_PlayerAddr mem_stream_getAddress( XWStreamCtxt* p_sctx ) { @@ -451,6 +458,7 @@ make_vtable( MemStreamCtxt* stream ) SET_VTABLE_ENTRY( vtable, stream_close, mem ); SET_VTABLE_ENTRY( vtable, stream_getSize, mem ); + SET_VTABLE_ENTRY( vtable, stream_getPtr, mem ); SET_VTABLE_ENTRY( vtable, stream_getAddress, mem ); SET_VTABLE_ENTRY( vtable, stream_setAddress, mem ); diff --git a/xwords4/common/model.c b/xwords4/common/model.c index b0be06f3c..4f996ea60 100644 --- a/xwords4/common/model.c +++ b/xwords4/common/model.c @@ -64,12 +64,12 @@ static void makeTileTrade( ModelCtxt* model, XP_S16 player, TrayTileSet* oldTiles, TrayTileSet* newTiles ); static XP_S16 commitTurn( ModelCtxt* model, XP_S16 turn, TrayTileSet* newTiles, XWStreamCtxt* stream, - XP_Bool useStack ); + WordNotifierInfo* wni, XP_Bool useStack ); static void buildModelFromStack( ModelCtxt* model, StackCtxt* stack, - XWStreamCtxt* stream, + XP_Bool useStack, XP_U16 initial, + XWStreamCtxt* stream, WordNotifierInfo* wni, MovePrintFuncPre mpfpr, - MovePrintFuncPost mpfpo, - void* closure ); + MovePrintFuncPost mpfpo, void* closure ); static void setPendingCounts( ModelCtxt* model, XP_S16 turn ); static void loadPlayerCtxt( XWStreamCtxt* stream, XP_U16 version, PlayerCtxt* pc ); @@ -135,9 +135,9 @@ model_makeFromStream( MPFORMAL XWStreamCtxt* stream, DictionaryCtxt* dict, stack_loadFromStream( model->vol.stack, stream ); - buildModelFromStack( model, model->vol.stack, (XWStreamCtxt*)NULL, - (MovePrintFuncPre)NULL, - (MovePrintFuncPost)NULL, NULL ); + buildModelFromStack( model, model->vol.stack, XP_FALSE, 0, + (XWStreamCtxt*)NULL, (WordNotifierInfo*)NULL, + (MovePrintFuncPre)NULL, (MovePrintFuncPost)NULL, NULL ); for ( i = 0; i < model->nPlayers; ++i ) { loadPlayerCtxt( stream, version, &model->players[i] ); @@ -244,19 +244,19 @@ model_destroy( ModelCtxt* model ) } /* model_destroy */ static void -buildModelFromStack( ModelCtxt* model, StackCtxt* stack, - XWStreamCtxt* stream, - MovePrintFuncPre mpf_pre, MovePrintFuncPost mpf_post, - void* closure ) +buildModelFromStack( ModelCtxt* model, StackCtxt* stack, XP_Bool useStack, + XP_U16 initial, XWStreamCtxt* stream, + WordNotifierInfo* wni, MovePrintFuncPre mpf_pre, + MovePrintFuncPost mpf_post, void* closure ) { StackEntry entry; - XP_U16 i; + XP_U16 ii; XP_S16 moveScore = 0; /* keep compiler happy */ - for ( i = 0; stack_getNthEntry( stack, i, &entry ); ++i ) { + for ( ii = initial; stack_getNthEntry( stack, ii, &entry ); ++ii ) { if ( !!mpf_pre ) { - (*mpf_pre)( model, i, &entry, closure ); + (*mpf_pre)( model, ii, &entry, closure ); } switch ( entry.moveType ) { @@ -265,7 +265,8 @@ buildModelFromStack( ModelCtxt* model, StackCtxt* stack, model_makeTurnFromMoveInfo( model, entry.playerNum, &entry.u.move.moveInfo); moveScore = commitTurn( model, entry.playerNum, - &entry.u.move.newTiles, stream, XP_FALSE); + &entry.u.move.newTiles, + stream, wni, useStack ); break; case TRADE_TYPE: makeTileTrade( model, entry.playerNum, &entry.u.trade.oldTiles, @@ -280,7 +281,7 @@ buildModelFromStack( ModelCtxt* model, StackCtxt* stack, &entry.u.phony.moveInfo); /* do something here to cause it to print */ (void)getCurrentMoveScoreIfLegal( model, entry.playerNum, stream, - &moveScore ); + wni, &moveScore ); moveScore = 0; model_resetCurrentTurn( model, entry.playerNum ); @@ -290,7 +291,7 @@ buildModelFromStack( ModelCtxt* model, StackCtxt* stack, } if ( !!mpf_post ) { - (*mpf_post)( model, i, &entry, moveScore, closure ); + (*mpf_post)( model, ii, &entry, moveScore, closure ); } } } /* buildModelFromStack */ @@ -668,10 +669,10 @@ model_undoLatestMoves( ModelCtxt* model, PoolContext* pool, XP_U16 turn = 0; Tile blankTile = dict_getBlankTile( model_getDictionary(model) ); XP_Bool success = XP_TRUE; - XP_S16 moveSought = *moveNumP; + XP_S16 moveSought = !!moveNumP ? *moveNumP : -1; XP_U16 nMovesUndone; XP_U16 nStackEntries; - + nStackEntries = stack_getNEntries( stack ); if ( nStackEntries < nMovesSought ) { return XP_FALSE; @@ -772,10 +773,16 @@ model_undoLatestMoves( ModelCtxt* model, PoolContext* pool, } if ( success ) { - *turnP = turn; - *moveNumP = entry.moveNum; + if ( !!turnP ) { + *turnP = turn; + } + if ( !!moveNumP ) { + *moveNumP = entry.moveNum; + } } else { while ( nMovesUndone-- ) { + /* undo isn't enough here: pool's got tiles in it!! */ + XP_ASSERT( 0 ); stack_redo( stack ); } } @@ -873,11 +880,11 @@ model_makeTurnFromStream( ModelCtxt* model, XP_U16 playerNum, void model_makeTurnFromMoveInfo( ModelCtxt* model, XP_U16 playerNum, - MoveInfo* newMove ) + const MoveInfo* newMove ) { XP_U16 col, row, i; XP_U16* other; - MoveInfoTile* tinfo; + const MoveInfoTile* tinfo; Tile blank; XP_U16 numTiles; @@ -1389,7 +1396,7 @@ putBackOtherPlayersTiles( ModelCtxt* model, XP_U16 notMyTurn, */ static XP_S16 commitTurn( ModelCtxt* model, XP_S16 turn, TrayTileSet* newTiles, - XWStreamCtxt* stream, XP_Bool useStack ) + XWStreamCtxt* stream, WordNotifierInfo* wni, XP_Bool useStack ) { XP_U16 ii; PlayerCtxt* player; @@ -1403,7 +1410,7 @@ commitTurn( ModelCtxt* model, XP_S16 turn, TrayTileSet* newTiles, #ifdef DEBUG XP_ASSERT( getCurrentMoveScoreIfLegal( model, turn, (XWStreamCtxt*)NULL, - &score ) ); + (WordNotifierInfo*)NULL, &score ) ); invalidateScore( model, turn ); #endif @@ -1450,7 +1457,7 @@ commitTurn( ModelCtxt* model, XP_S16 turn, TrayTileSet* newTiles, ++model->vol.nTilesOnBoard; } - (void)getCurrentMoveScoreIfLegal( model, turn, stream, &score ); + (void)getCurrentMoveScoreIfLegal( model, turn, stream, wni, &score ); XP_ASSERT( score >= 0 ); player->score += score; @@ -1473,7 +1480,8 @@ commitTurn( ModelCtxt* model, XP_S16 turn, TrayTileSet* newTiles, void model_commitTurn( ModelCtxt* model, XP_S16 turn, TrayTileSet* newTiles ) { - (void)commitTurn( model, turn, newTiles, (XWStreamCtxt*)NULL, XP_TRUE ); + (void)commitTurn( model, turn, newTiles, (XWStreamCtxt*)NULL, + (WordNotifierInfo*)NULL, XP_TRUE ); } /* model_commitTurn */ /* Given a rack of new tiles and of old, remove all the old from the tray and @@ -1904,8 +1912,8 @@ makeTmpModel( ModelCtxt* model, XWStreamCtxt* stream, model_numRows(model)); model_setNPlayers( tmpModel, model->nPlayers ); - buildModelFromStack( tmpModel, model->vol.stack, stream, - mpf_pre, mpf_post, closure ); + buildModelFromStack( tmpModel, model->vol.stack, XP_FALSE, 0, stream, + (WordNotifierInfo*)NULL, mpf_pre, mpf_post, closure ); return tmpModel; } /* makeTmpModel */ @@ -2025,6 +2033,59 @@ model_recentPassCountOk( ModelCtxt* model ) return count < okCount; } +typedef struct _RecordWordsInfo { + XWStreamCtxt* stream; + XP_U16 nWords; +} RecordWordsInfo; + +static XP_Bool +recordWord( const XP_UCHAR* word, XP_Bool isLegal, void* closure ) +{ + RecordWordsInfo* info = (RecordWordsInfo*)closure; + XWStreamCtxt* stream = info->stream; + XP_ASSERT( isLegal ); + XP_LOGF( "%s(%s)", __func__, word ); + if ( 0 < info->nWords++ ) { + stream_putU8( stream, '\n' ); + } + stream_catString( stream, word ); + return XP_TRUE; +} + +XP_Bool +model_getWordsPlayed( ModelCtxt* model, PoolContext* pool, + XP_U16 nTurns, XWStreamCtxt* stream ) +{ + XP_ASSERT( !!stream ); + StackCtxt* stack = model->vol.stack; + StackCtxt* tmpStack = stack_copy( stack ); + + XP_U16 nPlayers = model->nPlayers; + XP_U16 nEntries = stack_getNEntries( stack ); + nEntries -= nPlayers; /* skip assignments */ + if ( nTurns > nEntries ) { + nTurns = nEntries; + } + + if ( model_undoLatestMoves( model, pool, nTurns, NULL, NULL ) ) { + RecordWordsInfo info = { .stream = stream, .nWords = 0 }; + WordNotifierInfo notifyInfo = { .proc = recordWord, + .closure = &info, + }; + + /* Now push the undone moves back into the model one at a time. + recordWord() will add each played word to the stream as it's + scored */ + buildModelFromStack( model, tmpStack, XP_TRUE, + nEntries - nTurns + nPlayers,/* skip assignments */ + (XWStreamCtxt*)NULL, ¬ifyInfo, + (MovePrintFuncPre)NULL, (MovePrintFuncPost)NULL, + NULL ); + } + stack_destroy( tmpStack ); + return XP_TRUE; +} + XP_Bool model_getPlayersLastScore( ModelCtxt* model, XP_S16 player, XP_UCHAR* expl, XP_U16* explLen ) diff --git a/xwords4/common/model.h b/xwords4/common/model.h index 2e15c0bc6..5775b7c52 100644 --- a/xwords4/common/model.h +++ b/xwords4/common/model.h @@ -197,7 +197,7 @@ void model_currentMoveToStream( ModelCtxt* model, XP_S16 turn, void model_makeTurnFromStream( ModelCtxt* model, XP_U16 playerNum, XWStreamCtxt* stream ); void model_makeTurnFromMoveInfo( ModelCtxt* model, XP_U16 playerNum, - MoveInfo* newMove ); + const MoveInfo* newMove ); void model_resetCurrentTurn( ModelCtxt* model, XP_S16 turn ); XP_S16 model_getNMoves( const ModelCtxt* model ); @@ -246,11 +246,14 @@ typedef struct WordNotifierInfo { } WordNotifierInfo; XP_Bool getCurrentMoveScoreIfLegal( ModelCtxt* model, XP_S16 turn, - XWStreamCtxt* stream, XP_S16* score ); + XWStreamCtxt* stream, + WordNotifierInfo* wni, XP_S16* score ); XP_S16 model_getPlayerScore( ModelCtxt* model, XP_S16 player ); XP_Bool model_getPlayersLastScore( ModelCtxt* model, XP_S16 player, XP_UCHAR* expl, XP_U16* explLen ); +XP_Bool model_getWordsPlayed( ModelCtxt* model, PoolContext* pool, + XP_U16 nTurns, XWStreamCtxt* stream ); /* Have there been too many passes (so game should end)? */ XP_Bool model_recentPassCountOk( ModelCtxt* model ); diff --git a/xwords4/common/movestak.c b/xwords4/common/movestak.c index ac2295fa2..ead8acfa1 100644 --- a/xwords4/common/movestak.c +++ b/xwords4/common/movestak.c @@ -108,7 +108,7 @@ stack_loadFromStream( StackCtxt* stack, XWStreamCtxt* stream ) } /* stack_makeFromStream */ void -stack_writeToStream( StackCtxt* stack, XWStreamCtxt* stream ) +stack_writeToStream( const StackCtxt* stack, XWStreamCtxt* stream ) { XP_U16 nBytes; XWStreamCtxt* data = stack->data; @@ -138,6 +138,21 @@ stack_writeToStream( StackCtxt* stack, XWStreamCtxt* stream ) } } /* stack_writeToStream */ +StackCtxt* +stack_copy( const StackCtxt* stack ) +{ + StackCtxt* newStack = NULL; + XWStreamCtxt* stream = mem_stream_make( MPPARM(stack->mpool) + stack->vtmgr, NULL, 0, NULL ); + stack_writeToStream( stack, stream ); + + newStack = stack_make( MPPARM(stack->mpool) stack->vtmgr ); + stack_loadFromStream( newStack, stream ); + stack_setBitsPerTile( newStack, stack->bitsPerTile ); + stream_destroy( stream ); + return newStack; +} + static void pushEntry( StackCtxt* stack, const StackEntry* entry ) { diff --git a/xwords4/common/movestak.h b/xwords4/common/movestak.h index c287aa807..ba5c52ba7 100644 --- a/xwords4/common/movestak.h +++ b/xwords4/common/movestak.h @@ -72,7 +72,8 @@ void stack_init( StackCtxt* stack ); void stack_setBitsPerTile( StackCtxt* stack, XP_U16 bitsPerTile ); void stack_loadFromStream( StackCtxt* stack, XWStreamCtxt* stream ); -void stack_writeToStream( StackCtxt* stack, XWStreamCtxt* stream ); +void stack_writeToStream( const StackCtxt* stack, XWStreamCtxt* stream ); +StackCtxt* stack_copy( const StackCtxt* stack ); void stack_addMove( StackCtxt* stack, XP_U16 turn, MoveInfo* moveInfo, TrayTileSet* newTiles ); diff --git a/xwords4/common/mscore.c b/xwords4/common/mscore.c index 2e17ca684..6be9b12ae 100644 --- a/xwords4/common/mscore.c +++ b/xwords4/common/mscore.c @@ -75,7 +75,8 @@ static void formatSummary( XWStreamCtxt* stream, const ModelCtxt* model, * invalidate the score. */ static void -scoreCurrentMove( ModelCtxt* model, XP_S16 turn, XWStreamCtxt* stream ) +scoreCurrentMove( ModelCtxt* model, XP_S16 turn, XWStreamCtxt* stream, + WordNotifierInfo* notifyInfo ) { PlayerCtxt* player = &model->players[turn]; XP_S16 score; @@ -84,7 +85,7 @@ scoreCurrentMove( ModelCtxt* model, XP_S16 turn, XWStreamCtxt* stream ) /* recalc goes here */ score = checkScoreMove( model, turn, (EngineCtxt*)NULL, stream, - XP_TRUE, (WordNotifierInfo*)NULL ); + XP_TRUE, notifyInfo ); XP_ASSERT( score >= 0 || score == ILLEGAL_MOVE_SCORE ); player->curMoveScore = score; @@ -127,11 +128,12 @@ invalidateScore( ModelCtxt* model, XP_S16 turn ) XP_Bool getCurrentMoveScoreIfLegal( ModelCtxt* model, XP_S16 turn, - XWStreamCtxt* stream, XP_S16* score ) + XWStreamCtxt* stream, + WordNotifierInfo* wni, XP_S16* score ) { PlayerCtxt* player = &model->players[turn]; if ( !player->curMoveValid ) { - scoreCurrentMove( model, turn, stream ); + scoreCurrentMove( model, turn, stream, wni ); } *score = player->curMoveScore; diff --git a/xwords4/common/server.c b/xwords4/common/server.c index 88cc91161..d571e3d68 100644 --- a/xwords4/common/server.c +++ b/xwords4/common/server.c @@ -841,14 +841,11 @@ showPrevScore( ServerCtxt* server ) if ( !!server->nv.prevMoveStream ) { XWStreamCtxt* prevStream = server->nv.prevMoveStream; XP_U16 len = stream_getSize( prevStream ); - XP_UCHAR* buf = XP_MALLOC( server->mpool, len ); - stream_getBytes( prevStream, buf, len ); - stream_destroy( prevStream ); server->nv.prevMoveStream = NULL; - stream_putBytes( stream, buf, len ); - XP_FREE( server->mpool, buf ); + stream_putBytes( stream, stream_getPtr( prevStream ), len ); + stream_destroy( prevStream ); } (void)util_userQuery( util, QUERY_ROBOT_MOVE, stream ); @@ -2640,6 +2637,12 @@ server_formatRemainingTiles( ServerCtxt* server, XWStreamCtxt* stream, } } /* server_formatRemainingTiles */ +void +server_listWordsPlayed( ServerCtxt* server, XWStreamCtxt* stream, XP_U16 nMoves ) +{ + (void)model_getWordsPlayed( server->vol.model, server->pool, nMoves, stream ); +} + #define IMPOSSIBLY_LOW_SCORE -1000 void server_writeFinalScores( ServerCtxt* server, XWStreamCtxt* stream ) diff --git a/xwords4/common/server.h b/xwords4/common/server.h index aa8e1351c..e3c32a315 100644 --- a/xwords4/common/server.h +++ b/xwords4/common/server.h @@ -124,6 +124,9 @@ void server_formatDictCounts( ServerCtxt* server, XWStreamCtxt* stream, void server_formatRemainingTiles( ServerCtxt* server, XWStreamCtxt* stream, XP_S16 player ); +void server_listWordsPlayed( ServerCtxt* server, XWStreamCtxt* stream, + XP_U16 nMoves ); + void server_writeFinalScores( ServerCtxt* server, XWStreamCtxt* stream ); #ifdef CPLUS diff --git a/xwords4/common/tray.c b/xwords4/common/tray.c index c03420b8a..d81a78300 100644 --- a/xwords4/common/tray.c +++ b/xwords4/common/tray.c @@ -314,7 +314,9 @@ drawPendingScore( BoardCtxt* board, XP_Bool hasCursor ) XP_Rect lastTileR; (void)getCurrentMoveScoreIfLegal( board->model, selPlayer, - (XWStreamCtxt*)NULL, &turnScore ); + (XWStreamCtxt*)NULL, + (WordNotifierInfo*)NULL, + &turnScore ); figureTrayTileRect( board, MAX_TRAY_TILES-1, &lastTileR ); draw_score_pendingScore( board->draw, &lastTileR, turnScore, selPlayer, diff --git a/xwords4/common/xwstream.h b/xwords4/common/xwstream.h index 56dbacf2b..32a72f8f5 100644 --- a/xwords4/common/xwstream.h +++ b/xwords4/common/xwstream.h @@ -69,6 +69,8 @@ typedef struct StreamCtxVTable { void (*m_stream_close)( XWStreamCtxt* dctx ); XP_U16 (*m_stream_getSize)( const XWStreamCtxt* dctx ); + + const XP_U8* (*m_stream_getPtr)( const XWStreamCtxt* dctx ); /* void (*m_stream_makeReturnAddr)( XWStreamCtxt* dctx, XP_PlayerAddr* addr, */ /* XP_U16* addrLen ); */ @@ -143,6 +145,9 @@ struct XWStreamCtxt { #define stream_getSize(sc) \ (sc)->vtable->m_stream_getSize((sc)) +#define stream_getPtr(sc) \ + (sc)->vtable->m_stream_getPtr((sc)) + #define stream_makeReturnAddr(sc,addr,len) \ (sc)->vtable->m_stream_makeReturnAddr((sc),(addr),(len)) diff --git a/xwords4/linux/Makefile b/xwords4/linux/Makefile index e91e4af22..9be2e4f46 100644 --- a/xwords4/linux/Makefile +++ b/xwords4/linux/Makefile @@ -33,7 +33,7 @@ endif # CC = llvm-gcc DO_CURSES = -DPLATFORM_NCURSES -# DO_CURSES += -DUSE_GLIBLOOP +DO_CURSES += -DUSE_GLIBLOOP ifdef CURSES_SMALL_SCREEN DO_CURSES += -DCURSES_SMALL_SCREEN endif diff --git a/xwords4/linux/cursesmain.c b/xwords4/linux/cursesmain.c index 9b8a3a4f2..417c6ef8f 100644 --- a/xwords4/linux/cursesmain.c +++ b/xwords4/linux/cursesmain.c @@ -373,21 +373,37 @@ 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 ) { CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure; - XP_U32 nextTimer; + TimerInfo* ti = &globals->cGlobals.timerInfo[why]; - globals->cGlobals.timerInfo[why].proc = proc; - globals->cGlobals.timerInfo[why].closure = closure; + ti->proc = proc; + ti->closure = closure; - nextTimer = util_getCurSeconds(uc) + when; - globals->cGlobals.timerInfo[why].when = nextTimer; - if ( globals->nextTimer > nextTimer ) { - globals->nextTimer = nextTimer; - } +#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 @@ -898,7 +914,7 @@ cursesListenOnSocket( CursesAppGlobals* globals, int newSock { #ifdef USE_GLIBLOOP GIOChannel* channel = g_io_channel_unix_new( newSock ); - guint watch = g_io_add_watch( channel, G_IO_IN | G_IO_OUT |G_IO_ERR, + guint watch = g_io_add_watch( channel, G_IO_IN | G_IO_ERR, func, globals ); SourceData* data = g_malloc( sizeof(*data) ); @@ -1649,9 +1665,7 @@ cursesmain( XP_Bool isServer, LaunchParams* params ) #endif #ifdef USE_GLIBLOOP - if ( params->quitAfter >= 0 ) { - cursesListenOnSocket( &g_globals, 0, handle_stdin ); /* stdin */ - } + cursesListenOnSocket( &g_globals, 0, handle_stdin ); #else cursesListenOnSocket( &g_globals, 0 ); /* stdin */ diff --git a/xwords4/linux/cursesmain.h b/xwords4/linux/cursesmain.h index e30d7a347..74c5da17b 100644 --- a/xwords4/linux/cursesmain.h +++ b/xwords4/linux/cursesmain.h @@ -92,8 +92,6 @@ struct CursesAppGlobals { struct pollfd fdArray[FD_MAX]; /* one for stdio, one for listening socket */ int timepipe[2]; /* for reading/writing "user events" */ #endif - - XP_U32 nextTimer; }; #ifdef USE_GLIBLOOP diff --git a/xwords4/linux/gtkmain.c b/xwords4/linux/gtkmain.c index eef7276ac..213401fd2 100644 --- a/xwords4/linux/gtkmain.c +++ b/xwords4/linux/gtkmain.c @@ -1709,6 +1709,15 @@ gtk_util_playerScoreHeld( XW_UtilCtxt* uc, XP_U16 player ) GtkAppGlobals* globals = (GtkAppGlobals*)uc->closure; +#if 1 + XP_USE( player ); + XWStreamCtxt* stream = + mem_stream_make( MEMPOOL globals->cGlobals.params->vtMgr, globals, + CHANNEL_NONE, catOnClose ); + server_listWordsPlayed( globals->cGlobals.game.server, stream, 1000 ); + stream_destroy( stream ); +#else + XP_UCHAR scoreExpl[48]; XP_U16 explLen = sizeof(scoreExpl); @@ -1716,6 +1725,7 @@ gtk_util_playerScoreHeld( XW_UtilCtxt* uc, XP_U16 player ) player, scoreExpl, &explLen ) ) { XP_LOGF( "got: %s", scoreExpl ); } +#endif } #endif diff --git a/xwords4/linux/main.h b/xwords4/linux/main.h index 98b031f9a..0dbac5ee5 100644 --- a/xwords4/linux/main.h +++ b/xwords4/linux/main.h @@ -138,7 +138,11 @@ typedef struct LinSMSData LinSMSData; typedef struct _TimerInfo { XWTimerProc proc; void* closure; +#ifdef USE_GLIBLOOP + struct CommonGlobals* globals; +#else XP_U32 when; /* used only for ncurses */ +#endif } TimerInfo; struct CommonGlobals {