mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2024-12-28 09:58:30 +01:00
ad6be1bb7e
what's going on when multiple participants in a single game are on the same device. But for a couple of strings passed into the jni the changes are only in DEBUG code.
2743 lines
85 KiB
C
2743 lines
85 KiB
C
/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
|
|
/*
|
|
* Copyright 2000-2013 by Eric House (xwords@eehouse.org). All rights
|
|
* reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#ifdef PLATFORM_GTK
|
|
|
|
#include <gtk/gtk.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
|
|
#include <netdb.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include <unistd.h>
|
|
#include <ctype.h>
|
|
#include <gdk/gdkkeysyms.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
|
|
#ifndef CLIENT_ONLY
|
|
/* # include <prc.h> */
|
|
#endif
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
|
|
#include "main.h"
|
|
#include "linuxmain.h"
|
|
#include "linuxutl.h"
|
|
#include "linuxbt.h"
|
|
#include "linuxudp.h"
|
|
#include "linuxsms.h"
|
|
#include "gtkmain.h"
|
|
|
|
#include "draw.h"
|
|
#include "game.h"
|
|
#include "movestak.h"
|
|
#include "gtkask.h"
|
|
#include "gtkaskm.h"
|
|
#include "gtkchat.h"
|
|
#include "gtknewgame.h"
|
|
#include "gtkletterask.h"
|
|
#include "gtkpasswdask.h"
|
|
#include "gtkntilesask.h"
|
|
#include "gtkaskdict.h"
|
|
#include "linuxdict.h"
|
|
/* #include "undo.h" */
|
|
#include "gtkdraw.h"
|
|
#include "memstream.h"
|
|
#include "filestream.h"
|
|
#include "gamesdb.h"
|
|
#include "relaycon.h"
|
|
|
|
/* static guint gtkSetupClientSocket( GtkGameGlobals* globals, int sock ); */
|
|
static void setCtrlsForTray( GtkGameGlobals* globals );
|
|
static void new_game( GtkWidget* widget, GtkGameGlobals* globals );
|
|
static XP_Bool new_game_impl( GtkGameGlobals* globals, XP_Bool fireConnDlg );
|
|
static void setZoomButtons( GtkGameGlobals* globals, XP_Bool* inOut );
|
|
static void disenable_buttons( GtkGameGlobals* globals );
|
|
static GtkWidget* addButton( GtkWidget* hbox, gchar* label, GCallback func,
|
|
GtkGameGlobals* globals );
|
|
static void handle_invite_button( GtkWidget* widget, GtkGameGlobals* globals );
|
|
|
|
#define GTK_TRAY_HT_ROWS 3
|
|
|
|
#if 0
|
|
static XWStreamCtxt*
|
|
lookupClientStream( GtkGameGlobals* globals, int sock )
|
|
{
|
|
short i;
|
|
for ( i = 0; i < MAX_NUM_PLAYERS; ++i ) {
|
|
ClientStreamRec* rec = &globals->clientRecs[i];
|
|
if ( rec->sock == sock ) {
|
|
XP_ASSERT( rec->stream != NULL );
|
|
return rec->stream;
|
|
}
|
|
}
|
|
XP_ASSERT( i < MAX_NUM_PLAYERS );
|
|
return NULL;
|
|
} /* lookupClientStream */
|
|
|
|
static void
|
|
rememberClient( GtkGameGlobals* globals, guint key, int sock,
|
|
XWStreamCtxt* stream )
|
|
{
|
|
short i;
|
|
for ( i = 0; i < MAX_NUM_PLAYERS; ++i ) {
|
|
ClientStreamRec* rec = &globals->clientRecs[i];
|
|
if ( rec->stream == NULL ) {
|
|
XP_ASSERT( stream != NULL );
|
|
rec->stream = stream;
|
|
rec->key = key;
|
|
rec->sock = sock;
|
|
break;
|
|
}
|
|
}
|
|
XP_ASSERT( i < MAX_NUM_PLAYERS );
|
|
} /* rememberClient */
|
|
#endif
|
|
|
|
static void
|
|
gtkSetAltState( GtkGameGlobals* globals, guint state )
|
|
{
|
|
globals->altKeyDown
|
|
= (state & (GDK_MOD1_MASK|GDK_SHIFT_MASK|GDK_CONTROL_MASK)) != 0;
|
|
}
|
|
|
|
static gint
|
|
button_press_event( GtkWidget* XP_UNUSED(widget), GdkEventButton *event,
|
|
GtkGameGlobals* globals )
|
|
{
|
|
XP_Bool redraw, handled;
|
|
|
|
gtkSetAltState( globals, event->state );
|
|
|
|
if ( !globals->mouseDown ) {
|
|
globals->mouseDown = XP_TRUE;
|
|
redraw = board_handlePenDown( globals->cGlobals.game.board,
|
|
event->x, event->y, &handled );
|
|
if ( redraw ) {
|
|
board_draw( globals->cGlobals.game.board );
|
|
disenable_buttons( globals );
|
|
}
|
|
}
|
|
return 1;
|
|
} /* button_press_event */
|
|
|
|
static gint
|
|
motion_notify_event( GtkWidget* XP_UNUSED(widget), GdkEventMotion *event,
|
|
GtkGameGlobals* globals )
|
|
{
|
|
XP_Bool handled;
|
|
|
|
gtkSetAltState( globals, event->state );
|
|
|
|
if ( globals->mouseDown ) {
|
|
handled = board_handlePenMove( globals->cGlobals.game.board, event->x,
|
|
event->y );
|
|
if ( handled ) {
|
|
board_draw( globals->cGlobals.game.board );
|
|
disenable_buttons( globals );
|
|
}
|
|
} else {
|
|
handled = XP_FALSE;
|
|
}
|
|
|
|
return handled;
|
|
} /* motion_notify_event */
|
|
|
|
static gint
|
|
button_release_event( GtkWidget* XP_UNUSED(widget), GdkEventMotion *event,
|
|
GtkGameGlobals* globals )
|
|
{
|
|
XP_Bool redraw;
|
|
|
|
gtkSetAltState( globals, event->state );
|
|
|
|
if ( globals->mouseDown ) {
|
|
redraw = board_handlePenUp( globals->cGlobals.game.board,
|
|
event->x,
|
|
event->y );
|
|
if ( redraw ) {
|
|
board_draw( globals->cGlobals.game.board );
|
|
disenable_buttons( globals );
|
|
}
|
|
globals->mouseDown = XP_FALSE;
|
|
}
|
|
return 1;
|
|
} /* button_release_event */
|
|
|
|
#ifdef KEY_SUPPORT
|
|
static XP_Key
|
|
evtToXPKey( GdkEventKey* event, XP_Bool* movesCursorP )
|
|
{
|
|
XP_Key xpkey = XP_KEY_NONE;
|
|
XP_Bool movesCursor = XP_FALSE;
|
|
guint keyval = event->keyval;
|
|
|
|
switch( keyval ) {
|
|
#ifdef KEYBOARD_NAV
|
|
case GDK_Return:
|
|
xpkey = XP_RETURN_KEY;
|
|
break;
|
|
case GDK_space:
|
|
xpkey = XP_RAISEFOCUS_KEY;
|
|
break;
|
|
|
|
case GDK_Left:
|
|
xpkey = XP_CURSOR_KEY_LEFT;
|
|
movesCursor = XP_TRUE;
|
|
break;
|
|
case GDK_Right:
|
|
xpkey = XP_CURSOR_KEY_RIGHT;
|
|
movesCursor = XP_TRUE;
|
|
break;
|
|
case GDK_Up:
|
|
xpkey = XP_CURSOR_KEY_UP;
|
|
movesCursor = XP_TRUE;
|
|
break;
|
|
case GDK_Down:
|
|
xpkey = XP_CURSOR_KEY_DOWN;
|
|
movesCursor = XP_TRUE;
|
|
break;
|
|
#endif
|
|
case GDK_BackSpace:
|
|
XP_LOGF( "... it's a DEL" );
|
|
xpkey = XP_CURSOR_KEY_DEL;
|
|
break;
|
|
default:
|
|
keyval = keyval & 0x00FF; /* mask out gtk stuff */
|
|
if ( isalpha( keyval ) ) {
|
|
xpkey = toupper(keyval);
|
|
break;
|
|
#ifdef NUMBER_KEY_AS_INDEX
|
|
} else if ( isdigit( keyval ) ) {
|
|
xpkey = keyval;
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
*movesCursorP = movesCursor;
|
|
return xpkey;
|
|
} /* evtToXPKey */
|
|
|
|
#ifdef KEYBOARD_NAV
|
|
static gint
|
|
key_press_event( GtkWidget* XP_UNUSED(widget), GdkEventKey* event,
|
|
GtkGameGlobals* globals )
|
|
{
|
|
XP_Bool handled = XP_FALSE;
|
|
XP_Bool movesCursor;
|
|
XP_Key xpkey = evtToXPKey( event, &movesCursor );
|
|
|
|
gtkSetAltState( globals, event->state );
|
|
|
|
if ( xpkey != XP_KEY_NONE ) {
|
|
XP_Bool draw = globals->keyDown ?
|
|
board_handleKeyRepeat( globals->cGlobals.game.board, xpkey,
|
|
&handled )
|
|
: board_handleKeyDown( globals->cGlobals.game.board, xpkey,
|
|
&handled );
|
|
if ( draw ) {
|
|
board_draw( globals->cGlobals.game.board );
|
|
}
|
|
}
|
|
globals->keyDown = XP_TRUE;
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
static gint
|
|
key_release_event( GtkWidget* XP_UNUSED(widget), GdkEventKey* event,
|
|
GtkGameGlobals* globals )
|
|
{
|
|
XP_Bool handled = XP_FALSE;
|
|
XP_Bool movesCursor;
|
|
XP_Key xpkey = evtToXPKey( event, &movesCursor );
|
|
|
|
gtkSetAltState( globals, event->state );
|
|
|
|
if ( xpkey != XP_KEY_NONE ) {
|
|
XP_Bool draw;
|
|
draw = board_handleKeyUp( globals->cGlobals.game.board, xpkey,
|
|
&handled );
|
|
#ifdef KEYBOARD_NAV
|
|
if ( movesCursor && !handled ) {
|
|
BoardObjectType order[] = { OBJ_SCORE, OBJ_BOARD, OBJ_TRAY };
|
|
draw = linShiftFocus( &globals->cGlobals, xpkey, order,
|
|
NULL ) || draw;
|
|
}
|
|
#endif
|
|
if ( draw ) {
|
|
board_draw( globals->cGlobals.game.board );
|
|
}
|
|
}
|
|
|
|
/* XP_ASSERT( globals->keyDown ); */
|
|
#ifdef KEYBOARD_NAV
|
|
globals->keyDown = XP_FALSE;
|
|
#endif
|
|
|
|
return handled? 1 : 0; /* gtk will do something with the key if 0
|
|
returned */
|
|
} /* key_release_event */
|
|
#endif
|
|
|
|
#ifdef MEM_DEBUG
|
|
# define MEMPOOL globals->cGlobals.util->mpool,
|
|
#else
|
|
# define MEMPOOL
|
|
#endif
|
|
|
|
static void
|
|
relay_status_gtk( void* closure, CommsRelayState state )
|
|
{
|
|
XP_LOGF( "%s got status: %s", __func__, CommsRelayState2Str(state) );
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)closure;
|
|
if ( !!globals->draw ) {
|
|
globals->cGlobals.state = state;
|
|
globals->stateChar = 'A' + COMMS_RELAYSTATE_ALLCONNECTED - state;
|
|
draw_gtk_status( globals->draw, globals->stateChar );
|
|
}
|
|
}
|
|
|
|
static void
|
|
relay_connd_gtk( void* closure, XP_UCHAR* const room,
|
|
XP_Bool XP_UNUSED(reconnect), XP_U16 devOrder,
|
|
XP_Bool allHere, XP_U16 nMissing )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)closure;
|
|
globals->cGlobals.nMissing = nMissing;
|
|
XP_Bool skip = XP_FALSE;
|
|
char buf[256];
|
|
|
|
if ( allHere ) {
|
|
snprintf( buf, sizeof(buf),
|
|
"All expected players have joined in %s. Play!", room );
|
|
} else {
|
|
if ( nMissing > 0 ) {
|
|
snprintf( buf, sizeof(buf), "%dth connected to relay; waiting "
|
|
"in %s for %d player[s].", devOrder, room, nMissing );
|
|
} else {
|
|
/* an allHere message should be coming immediately, so no
|
|
notification now. */
|
|
skip = XP_TRUE;
|
|
}
|
|
}
|
|
|
|
if ( !skip ) {
|
|
(void)gtkask_timeout( globals->window, buf, GTK_BUTTONS_OK, NULL, 500 );
|
|
}
|
|
|
|
disenable_buttons( globals );
|
|
}
|
|
|
|
static gint
|
|
invoke_new_game( gpointer data )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)data;
|
|
new_game_impl( globals, XP_FALSE );
|
|
return 0;
|
|
}
|
|
|
|
static gint
|
|
invoke_new_game_conns( gpointer data )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)data;
|
|
new_game_impl( globals, XP_TRUE );
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
relay_error_gtk( void* closure, XWREASON relayErr )
|
|
{
|
|
LOG_FUNC();
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)closure;
|
|
|
|
gint (*proc)( gpointer data ) = NULL;
|
|
switch( relayErr ) {
|
|
case XWRELAY_ERROR_NO_ROOM:
|
|
case XWRELAY_ERROR_DUP_ROOM:
|
|
proc = invoke_new_game_conns;
|
|
break;
|
|
case XWRELAY_ERROR_TOO_MANY:
|
|
case XWRELAY_ERROR_BADPROTO:
|
|
proc = invoke_new_game;
|
|
break;
|
|
case XWRELAY_ERROR_DELETED:
|
|
(void)gtkask_timeout( globals->window,
|
|
"relay says another device deleted game.",
|
|
GTK_BUTTONS_OK, NULL, 1000 );
|
|
break;
|
|
case XWRELAY_ERROR_DEADGAME:
|
|
break;
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
|
|
if ( !!proc ) {
|
|
(void)g_idle_add( proc, globals );
|
|
}
|
|
}
|
|
|
|
static XP_Bool
|
|
relay_sendNoConn_gtk( const XP_U8* msg, XP_U16 len, const XP_UCHAR* relayID,
|
|
void* closure )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)closure;
|
|
XP_Bool success = XP_FALSE;
|
|
LaunchParams* params = globals->cGlobals.params;
|
|
if ( params->useUdp && !globals->draw ) {
|
|
XP_U16 seed = comms_getChannelSeed( globals->cGlobals.game.comms );
|
|
XP_U32 clientToken = makeClientToken( globals->cGlobals.selRow, seed );
|
|
XP_S16 nSent = relaycon_sendnoconn( params, msg, len, relayID,
|
|
clientToken );
|
|
success = nSent == len;
|
|
}
|
|
return success;
|
|
} /* relay_sendNoConn_gtk */
|
|
|
|
#ifdef COMMS_XPORT_FLAGSPROC
|
|
static XP_U32
|
|
gtk_getFlags( void* closure )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)closure;
|
|
return (!!globals->draw) ? COMMS_XPORT_FLAGS_NONE
|
|
: COMMS_XPORT_FLAGS_HASNOCONN;
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
setTransportProcs( TransportProcs* procs, GtkGameGlobals* globals )
|
|
{
|
|
procs->closure = globals;
|
|
procs->send = LINUX_SEND;
|
|
#ifdef COMMS_XPORT_FLAGSPROC
|
|
procs->getFlags = gtk_getFlags;
|
|
#endif
|
|
#ifdef COMMS_HEARTBEAT
|
|
procs->reset = linux_reset;
|
|
#endif
|
|
#ifdef XWFEATURE_RELAY
|
|
procs->rstatus = relay_status_gtk;
|
|
procs->rconnd = relay_connd_gtk;
|
|
procs->rerror = relay_error_gtk;
|
|
procs->sendNoConn = relay_sendNoConn_gtk;
|
|
#endif
|
|
}
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
# ifdef DEBUG
|
|
static void
|
|
drop_msg_toggle( GtkWidget* toggle, void* data )
|
|
{
|
|
XP_Bool disabled = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(toggle) );
|
|
long asInt = (long)data;
|
|
XP_Bool send = 0 != (asInt & 1);
|
|
asInt &= ~1;
|
|
DropTypeData* datum = (DropTypeData*)asInt;
|
|
comms_setAddrDisabled( datum->comms, datum->typ, send, disabled );
|
|
} /* drop_msg_toggle */
|
|
|
|
static void
|
|
addDropChecks( GtkGameGlobals* globals )
|
|
{
|
|
CommsCtxt* comms = globals->cGlobals.game.comms;
|
|
if ( !!comms ) {
|
|
CommsAddrRec addr;
|
|
comms_getAddr( comms, &addr );
|
|
CommsConnType typ;
|
|
for ( XP_U32 st = 0; addr_iter( &addr, &typ, &st ); ) {
|
|
DropTypeData* datum = &globals->dropData[typ];
|
|
datum->typ = typ;
|
|
datum->comms = comms;
|
|
|
|
GtkWidget* hbox = gtk_hbox_new( FALSE, 0 );
|
|
gchar buf[32];
|
|
snprintf( buf, sizeof(buf), "Drop %s messages",
|
|
ConnType2Str( typ ) );
|
|
GtkWidget* widget = gtk_label_new( buf );
|
|
gtk_box_pack_start( GTK_BOX(hbox), widget, FALSE, TRUE, 0);
|
|
gtk_widget_show( widget );
|
|
|
|
widget = gtk_check_button_new_with_label( "Incoming" );
|
|
if ( comms_getAddrDisabled( comms, typ, XP_FALSE ) ) {
|
|
gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(widget), TRUE );
|
|
}
|
|
g_signal_connect( GTK_OBJECT(widget), "toggled", G_CALLBACK(drop_msg_toggle),
|
|
datum );
|
|
gtk_box_pack_start( GTK_BOX(hbox), widget, FALSE, TRUE, 0);
|
|
gtk_widget_show( widget );
|
|
|
|
widget = gtk_check_button_new_with_label( "Outgoing" );
|
|
if ( comms_getAddrDisabled( comms, typ, XP_TRUE ) ) {
|
|
gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(widget), TRUE );
|
|
}
|
|
g_signal_connect( GTK_OBJECT(widget), "toggled", G_CALLBACK(drop_msg_toggle),
|
|
(void*)(((long)datum) | 1) );
|
|
gtk_box_pack_start( GTK_BOX(hbox), widget, FALSE, TRUE, 0);
|
|
gtk_widget_show( widget );
|
|
|
|
gtk_widget_show( hbox );
|
|
|
|
gtk_box_pack_start( GTK_BOX(globals->drop_checks_vbox), hbox, FALSE, TRUE, 0);
|
|
}
|
|
gtk_widget_show(globals->drop_checks_vbox);
|
|
}
|
|
}
|
|
# else
|
|
# define addDropChecks( globals )
|
|
# endif /* DEBUG */
|
|
#endif
|
|
|
|
static void
|
|
createOrLoadObjects( GtkGameGlobals* globals )
|
|
{
|
|
XWStreamCtxt* stream = NULL;
|
|
XP_Bool opened = XP_FALSE;
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
#endif
|
|
CommonGlobals* cGlobals = &globals->cGlobals;
|
|
LaunchParams* params = cGlobals->params;
|
|
|
|
globals->draw = (GtkDrawCtx*)gtkDrawCtxtMake( globals->drawing_area,
|
|
globals );
|
|
|
|
TransportProcs procs;
|
|
setTransportProcs( &procs, globals );
|
|
|
|
if ( !!params->fileName && file_exists( params->fileName ) ) {
|
|
stream = streamFromFile( cGlobals, params->fileName, globals );
|
|
#ifdef USE_SQLITE
|
|
} else if ( !!params->dbFileName && file_exists( params->dbFileName ) ) {
|
|
XP_UCHAR buf[32];
|
|
XP_SNPRINTF( buf, sizeof(buf), "%d", params->dbFileID );
|
|
mpool_setTag( MEMPOOL buf );
|
|
stream = streamFromDB( cGlobals, globals );
|
|
#endif
|
|
} else if ( !!cGlobals->pDb && 0 <= cGlobals->selRow ) {
|
|
stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
|
|
params->vtMgr,
|
|
cGlobals, CHANNEL_NONE, NULL );
|
|
if ( !loadGame( stream, cGlobals->pDb, cGlobals->selRow ) ) {
|
|
stream_destroy( stream );
|
|
stream = NULL;
|
|
}
|
|
}
|
|
|
|
if ( !!stream ) {
|
|
if ( NULL == cGlobals->dict ) {
|
|
cGlobals->dict = makeDictForStream( cGlobals, stream );
|
|
}
|
|
|
|
opened = game_makeFromStream( MEMPOOL stream, &cGlobals->game,
|
|
cGlobals->gi, cGlobals->dict,
|
|
&cGlobals->dicts, cGlobals->util,
|
|
(DrawCtx*)globals->draw,
|
|
&cGlobals->cp, &procs );
|
|
XP_LOGF( "%s: loaded gi at %p", __func__, &cGlobals->gi );
|
|
stream_destroy( stream );
|
|
}
|
|
|
|
if ( !opened ) {
|
|
CommsAddrRec addr = cGlobals->addr;
|
|
|
|
/* XP_MEMSET( &addr, 0, sizeof(addr) ); */
|
|
/* addr.conType = cGlobals->addr.conType; */
|
|
|
|
#ifdef XWFEATURE_RELAY
|
|
/* if ( addr.conType == COMMS_CONN_RELAY ) { */
|
|
/* XP_ASSERT( !!params->connInfo.relay.relayName ); */
|
|
/* globals->cGlobals.defaultServerName */
|
|
/* = params->connInfo.relay.relayName; */
|
|
/* } */
|
|
#endif
|
|
game_makeNewGame( MEMPOOL &cGlobals->game, cGlobals->gi,
|
|
cGlobals->util, (DrawCtx*)globals->draw,
|
|
&cGlobals->cp, &procs, params->gameSeed );
|
|
|
|
// addr.conType = params->conType;
|
|
CommsConnType typ;
|
|
for ( XP_U32 st = 0; addr_iter( &addr, &typ, &st ); ) {
|
|
if ( params->commsDisableds[typ][0] ) {
|
|
comms_setAddrDisabled( cGlobals->game.comms, typ, XP_FALSE, XP_TRUE );
|
|
}
|
|
if ( params->commsDisableds[typ][1] ) {
|
|
comms_setAddrDisabled( cGlobals->game.comms, typ, XP_TRUE, XP_TRUE );
|
|
}
|
|
switch( typ ) {
|
|
#ifdef XWFEATURE_RELAY
|
|
case COMMS_CONN_RELAY:
|
|
/* addr.u.ip_relay.ipAddr = 0; */
|
|
/* addr.u.ip_relay.port = params->connInfo.relay.defaultSendPort; */
|
|
/* addr.u.ip_relay.seeksPublicRoom = params->connInfo.relay.seeksPublicRoom; */
|
|
/* addr.u.ip_relay.advertiseRoom = params->connInfo.relay.advertiseRoom; */
|
|
/* XP_STRNCPY( addr.u.ip_relay.hostName, params->connInfo.relay.relayName, */
|
|
/* sizeof(addr.u.ip_relay.hostName) - 1 ); */
|
|
/* XP_STRNCPY( addr.u.ip_relay.invite, params->connInfo.relay.invite, */
|
|
/* sizeof(addr.u.ip_relay.invite) - 1 ); */
|
|
break;
|
|
#endif
|
|
#ifdef XWFEATURE_BLUETOOTH
|
|
case COMMS_CONN_BT:
|
|
XP_ASSERT( sizeof(addr.u.bt.btAddr)
|
|
>= sizeof(params->connInfo.bt.hostAddr));
|
|
XP_MEMCPY( &addr.u.bt.btAddr, ¶ms->connInfo.bt.hostAddr,
|
|
sizeof(params->connInfo.bt.hostAddr) );
|
|
break;
|
|
#endif
|
|
#ifdef XWFEATURE_IP_DIRECT
|
|
case COMMS_CONN_IP_DIRECT:
|
|
XP_STRNCPY( addr.u.ip.hostName_ip, params->connInfo.ip.hostName,
|
|
sizeof(addr.u.ip.hostName_ip) - 1 );
|
|
addr.u.ip.port_ip = params->connInfo.ip.port;
|
|
break;
|
|
#endif
|
|
#ifdef XWFEATURE_SMS
|
|
case COMMS_CONN_SMS:
|
|
/* No! Don't overwrite what may be a return address with local
|
|
stuff */
|
|
/* XP_STRNCPY( addr.u.sms.phone, params->connInfo.sms.phone, */
|
|
/* sizeof(addr.u.sms.phone) - 1 ); */
|
|
/* addr.u.sms.port = params->connInfo.sms.port; */
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Need to save in order to have a valid selRow for the first send */
|
|
saveGame( cGlobals );
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
/* This may trigger network activity */
|
|
if ( !!cGlobals->game.comms ) {
|
|
comms_setAddr( cGlobals->game.comms, &addr );
|
|
}
|
|
#endif
|
|
model_setDictionary( cGlobals->game.model, cGlobals->dict );
|
|
setSquareBonuses( cGlobals );
|
|
model_setPlayerDicts( cGlobals->game.model, &cGlobals->dicts );
|
|
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
|
cGlobals->gi->allowHintRect = params->allowHintRect;
|
|
#endif
|
|
|
|
if ( params->needsNewGame ) {
|
|
new_game_impl( globals, XP_FALSE );
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
} else {
|
|
DeviceRole serverRole = cGlobals->gi->serverRole;
|
|
if ( serverRole == SERVER_ISCLIENT ) {
|
|
XWStreamCtxt* stream =
|
|
mem_stream_make( MEMPOOL params->vtMgr,
|
|
cGlobals, CHANNEL_NONE,
|
|
sendOnClose );
|
|
(void)server_initClientConnection( cGlobals->game.server,
|
|
stream );
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if ( !params->fileName && !!params->dbName ) {
|
|
XP_UCHAR buf[64];
|
|
snprintf( buf, sizeof(buf), "%s / %lld", params->dbName,
|
|
cGlobals->selRow );
|
|
gtk_window_set_title( GTK_WINDOW(globals->window), buf );
|
|
}
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
if ( !!globals->cGlobals.game.comms ) {
|
|
comms_start( globals->cGlobals.game.comms );
|
|
}
|
|
#endif
|
|
server_do( globals->cGlobals.game.server );
|
|
saveGame( cGlobals ); /* again, to include address etc. */
|
|
|
|
addDropChecks( globals );
|
|
disenable_buttons( globals );
|
|
} /* createOrLoadObjects */
|
|
|
|
/* Create a new backing pixmap of the appropriate size and set up contxt to
|
|
* draw using that size.
|
|
*/
|
|
static gboolean
|
|
configure_event( GtkWidget* widget, GdkEventConfigure* XP_UNUSED(event),
|
|
GtkGameGlobals* globals )
|
|
{
|
|
if ( globals->draw == NULL ) {
|
|
createOrLoadObjects( globals );
|
|
}
|
|
|
|
CommonGlobals* cGlobals = &globals->cGlobals;
|
|
BoardCtxt* board = cGlobals->game.board;
|
|
|
|
short bdWidth = widget->allocation.width - (GTK_RIGHT_MARGIN
|
|
+ GTK_BOARD_LEFT_MARGIN);
|
|
short bdHeight = widget->allocation.height - (GTK_TOP_MARGIN + GTK_BOTTOM_MARGIN)
|
|
- GTK_MIN_TRAY_SCALEV - GTK_BOTTOM_MARGIN;
|
|
|
|
#ifdef COMMON_LAYOUT
|
|
XP_ASSERT( !cGlobals->params->verticalScore ); /* not supported */
|
|
|
|
BoardDims dims;
|
|
board_figureLayout( board, cGlobals->gi,
|
|
GTK_BOARD_LEFT, GTK_HOR_SCORE_TOP, bdWidth, bdHeight,
|
|
#if 1
|
|
150, 200,
|
|
#else
|
|
0, 0,
|
|
#endif
|
|
bdWidth-25, 16, 16,
|
|
XP_FALSE, &dims );
|
|
board_applyLayout( board, &dims );
|
|
|
|
#else
|
|
short timerLeft, timerTop;
|
|
gint hscale, vscale;
|
|
gint trayTop;
|
|
gint boardTop = 0;
|
|
XP_U16 netStatWidth = 0;
|
|
gint nCols;
|
|
gint nRows;
|
|
|
|
nCols = cGlobals->gi->boardSize;
|
|
nRows = nCols;
|
|
if ( cGlobals->params->verticalScore ) {
|
|
bdWidth -= GTK_VERT_SCORE_WIDTH;
|
|
}
|
|
|
|
hscale = bdWidth / nCols;
|
|
if ( 0 != cGlobals->params->nHidden ) {
|
|
vscale = hscale;
|
|
} else {
|
|
vscale = (bdHeight / (nCols + GTK_TRAY_HT_ROWS)); /* makd tray height
|
|
3x cell height */
|
|
}
|
|
|
|
if ( !cGlobals->params->verticalScore ) {
|
|
boardTop += GTK_HOR_SCORE_HEIGHT;
|
|
}
|
|
|
|
trayTop = boardTop + (vscale * nRows);
|
|
/* move tray up if part of board's meant to be hidden */
|
|
trayTop -= vscale * cGlobals->params->nHidden;
|
|
board_setPos( board, GTK_BOARD_LEFT, boardTop,
|
|
hscale * nCols, vscale * nRows, hscale * 4, XP_FALSE );
|
|
/* board_setScale( board, hscale, vscale ); */
|
|
|
|
if ( !!cGlobals->game.comms ) {
|
|
netStatWidth = GTK_NETSTAT_WIDTH;
|
|
}
|
|
|
|
timerTop = GTK_TIMER_TOP;
|
|
if ( cGlobals->params->verticalScore ) {
|
|
timerLeft = GTK_BOARD_LEFT + (hscale*nCols) + 1;
|
|
board_setScoreboardLoc( board,
|
|
timerLeft,
|
|
GTK_VERT_SCORE_TOP,
|
|
GTK_VERT_SCORE_WIDTH,
|
|
vscale*nCols,
|
|
XP_FALSE );
|
|
|
|
} else {
|
|
timerLeft = GTK_BOARD_LEFT + (hscale*nCols)
|
|
- GTK_TIMER_WIDTH - netStatWidth;
|
|
board_setScoreboardLoc( board,
|
|
GTK_BOARD_LEFT, GTK_HOR_SCORE_TOP,
|
|
timerLeft-GTK_BOARD_LEFT,
|
|
GTK_HOR_SCORE_HEIGHT,
|
|
XP_TRUE );
|
|
|
|
}
|
|
|
|
/* Still pending: do this for the vertical score case */
|
|
if ( cGlobals->game.comms ) {
|
|
globals->netStatLeft = timerLeft + GTK_TIMER_WIDTH;
|
|
globals->netStatTop = 0;
|
|
}
|
|
|
|
board_setTimerLoc( board, timerLeft, timerTop,
|
|
GTK_TIMER_WIDTH, GTK_HOR_SCORE_HEIGHT );
|
|
|
|
board_setTrayLoc( board, GTK_TRAY_LEFT, trayTop,
|
|
hscale * nCols, vscale * GTK_TRAY_HT_ROWS + 10,
|
|
GTK_DIVIDER_WIDTH );
|
|
|
|
#endif
|
|
globals->gridOn = XP_TRUE;
|
|
|
|
setCtrlsForTray( globals );
|
|
board_invalAll( board );
|
|
|
|
XP_Bool inOut[2];
|
|
board_zoom( board, 0, inOut );
|
|
setZoomButtons( globals, inOut );
|
|
|
|
return TRUE;
|
|
} /* configure_event */
|
|
|
|
/* Redraw the screen from the backing pixmap */
|
|
static gint
|
|
expose_event( GtkWidget* XP_UNUSED(widget),
|
|
GdkEventExpose* XP_UNUSED(event),
|
|
GtkGameGlobals* globals )
|
|
{
|
|
/*
|
|
gdk_draw_rectangle( widget->window,//((GtkDrawCtx*)globals->draw)->pixmap,
|
|
widget->style->white_gc,
|
|
TRUE,
|
|
0, 0,
|
|
widget->allocation.width,
|
|
widget->allocation.height+widget->allocation.y );
|
|
*/
|
|
/* I want to inval only the area that's exposed, but the rect is always
|
|
empty, even when clearly shouldn't be. Need to investigate. Until
|
|
fixed, use board_invalAll to ensure board is drawn.*/
|
|
/* board_invalRect( globals->cGlobals.game.board, (XP_Rect*)&event->area ); */
|
|
|
|
board_invalAll( globals->cGlobals.game.board );
|
|
board_draw( globals->cGlobals.game.board );
|
|
draw_gtk_status( globals->draw, globals->stateChar );
|
|
|
|
/* gdk_draw_pixmap( widget->window, */
|
|
/* widget->style->fg_gc[GTK_WIDGET_STATE (widget)], */
|
|
/* ((GtkDrawCtx*)globals->draw)->pixmap, */
|
|
/* event->area.x, event->area.y, */
|
|
/* event->area.x, event->area.y, */
|
|
/* event->area.width, event->area.height ); */
|
|
|
|
return FALSE;
|
|
} /* expose_event */
|
|
|
|
#if 0
|
|
static gint
|
|
handle_client_event( GtkWidget *widget, GdkEventClient *event,
|
|
GtkGameGlobals* globals )
|
|
{
|
|
XP_LOGF( "handle_client_event called: event->type = " );
|
|
if ( event->type == GDK_CLIENT_EVENT ) {
|
|
XP_LOGF( "GDK_CLIENT_EVENT" );
|
|
return 1;
|
|
} else {
|
|
XP_LOGF( "%d", event->type );
|
|
return 0;
|
|
}
|
|
} /* handle_client_event */
|
|
#endif
|
|
|
|
void
|
|
destroy_board_window( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
LOG_FUNC();
|
|
if ( !!globals->cGlobals.game.comms ) {
|
|
comms_stop( globals->cGlobals.game.comms );
|
|
}
|
|
saveGame( &globals->cGlobals );
|
|
windowDestroyed( globals );
|
|
// gtk_main_quit();
|
|
}
|
|
|
|
static void
|
|
cleanup( GtkGameGlobals* globals )
|
|
{
|
|
CommonGlobals* cGlobals = &globals->cGlobals;
|
|
saveGame( cGlobals );
|
|
g_source_remove( globals->idleID );
|
|
|
|
#ifdef XWFEATURE_BLUETOOTH
|
|
linux_bt_close( cGlobals );
|
|
#endif
|
|
#ifdef XWFEATURE_SMS
|
|
// linux_sms_close( cGlobals );
|
|
#endif
|
|
#ifdef XWFEATURE_IP_DIRECT
|
|
linux_udp_close( cGlobals );
|
|
#endif
|
|
#ifdef XWFEATURE_RELAY
|
|
linux_close_socket( cGlobals );
|
|
#endif
|
|
game_dispose( &cGlobals->game );
|
|
gi_disposePlayerInfo( MEMPOOL cGlobals->gi );
|
|
dict_unref( cGlobals->dict );
|
|
|
|
linux_util_vt_destroy( cGlobals->util );
|
|
free( cGlobals->util );
|
|
} /* cleanup */
|
|
|
|
GtkWidget*
|
|
makeAddSubmenu( GtkWidget* menubar, gchar* label )
|
|
{
|
|
GtkWidget* submenu;
|
|
GtkWidget* item;
|
|
|
|
item = gtk_menu_item_new_with_label( label );
|
|
gtk_menu_bar_append( GTK_MENU_BAR(menubar), item );
|
|
|
|
submenu = gtk_menu_new();
|
|
gtk_menu_item_set_submenu( GTK_MENU_ITEM(item), submenu );
|
|
|
|
gtk_widget_show(item);
|
|
|
|
return submenu;
|
|
} /* makeAddSubmenu */
|
|
|
|
static void
|
|
tile_values( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
CommonGlobals* cGlobals = &globals->cGlobals;
|
|
if ( !!cGlobals->game.server ) {
|
|
XWStreamCtxt* stream =
|
|
mem_stream_make( MEMPOOL
|
|
cGlobals->params->vtMgr,
|
|
globals,
|
|
CHANNEL_NONE,
|
|
catOnClose );
|
|
server_formatDictCounts( cGlobals->game.server, stream, 5 );
|
|
stream_putU8( stream, '\n' );
|
|
stream_destroy( stream );
|
|
}
|
|
|
|
} /* tile_values */
|
|
|
|
static void
|
|
game_history( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
catGameHistory( &globals->cGlobals );
|
|
} /* game_history */
|
|
|
|
#ifdef TEXT_MODEL
|
|
static void
|
|
dump_board( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
if ( !!globals->cGlobals.game.model ) {
|
|
XWStreamCtxt* stream =
|
|
mem_stream_make( MEMPOOL
|
|
globals->cGlobals.params->vtMgr,
|
|
globals,
|
|
CHANNEL_NONE,
|
|
catOnClose );
|
|
model_writeToTextStream( globals->cGlobals.game.model, stream );
|
|
stream_destroy( stream );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
final_scores( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
XP_Bool gameOver = server_getGameIsOver( globals->cGlobals.game.server );
|
|
|
|
if ( gameOver ) {
|
|
catFinalScores( &globals->cGlobals, -1 );
|
|
} else if ( GTK_RESPONSE_YES == gtkask( globals->window,
|
|
"Are you sure you want to resign?",
|
|
GTK_BUTTONS_YES_NO, NULL ) ) {
|
|
globals->cGlobals.manualFinal = XP_TRUE;
|
|
server_endGame( globals->cGlobals.game.server );
|
|
gameOver = TRUE;
|
|
}
|
|
|
|
/* the end game listener will take care of printing the final scores */
|
|
} /* final_scores */
|
|
|
|
static XP_Bool
|
|
new_game_impl( GtkGameGlobals* globals, XP_Bool fireConnDlg )
|
|
{
|
|
XP_Bool success = XP_FALSE;
|
|
CommsAddrRec addr;
|
|
|
|
if ( !!globals->cGlobals.game.comms ) {
|
|
comms_getAddr( globals->cGlobals.game.comms, &addr );
|
|
} else {
|
|
comms_getInitialAddr( &addr, RELAY_NAME_DEFAULT, RELAY_PORT_DEFAULT );
|
|
}
|
|
|
|
CurGameInfo* gi = globals->cGlobals.gi;
|
|
success = newGameDialog( globals, gi, &addr, XP_TRUE, fireConnDlg );
|
|
if ( success ) {
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
XP_Bool isClient = gi->serverRole == SERVER_ISCLIENT;
|
|
#endif
|
|
TransportProcs procs = {
|
|
.closure = globals,
|
|
.send = LINUX_SEND,
|
|
#ifdef COMMS_HEARTBEAT
|
|
.reset = linux_reset,
|
|
#endif
|
|
};
|
|
|
|
if ( !game_reset( MEMPOOL &globals->cGlobals.game, gi,
|
|
globals->cGlobals.util,
|
|
&globals->cGlobals.cp, &procs ) ) {
|
|
/* if ( NULL == globals->draw ) { */
|
|
/* globals->draw = (GtkDrawCtx*)gtkDrawCtxtMake( globals->drawing_area, */
|
|
/* globals ); */
|
|
/* } */
|
|
/* game_makeNewGame( MEMPOOL &globals->cGlobals.game, gi, */
|
|
/* globals->cGlobals.params->util, */
|
|
/* (DrawCtx*)globals->draw, */
|
|
/* &globals->cGlobals.cp, &procs, */
|
|
/* globals->cGlobals.params->gameSeed ); */
|
|
/* ModelCtxt* model = globals->cGlobals.game.model; */
|
|
/* if ( NULL == model_getDictionary( model ) ) { */
|
|
/* DictionaryCtxt* dict = */
|
|
/* linux_dictionary_make( MEMPOOL globals->cGlobals.params, */
|
|
/* gi->dictName, XP_TRUE ); */
|
|
/* model_setDictionary( model, dict ); */
|
|
/* } */
|
|
}
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
if ( !!globals->cGlobals.game.comms ) {
|
|
comms_setAddr( globals->cGlobals.game.comms, &addr );
|
|
} else if ( gi->serverRole != SERVER_STANDALONE ) {
|
|
XP_ASSERT(0);
|
|
}
|
|
|
|
if ( isClient ) {
|
|
XWStreamCtxt* stream =
|
|
mem_stream_make( MEMPOOL globals->cGlobals.params->vtMgr,
|
|
&globals->cGlobals, CHANNEL_NONE,
|
|
sendOnClose );
|
|
(void)server_initClientConnection( globals->cGlobals.game.server,
|
|
stream );
|
|
}
|
|
#endif
|
|
(void)server_do( globals->cGlobals.game.server ); /* assign tiles, etc. */
|
|
board_invalAll( globals->cGlobals.game.board );
|
|
board_draw( globals->cGlobals.game.board );
|
|
}
|
|
return success;
|
|
} /* new_game_impl */
|
|
|
|
static void
|
|
new_game( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
new_game_impl( globals, XP_FALSE );
|
|
} /* new_game */
|
|
|
|
static void
|
|
game_info( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
CommsAddrRec addr;
|
|
comms_getAddr( globals->cGlobals.game.comms, &addr );
|
|
|
|
/* Anything to do if OK is clicked? Changed names etc. already saved. Try
|
|
server_do in case one's become a robot. */
|
|
CurGameInfo* gi = globals->cGlobals.gi;
|
|
if ( newGameDialog( globals, gi, &addr, XP_FALSE, XP_FALSE ) ) {
|
|
if ( server_do( globals->cGlobals.game.server ) ) {
|
|
board_draw( globals->cGlobals.game.board );
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
load_game( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* XP_UNUSED(globals) )
|
|
{
|
|
XP_ASSERT(0);
|
|
} /* load_game */
|
|
|
|
static void
|
|
save_game( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* XP_UNUSED(globals) )
|
|
{
|
|
XP_ASSERT(0);
|
|
} /* save_game */
|
|
|
|
#ifdef XWFEATURE_CHANGEDICT
|
|
static void
|
|
change_dictionary( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
CommonGlobals* cGlobals = &globals->cGlobals;
|
|
LaunchParams* params = cGlobals->params;
|
|
GSList* dicts = listDicts( params );
|
|
gchar buf[265];
|
|
gchar* name = gtkaskdict( dicts, buf, VSIZE(buf) );
|
|
if ( !!name ) {
|
|
DictionaryCtxt* dict =
|
|
linux_dictionary_make( MPPARM(cGlobals->util->mpool) params, name,
|
|
params->useMmap );
|
|
game_changeDict( MPPARM(cGlobals->util->mpool) &cGlobals->game,
|
|
cGlobals->gi, dict );
|
|
}
|
|
g_slist_free( dicts );
|
|
} /* change_dictionary */
|
|
#endif
|
|
|
|
static void
|
|
handle_undo( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* XP_UNUSED(globals) )
|
|
{
|
|
} /* handle_undo */
|
|
|
|
static void
|
|
handle_redo( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* XP_UNUSED(globals) )
|
|
{
|
|
} /* handle_redo */
|
|
|
|
#ifdef FEATURE_TRAY_EDIT
|
|
static void
|
|
handle_trayEditToggle( GtkWidget* XP_UNUSED(widget),
|
|
GtkGameGlobals* XP_UNUSED(globals),
|
|
XP_Bool XP_UNUSED(on) )
|
|
{
|
|
} /* handle_trayEditToggle */
|
|
|
|
static void
|
|
handle_trayEditToggle_on( GtkWidget* widget, GtkGameGlobals* globals )
|
|
{
|
|
handle_trayEditToggle( widget, globals, XP_TRUE );
|
|
}
|
|
|
|
static void
|
|
handle_trayEditToggle_off( GtkWidget* widget, GtkGameGlobals* globals )
|
|
{
|
|
handle_trayEditToggle( widget, globals, XP_FALSE );
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
handle_trade_cancel( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
BoardCtxt* board = globals->cGlobals.game.board;
|
|
if ( board_endTrade( board ) ) {
|
|
board_draw( board );
|
|
}
|
|
}
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
static void
|
|
handle_resend( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
CommsCtxt* comms = globals->cGlobals.game.comms;
|
|
if ( comms != NULL ) {
|
|
comms_resendAll( comms, XP_TRUE );
|
|
}
|
|
} /* handle_resend */
|
|
|
|
#ifdef XWFEATURE_COMMSACK
|
|
static void
|
|
handle_ack( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
CommsCtxt* comms = globals->cGlobals.game.comms;
|
|
if ( comms != NULL ) {
|
|
comms_ackAny( comms );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
static void
|
|
handle_commstats( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
CommsCtxt* comms = globals->cGlobals.game.comms;
|
|
|
|
if ( !!comms ) {
|
|
XWStreamCtxt* stream =
|
|
mem_stream_make( MEMPOOL
|
|
globals->cGlobals.params->vtMgr,
|
|
globals,
|
|
CHANNEL_NONE, catOnClose );
|
|
comms_getStats( comms, stream );
|
|
stream_destroy( stream );
|
|
}
|
|
} /* handle_commstats */
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef MEM_DEBUG
|
|
static void
|
|
handle_memstats( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
XWStreamCtxt* stream = mem_stream_make( MEMPOOL
|
|
globals->cGlobals.params->vtMgr,
|
|
globals,
|
|
CHANNEL_NONE, catOnClose );
|
|
mpool_stats( MEMPOOL stream );
|
|
stream_destroy( stream );
|
|
|
|
} /* handle_memstats */
|
|
#endif
|
|
|
|
#ifdef XWFEATURE_ACTIVERECT
|
|
static gint
|
|
inval_board_ontimer( gpointer data )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)data;
|
|
BoardCtxt* board = globals->cGlobals.game.board;
|
|
board_draw( board );
|
|
return XP_FALSE;
|
|
} /* pen_timer_func */
|
|
|
|
static void
|
|
frame_active( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
XP_Rect rect;
|
|
BoardCtxt* board = globals->cGlobals.game.board;
|
|
board_getActiveRect( board, &rect );
|
|
frame_active_rect( globals->draw, &rect );
|
|
board_invalRect( board, &rect );
|
|
(void)g_timeout_add( 1000, inval_board_ontimer, globals );
|
|
}
|
|
#endif
|
|
|
|
static GtkWidget*
|
|
createAddItem( GtkWidget* parent, gchar* label,
|
|
GtkSignalFunc handlerFunc, GtkGameGlobals* globals )
|
|
{
|
|
GtkWidget* item = gtk_menu_item_new_with_label( label );
|
|
|
|
/* g_print( "createAddItem called with label %s\n", label ); */
|
|
|
|
if ( handlerFunc != NULL ) {
|
|
g_signal_connect( GTK_OBJECT(item), "activate",
|
|
G_CALLBACK(handlerFunc), globals );
|
|
}
|
|
|
|
gtk_menu_append( GTK_MENU(parent), item );
|
|
gtk_widget_show( item );
|
|
|
|
return item;
|
|
} /* createAddItem */
|
|
|
|
static GtkWidget*
|
|
makeMenus( GtkGameGlobals* globals )
|
|
{
|
|
GtkWidget* menubar = gtk_menu_bar_new();
|
|
GtkWidget* fileMenu;
|
|
|
|
fileMenu = makeAddSubmenu( menubar, "File" );
|
|
(void)createAddItem( fileMenu, "Tile values",
|
|
GTK_SIGNAL_FUNC(tile_values), globals );
|
|
(void)createAddItem( fileMenu, "Game history",
|
|
GTK_SIGNAL_FUNC(game_history), globals );
|
|
#ifdef TEXT_MODEL
|
|
(void)createAddItem( fileMenu, "Dump board",
|
|
GTK_SIGNAL_FUNC(dump_board), globals );
|
|
#endif
|
|
|
|
(void)createAddItem( fileMenu, "Final scores",
|
|
GTK_SIGNAL_FUNC(final_scores), globals );
|
|
|
|
(void)createAddItem( fileMenu, "New game",
|
|
GTK_SIGNAL_FUNC(new_game), globals );
|
|
(void)createAddItem( fileMenu, "Game info",
|
|
GTK_SIGNAL_FUNC(game_info), globals );
|
|
|
|
(void)createAddItem( fileMenu, "Load game",
|
|
GTK_SIGNAL_FUNC(load_game), globals );
|
|
(void)createAddItem( fileMenu, "Save game",
|
|
GTK_SIGNAL_FUNC(save_game), globals );
|
|
#ifdef XWFEATURE_CHANGEDICT
|
|
(void)createAddItem( fileMenu, "Change dictionary",
|
|
GTK_SIGNAL_FUNC(change_dictionary), globals );
|
|
#endif
|
|
(void)createAddItem( fileMenu, "Cancel trade",
|
|
GTK_SIGNAL_FUNC(handle_trade_cancel), globals );
|
|
|
|
fileMenu = makeAddSubmenu( menubar, "Edit" );
|
|
|
|
(void)createAddItem( fileMenu, "Undo",
|
|
GTK_SIGNAL_FUNC(handle_undo), globals );
|
|
(void)createAddItem( fileMenu, "Redo",
|
|
GTK_SIGNAL_FUNC(handle_redo), globals );
|
|
|
|
#ifdef FEATURE_TRAY_EDIT
|
|
(void)createAddItem( fileMenu, "Allow tray edit",
|
|
GTK_SIGNAL_FUNC(handle_trayEditToggle_on), globals );
|
|
(void)createAddItem( fileMenu, "Dis-allow tray edit",
|
|
GTK_SIGNAL_FUNC(handle_trayEditToggle_off), globals );
|
|
#endif
|
|
fileMenu = makeAddSubmenu( menubar, "Network" );
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
(void)createAddItem( fileMenu, "Resend",
|
|
GTK_SIGNAL_FUNC(handle_resend), globals );
|
|
#ifdef XWFEATURE_COMMSACK
|
|
(void)createAddItem( fileMenu, "ack any",
|
|
GTK_SIGNAL_FUNC(handle_ack), globals );
|
|
#endif
|
|
# ifdef DEBUG
|
|
(void)createAddItem( fileMenu, "Stats",
|
|
GTK_SIGNAL_FUNC(handle_commstats), globals );
|
|
# endif
|
|
#endif
|
|
#ifdef MEM_DEBUG
|
|
(void)createAddItem( fileMenu, "Mem stats",
|
|
GTK_SIGNAL_FUNC(handle_memstats), globals );
|
|
#endif
|
|
|
|
#ifdef XWFEATURE_ACTIVERECT
|
|
fileMenu = makeAddSubmenu( menubar, "Test" );
|
|
(void)createAddItem( fileMenu, "Frame active area",
|
|
GTK_SIGNAL_FUNC(frame_active), globals );
|
|
#endif
|
|
/* (void)createAddItem( fileMenu, "Print board", */
|
|
/* GTK_SIGNAL_FUNC(handle_print_board), globals ); */
|
|
|
|
/* listAllGames( menubar, argc, argv, globals ); */
|
|
|
|
gtk_widget_show( menubar );
|
|
|
|
return menubar;
|
|
} /* makeMenus */
|
|
|
|
static void
|
|
disenable_buttons( GtkGameGlobals* globals )
|
|
{
|
|
XP_U16 nPending = server_getPendingRegs( globals->cGlobals.game.server );
|
|
if ( !globals->invite_button && 0 < nPending ) {
|
|
globals->invite_button =
|
|
addButton( globals->buttons_hbox, "Invite",
|
|
G_CALLBACK(handle_invite_button), globals );
|
|
}
|
|
|
|
GameStateInfo gsi;
|
|
game_getState( &globals->cGlobals.game, &gsi );
|
|
|
|
XP_Bool canFlip = 1 < board_visTileCount( globals->cGlobals.game.board );
|
|
gtk_widget_set_sensitive( globals->flip_button, canFlip );
|
|
|
|
XP_Bool canToggle = board_canTogglePending( globals->cGlobals.game.board );
|
|
gtk_widget_set_sensitive( globals->toggle_undo_button, canToggle );
|
|
|
|
gtk_widget_set_sensitive( globals->prevhint_button, gsi.canHint );
|
|
gtk_widget_set_sensitive( globals->nexthint_button, gsi.canHint );
|
|
|
|
if ( !!globals->invite_button ) {
|
|
gtk_widget_set_sensitive( globals->invite_button, 0 < nPending );
|
|
}
|
|
gtk_widget_set_sensitive( globals->commit_button, gsi.curTurnSelected );
|
|
|
|
#ifdef XWFEATURE_CHAT
|
|
gtk_widget_set_sensitive( globals->chat_button, gsi.canChat );
|
|
#endif
|
|
}
|
|
|
|
static gboolean
|
|
handle_flip_button( GtkWidget* XP_UNUSED(widget), gpointer _globals )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)_globals;
|
|
if ( board_flip( globals->cGlobals.game.board ) ) {
|
|
board_draw( globals->cGlobals.game.board );
|
|
}
|
|
return TRUE;
|
|
} /* handle_flip_button */
|
|
|
|
static gboolean
|
|
handle_value_button( GtkWidget* XP_UNUSED(widget), gpointer closure )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)closure;
|
|
if ( board_toggle_showValues( globals->cGlobals.game.board ) ) {
|
|
board_draw( globals->cGlobals.game.board );
|
|
}
|
|
return TRUE;
|
|
} /* handle_value_button */
|
|
|
|
static void
|
|
handle_hint_button( GtkGameGlobals* globals, XP_Bool prev )
|
|
{
|
|
XP_Bool redo;
|
|
if ( board_requestHint( globals->cGlobals.game.board,
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
|
XP_FALSE,
|
|
#endif
|
|
prev, &redo ) ) {
|
|
board_draw( globals->cGlobals.game.board );
|
|
disenable_buttons( globals );
|
|
}
|
|
} /* handle_hint_button */
|
|
|
|
static void
|
|
handle_prevhint_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
handle_hint_button( globals, XP_TRUE );
|
|
} /* handle_prevhint_button */
|
|
|
|
static void
|
|
handle_nexthint_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
handle_hint_button( globals, XP_FALSE );
|
|
} /* handle_nexthint_button */
|
|
|
|
static void
|
|
handle_nhint_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
XP_Bool redo;
|
|
|
|
board_resetEngine( globals->cGlobals.game.board );
|
|
if ( board_requestHint( globals->cGlobals.game.board,
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
|
XP_TRUE,
|
|
#endif
|
|
XP_FALSE, &redo ) ) {
|
|
board_draw( globals->cGlobals.game.board );
|
|
}
|
|
} /* handle_nhint_button */
|
|
|
|
static void
|
|
handle_colors_button( GtkWidget* XP_UNUSED(widget),
|
|
GtkGameGlobals* XP_UNUSED(globals) )
|
|
{
|
|
/* XP_Bool oldVal = board_getShowColors( globals->cGlobals.game.board ); */
|
|
/* if ( board_setShowColors( globals->cGlobals.game.board, !oldVal ) ) { */
|
|
/* board_draw( globals->cGlobals.game.board ); */
|
|
/* } */
|
|
} /* handle_colors_button */
|
|
|
|
static void
|
|
handle_juggle_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
if ( board_juggleTray( globals->cGlobals.game.board ) ) {
|
|
board_draw( globals->cGlobals.game.board );
|
|
}
|
|
} /* handle_juggle_button */
|
|
|
|
static void
|
|
handle_undo_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
if ( server_handleUndo( globals->cGlobals.game.server, 0 ) ) {
|
|
board_draw( globals->cGlobals.game.board );
|
|
}
|
|
} /* handle_undo_button */
|
|
|
|
static void
|
|
handle_redo_button( GtkWidget* XP_UNUSED(widget),
|
|
GtkGameGlobals* XP_UNUSED(globals) )
|
|
{
|
|
} /* handle_redo_button */
|
|
|
|
static void
|
|
handle_toggle_undo( GtkWidget* XP_UNUSED(widget),
|
|
GtkGameGlobals* globals )
|
|
{
|
|
BoardCtxt* board = globals->cGlobals.game.board;
|
|
if ( board_redoReplacedTiles( board ) || board_replaceTiles( board ) ) {
|
|
board_draw( board );
|
|
}
|
|
}
|
|
|
|
static void
|
|
handle_trade_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
if ( board_beginTrade( globals->cGlobals.game.board ) ) {
|
|
board_draw( globals->cGlobals.game.board );
|
|
disenable_buttons( globals );
|
|
}
|
|
} /* handle_juggle_button */
|
|
|
|
static void
|
|
handle_done_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
if ( board_commitTurn( globals->cGlobals.game.board ) ) {
|
|
board_draw( globals->cGlobals.game.board );
|
|
disenable_buttons( globals );
|
|
}
|
|
} /* handle_done_button */
|
|
|
|
static void
|
|
setZoomButtons( GtkGameGlobals* globals, XP_Bool* inOut )
|
|
{
|
|
gtk_widget_set_sensitive( globals->zoomin_button, inOut[0] );
|
|
gtk_widget_set_sensitive( globals->zoomout_button, inOut[1] );
|
|
}
|
|
|
|
static void
|
|
handle_zoomin_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
XP_Bool inOut[2];
|
|
if ( board_zoom( globals->cGlobals.game.board, 1, inOut ) ) {
|
|
board_draw( globals->cGlobals.game.board );
|
|
setZoomButtons( globals, inOut );
|
|
}
|
|
} /* handle_zoomin_button */
|
|
|
|
static void
|
|
handle_zoomout_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
XP_Bool inOut[2];
|
|
if ( board_zoom( globals->cGlobals.game.board, -1, inOut ) ) {
|
|
board_draw( globals->cGlobals.game.board );
|
|
setZoomButtons( globals, inOut );
|
|
}
|
|
} /* handle_zoomout_button */
|
|
|
|
#ifdef XWFEATURE_CHAT
|
|
static void
|
|
handle_chat_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
gchar* msg = gtkGetChatMessage( globals );
|
|
if ( NULL != msg ) {
|
|
server_sendChat( globals->cGlobals.game.server, msg );
|
|
g_free( msg );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
scroll_value_changed( GtkAdjustment *adj, GtkGameGlobals* globals )
|
|
{
|
|
XP_U16 newValue;
|
|
gfloat newValueF = adj->value;
|
|
|
|
/* XP_ASSERT( newValueF >= 0.0 */
|
|
/* && newValueF <= globals->cGlobals.params->nHidden ); */
|
|
newValue = (XP_U16)newValueF;
|
|
|
|
if ( board_setYOffset( globals->cGlobals.game.board, newValue ) ) {
|
|
board_draw( globals->cGlobals.game.board );
|
|
}
|
|
} /* scroll_value_changed */
|
|
|
|
static void
|
|
handle_grid_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
globals->gridOn = !globals->gridOn;
|
|
|
|
board_invalAll( globals->cGlobals.game.board );
|
|
board_draw( globals->cGlobals.game.board );
|
|
} /* handle_grid_button */
|
|
|
|
static void
|
|
handle_hide_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
BoardCtxt* board;
|
|
XP_Bool draw = XP_FALSE;
|
|
|
|
if ( globals->cGlobals.params->nHidden > 0 ) {
|
|
gint nRows = globals->cGlobals.gi->boardSize;
|
|
globals->adjustment->page_size = nRows;
|
|
globals->adjustment->value = 0.0;
|
|
|
|
gtk_signal_emit_by_name( GTK_OBJECT(globals->adjustment), "changed" );
|
|
gtk_adjustment_value_changed( GTK_ADJUSTMENT(globals->adjustment) );
|
|
}
|
|
|
|
board = globals->cGlobals.game.board;
|
|
if ( TRAY_REVEALED == board_getTrayVisState( board ) ) {
|
|
draw = board_hideTray( board );
|
|
} else {
|
|
draw = board_showTray( board );
|
|
}
|
|
if ( draw ) {
|
|
board_draw( board );
|
|
}
|
|
} /* handle_hide_button */
|
|
|
|
static void
|
|
handle_commit_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
if ( board_commitTurn( globals->cGlobals.game.board ) ) {
|
|
board_draw( globals->cGlobals.game.board );
|
|
}
|
|
} /* handle_commit_button */
|
|
|
|
static void
|
|
handle_invite_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|
{
|
|
const CurGameInfo* gi = globals->cGlobals.gi;
|
|
|
|
gchar* countStr;
|
|
gchar* phone = NULL;
|
|
gchar* portstr = NULL;
|
|
gchar* forceChannelStr;
|
|
AskMInfo infos[] = {
|
|
{ "Number of players", &countStr },
|
|
{ "Remote phone#", &phone },
|
|
{ "Remote port", &portstr },
|
|
{ "Force channel", &forceChannelStr },
|
|
};
|
|
|
|
XP_U16 nMissing = server_getPendingRegs( globals->cGlobals.game.server );
|
|
gchar buf[64];
|
|
sprintf( buf, "%d", nMissing );
|
|
countStr = buf;
|
|
gchar forceChannelBuf[64];
|
|
sprintf( forceChannelBuf, "%d", 1 );
|
|
forceChannelStr = forceChannelBuf;
|
|
|
|
while ( gtkaskm( "Invite how many and how?", infos, VSIZE(infos) ) ) {
|
|
int nPlayers = atoi( countStr );
|
|
if ( 0 >= nPlayers || nPlayers > nMissing ) {
|
|
gchar buf[128];
|
|
sprintf( buf, "Please invite between 1 and %d players (inclusive).",
|
|
nMissing );
|
|
gtktell( globals->window, buf );
|
|
break;
|
|
}
|
|
|
|
int port = atoi( portstr );
|
|
if ( 0 == port ) {
|
|
gtktell( globals->window, "Port must be a number and not 0." );
|
|
break;
|
|
}
|
|
int forceChannel = atoi( forceChannelStr );
|
|
if ( 1 > forceChannel || 4 <= forceChannel ) {
|
|
gtktell( globals->window, "Channel must be between 1 and the number of client devices." );
|
|
break;
|
|
}
|
|
|
|
gchar gameName[64];
|
|
snprintf( gameName, VSIZE(gameName), "Game %d", gi->gameID );
|
|
|
|
CommsAddrRec addr;
|
|
CommsCtxt* comms = globals->cGlobals.game.comms;
|
|
XP_ASSERT( comms );
|
|
comms_getAddr( comms, &addr );
|
|
|
|
linux_sms_invite( globals->cGlobals.params, gi, &addr, gameName,
|
|
nPlayers, forceChannel, phone, port );
|
|
break;
|
|
}
|
|
for ( int ii = 0; ii < VSIZE(infos); ++ii ) {
|
|
g_free( *infos[ii].result );
|
|
}
|
|
} /* handle_invite_button */
|
|
|
|
static void
|
|
gtkUserError( GtkGameGlobals* globals, const char* format, ... )
|
|
{
|
|
char buf[512];
|
|
va_list ap;
|
|
|
|
va_start( ap, format );
|
|
|
|
vsprintf( buf, format, ap );
|
|
|
|
(void)gtkask( globals->window, buf, GTK_BUTTONS_OK, NULL );
|
|
|
|
va_end(ap);
|
|
} /* gtkUserError */
|
|
|
|
static VTableMgr*
|
|
gtk_util_getVTManager(XW_UtilCtxt* uc)
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
|
|
return globals->cGlobals.params->vtMgr;
|
|
} /* linux_util_getVTManager */
|
|
|
|
static XP_S16
|
|
gtk_util_userPickTileBlank( XW_UtilCtxt* uc, XP_U16 playerNum,
|
|
const XP_UCHAR** texts, XP_U16 nTiles )
|
|
{
|
|
XP_S16 chosen;
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
|
|
XP_UCHAR* name = globals->cGlobals.gi->players[playerNum].name;
|
|
|
|
chosen = gtkletterask( NULL, XP_FALSE, name, nTiles, texts );
|
|
return chosen;
|
|
}
|
|
|
|
static XP_S16
|
|
gtk_util_userPickTileTray( XW_UtilCtxt* uc, const PickInfo* pi,
|
|
XP_U16 playerNum, const XP_UCHAR** texts,
|
|
XP_U16 nTiles )
|
|
{
|
|
XP_S16 chosen;
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
|
|
XP_UCHAR* name = globals->cGlobals.gi->players[playerNum].name;
|
|
|
|
chosen = gtkletterask( pi, XP_TRUE, name, nTiles, texts );
|
|
return chosen;
|
|
} /* gtk_util_userPickTile */
|
|
|
|
static XP_Bool
|
|
gtk_util_askPassword( XW_UtilCtxt* XP_UNUSED(uc), const XP_UCHAR* name,
|
|
XP_UCHAR* buf, XP_U16* len )
|
|
{
|
|
XP_Bool ok = gtkpasswdask( name, buf, len );
|
|
return ok;
|
|
} /* gtk_util_askPassword */
|
|
|
|
static void
|
|
setCtrlsForTray( GtkGameGlobals* XP_UNUSED(globals) )
|
|
{
|
|
#if 0
|
|
XW_TrayVisState state =
|
|
board_getTrayVisState( globals->cGlobals.game.board );
|
|
XP_S16 nHidden = globals->cGlobals.params->nHidden;
|
|
|
|
if ( nHidden != 0 ) {
|
|
XP_U16 pageSize = nRows;
|
|
|
|
if ( state == TRAY_HIDDEN ) { /* we recover what tray covers */
|
|
nHidden -= GTK_TRAY_HT_ROWS;
|
|
}
|
|
if ( nHidden > 0 ) {
|
|
pageSize -= nHidden;
|
|
}
|
|
globals->adjustment->page_size = pageSize;
|
|
|
|
globals->adjustment->value =
|
|
board_getYOffset( globals->cGlobals.game.board );
|
|
gtk_signal_emit_by_name( GTK_OBJECT(globals->adjustment), "changed" );
|
|
}
|
|
#endif
|
|
} /* setCtrlsForTray */
|
|
|
|
static void
|
|
gtk_util_trayHiddenChange( XW_UtilCtxt* uc, XW_TrayVisState XP_UNUSED(state),
|
|
XP_U16 XP_UNUSED(nVisibleRows) )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
|
|
setCtrlsForTray( globals );
|
|
} /* gtk_util_trayHiddenChange */
|
|
|
|
static void
|
|
gtk_util_yOffsetChange( XW_UtilCtxt* uc, XP_U16 maxOffset,
|
|
XP_U16 XP_UNUSED(oldOffset),
|
|
XP_U16 newOffset )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
|
|
if ( !!globals->adjustment ) {
|
|
gint nRows = globals->cGlobals.gi->boardSize;
|
|
globals->adjustment->page_size = nRows - maxOffset;
|
|
globals->adjustment->value = newOffset;
|
|
gtk_adjustment_value_changed( GTK_ADJUSTMENT(globals->adjustment) );
|
|
}
|
|
} /* gtk_util_yOffsetChange */
|
|
|
|
static void
|
|
gtkShowFinalScores( const GtkGameGlobals* globals )
|
|
{
|
|
XWStreamCtxt* stream;
|
|
XP_UCHAR* text;
|
|
const CommonGlobals* cGlobals = &globals->cGlobals;
|
|
|
|
stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
|
|
cGlobals->params->vtMgr,
|
|
NULL, CHANNEL_NONE, NULL );
|
|
server_writeFinalScores( cGlobals->game.server, stream );
|
|
|
|
text = strFromStream( stream );
|
|
stream_destroy( stream );
|
|
|
|
XP_U16 timeout = cGlobals->manualFinal? 0 : cGlobals->params->askTimeout;
|
|
const AskPair buttons[] = {
|
|
{ "OK", 1 },
|
|
{ "Rematch", 2 },
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
gint chosen = gtkask_timeout( globals->window, text, GTK_BUTTONS_NONE,
|
|
buttons, timeout );
|
|
free( text );
|
|
if ( 2 == chosen ) {
|
|
XP_LOGF( "%s: rematch chosen!", __func__ );
|
|
XP_ASSERT( 0 );
|
|
}
|
|
} /* gtkShowFinalScores */
|
|
|
|
static void
|
|
gtk_util_informMove( XW_UtilCtxt* uc, XWStreamCtxt* expl,
|
|
XWStreamCtxt* words )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
|
|
char* question = strFromStream( !!words? words : expl );
|
|
(void)gtkask( globals->window, question, GTK_BUTTONS_OK, NULL );
|
|
free( question );
|
|
}
|
|
|
|
static void
|
|
gtk_util_informUndo( XW_UtilCtxt* uc )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
|
|
(void)gtkask_timeout( globals->window, "Remote player undid a move",
|
|
GTK_BUTTONS_OK, NULL, 500 );
|
|
}
|
|
|
|
static void
|
|
gtk_util_notifyGameOver( XW_UtilCtxt* uc, XP_S16 quitter )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
|
|
CommonGlobals* cGlobals = &globals->cGlobals;
|
|
|
|
if ( cGlobals->params->printHistory ) {
|
|
catGameHistory( cGlobals );
|
|
}
|
|
|
|
catFinalScores( cGlobals, quitter );
|
|
|
|
if ( cGlobals->params->quitAfter >= 0 ) {
|
|
sleep( cGlobals->params->quitAfter );
|
|
destroy_board_window( NULL, globals );
|
|
} else if ( cGlobals->params->undoWhenDone ) {
|
|
server_handleUndo( cGlobals->game.server, 0 );
|
|
board_draw( cGlobals->game.board );
|
|
} else if ( !cGlobals->params->skipGameOver ) {
|
|
gtkShowFinalScores( globals );
|
|
}
|
|
} /* gtk_util_notifyGameOver */
|
|
|
|
static void
|
|
gtk_util_informNetDict( XW_UtilCtxt* uc, XP_LangCode XP_UNUSED(lang),
|
|
const XP_UCHAR* oldName,
|
|
const XP_UCHAR* newName, const XP_UCHAR* newSum,
|
|
XWPhoniesChoice phoniesAction )
|
|
{
|
|
if ( 0 != strcmp( oldName, newName ) ) {
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
|
|
gchar buf[512];
|
|
int offset = snprintf( buf, VSIZE(buf),
|
|
"dict changing from %s to %s (sum=%s).",
|
|
oldName, newName, newSum );
|
|
if ( PHONIES_DISALLOW == phoniesAction ) {
|
|
snprintf( &buf[offset], VSIZE(buf)-offset, "%s",
|
|
"\nPHONIES_DISALLOW is set so this may "
|
|
"lead to some surprises." );
|
|
}
|
|
(void)gtkask( globals->window, buf, GTK_BUTTONS_OK, NULL );
|
|
}
|
|
}
|
|
|
|
static gint
|
|
changeRoles( gpointer data )
|
|
{
|
|
linuxChangeRoles( (CommonGlobals*)data );
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
gtk_util_setIsServer( XW_UtilCtxt* uc, XP_Bool isServer )
|
|
{
|
|
CommonGlobals* cGlobals = (CommonGlobals*)uc->closure;
|
|
linuxSetIsServer( cGlobals, isServer );
|
|
|
|
(void)g_idle_add( changeRoles, cGlobals );
|
|
}
|
|
|
|
/* define this to prevent user events during debugging from stopping the engine */
|
|
/* #define DONT_ABORT_ENGINE */
|
|
|
|
#ifdef XWFEATURE_HILITECELL
|
|
static XP_Bool
|
|
gtk_util_hiliteCell( XW_UtilCtxt* uc, XP_U16 col, XP_U16 row )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
|
|
#ifndef DONT_ABORT_ENGINE
|
|
gboolean pending;
|
|
#endif
|
|
|
|
board_hiliteCellAt( globals->cGlobals.game.board, col, row );
|
|
if ( globals->cGlobals.params->sleepOnAnchor ) {
|
|
usleep( 10000 );
|
|
}
|
|
|
|
#ifdef DONT_ABORT_ENGINE
|
|
return XP_TRUE; /* keep going */
|
|
#else
|
|
pending = gdk_events_pending();
|
|
if ( pending ) {
|
|
XP_DEBUGF( "gtk_util_hiliteCell=>%d", pending );
|
|
}
|
|
return !pending;
|
|
#endif
|
|
} /* gtk_util_hiliteCell */
|
|
#endif
|
|
|
|
static XP_Bool
|
|
gtk_util_altKeyDown( XW_UtilCtxt* uc )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
|
|
return globals->altKeyDown;
|
|
}
|
|
|
|
static XP_Bool
|
|
gtk_util_engineProgressCallback( XW_UtilCtxt* XP_UNUSED(uc) )
|
|
{
|
|
#ifdef DONT_ABORT_ENGINE
|
|
return XP_TRUE; /* keep going */
|
|
#else
|
|
gboolean pending = gdk_events_pending();
|
|
|
|
/* XP_DEBUGF( "gdk_events_pending returned %d\n", pending ); */
|
|
|
|
return !pending;
|
|
#endif
|
|
} /* gtk_util_engineProgressCallback */
|
|
|
|
static void
|
|
cancelTimer( GtkGameGlobals* globals, XWTimerReason why )
|
|
{
|
|
guint src = globals->timerSources[why-1];
|
|
if ( src != 0 ) {
|
|
g_source_remove( src );
|
|
globals->timerSources[why-1] = 0;
|
|
}
|
|
} /* cancelTimer */
|
|
|
|
static gint
|
|
pen_timer_func( gpointer data )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)data;
|
|
|
|
if ( linuxFireTimer( &globals->cGlobals, TIMER_PENDOWN ) ) {
|
|
board_draw( globals->cGlobals.game.board );
|
|
}
|
|
|
|
return XP_FALSE;
|
|
} /* pen_timer_func */
|
|
|
|
static gint
|
|
score_timer_func( gpointer data )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)data;
|
|
|
|
if ( linuxFireTimer( &globals->cGlobals, TIMER_TIMERTICK ) ) {
|
|
board_draw( globals->cGlobals.game.board );
|
|
}
|
|
|
|
return XP_FALSE;
|
|
} /* score_timer_func */
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
static gint
|
|
comms_timer_func( gpointer data )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)data;
|
|
|
|
if ( linuxFireTimer( &globals->cGlobals, TIMER_COMMS ) ) {
|
|
board_draw( globals->cGlobals.game.board );
|
|
}
|
|
|
|
return (gint)0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef XWFEATURE_SLOW_ROBOT
|
|
static gint
|
|
slowrob_timer_func( gpointer data )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)data;
|
|
|
|
if ( linuxFireTimer( &globals->cGlobals, TIMER_SLOWROBOT ) ) {
|
|
board_draw( globals->cGlobals.game.board );
|
|
}
|
|
|
|
return (gint)0;
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
gtk_util_setTimer( XW_UtilCtxt* uc, XWTimerReason why,
|
|
XP_U16 XP_UNUSED_STANDALONE(when),
|
|
XWTimerProc proc, void* closure )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
|
|
guint newSrc;
|
|
|
|
cancelTimer( globals, why );
|
|
|
|
if ( why == TIMER_PENDOWN ) {
|
|
if ( 0 != globals->timerSources[why-1] ) {
|
|
g_source_remove( globals->timerSources[why-1] );
|
|
}
|
|
newSrc = g_timeout_add( 1000, pen_timer_func, globals );
|
|
} else if ( why == TIMER_TIMERTICK ) {
|
|
/* one second */
|
|
globals->scoreTimerInterval = 100 * 10000;
|
|
|
|
(void)gettimeofday( &globals->scoreTv, NULL );
|
|
|
|
newSrc = g_timeout_add( 1000, score_timer_func, globals );
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
} else if ( why == TIMER_COMMS ) {
|
|
newSrc = g_timeout_add( 1000 * when, comms_timer_func, globals );
|
|
#endif
|
|
#ifdef XWFEATURE_SLOW_ROBOT
|
|
} else if ( why == TIMER_SLOWROBOT ) {
|
|
newSrc = g_timeout_add( 1000 * when, slowrob_timer_func, globals );
|
|
#endif
|
|
} else {
|
|
XP_ASSERT( 0 );
|
|
}
|
|
|
|
globals->cGlobals.timerInfo[why].proc = proc;
|
|
globals->cGlobals.timerInfo[why].closure = closure;
|
|
XP_ASSERT( newSrc != 0 );
|
|
globals->timerSources[why-1] = newSrc;
|
|
} /* gtk_util_setTimer */
|
|
|
|
static void
|
|
gtk_util_clearTimer( XW_UtilCtxt* uc, XWTimerReason why )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
|
|
globals->cGlobals.timerInfo[why].proc = NULL;
|
|
}
|
|
|
|
static gint
|
|
idle_func( gpointer data )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)data;
|
|
/* XP_DEBUGF( "idle_func called\n" ); */
|
|
|
|
/* remove before calling server_do. If server_do puts up a dialog that
|
|
calls gtk_main, then this idle proc will also apply to that event loop
|
|
and bad things can happen. So kill the idle proc asap. */
|
|
g_source_remove( globals->idleID );
|
|
|
|
ServerCtxt* server = globals->cGlobals.game.server;
|
|
if ( !!server && server_do( server ) ) {
|
|
if ( !!globals->cGlobals.game.board ) {
|
|
board_draw( globals->cGlobals.game.board );
|
|
}
|
|
}
|
|
return 0; /* 0 will stop it from being called again */
|
|
} /* idle_func */
|
|
|
|
static void
|
|
gtk_util_requestTime( XW_UtilCtxt* uc )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
|
|
globals->idleID = g_idle_add( idle_func, globals );
|
|
} /* gtk_util_requestTime */
|
|
|
|
static XP_Bool
|
|
gtk_util_warnIllegalWord( XW_UtilCtxt* uc, BadWordInfo* bwi, XP_U16 player,
|
|
XP_Bool turnLost )
|
|
{
|
|
XP_Bool result;
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
|
|
char buf[300];
|
|
|
|
if ( turnLost ) {
|
|
char wordsBuf[256];
|
|
XP_U16 i;
|
|
XP_UCHAR* name = globals->cGlobals.gi->players[player].name;
|
|
XP_ASSERT( !!name );
|
|
|
|
for ( i = 0, wordsBuf[0] = '\0'; ; ) {
|
|
char wordBuf[18];
|
|
sprintf( wordBuf, "\"%s\"", bwi->words[i] );
|
|
strcat( wordsBuf, wordBuf );
|
|
if ( ++i == bwi->nWords ) {
|
|
break;
|
|
}
|
|
strcat( wordsBuf, ", " );
|
|
}
|
|
|
|
sprintf( buf, "Player %d (%s) played illegal word[s] %s; loses turn.",
|
|
player+1, name, wordsBuf );
|
|
|
|
if ( globals->cGlobals.params->skipWarnings ) {
|
|
XP_LOGF( "%s", buf );
|
|
} else {
|
|
gtkUserError( globals, buf );
|
|
}
|
|
result = XP_TRUE;
|
|
} else {
|
|
XP_ASSERT( bwi->nWords == 1 );
|
|
sprintf( buf, "Word \"%s\" not in the current dictionary (%s). "
|
|
"Use it anyway?", bwi->words[0], bwi->dictName );
|
|
result = GTK_RESPONSE_YES == gtkask( globals->window, buf,
|
|
GTK_BUTTONS_YES_NO, NULL );
|
|
}
|
|
|
|
return result;
|
|
} /* gtk_util_warnIllegalWord */
|
|
|
|
static void
|
|
gtk_util_remSelected( XW_UtilCtxt* uc )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
|
|
XWStreamCtxt* stream;
|
|
XP_UCHAR* text;
|
|
|
|
stream = mem_stream_make( MEMPOOL
|
|
globals->cGlobals.params->vtMgr,
|
|
globals, CHANNEL_NONE, NULL );
|
|
board_formatRemainingTiles( globals->cGlobals.game.board, stream );
|
|
text = strFromStream( stream );
|
|
stream_destroy( stream );
|
|
|
|
(void)gtkask( globals->window, text, GTK_BUTTONS_OK, NULL );
|
|
free( text );
|
|
}
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
static XWStreamCtxt*
|
|
gtk_util_makeStreamFromAddr(XW_UtilCtxt* uc, XP_PlayerAddr channelNo )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
|
|
|
|
XWStreamCtxt* stream = mem_stream_make( MEMPOOL
|
|
globals->cGlobals.params->vtMgr,
|
|
&globals->cGlobals, channelNo,
|
|
sendOnClose );
|
|
return stream;
|
|
} /* gtk_util_makeStreamFromAddr */
|
|
|
|
#ifdef XWFEATURE_CHAT
|
|
static void
|
|
gtk_util_showChat( XW_UtilCtxt* uc, const XP_UCHAR* const msg )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
|
|
(void)gtkask( globals->window, msg, GTK_BUTTONS_OK, NULL );
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
|
static XP_Bool
|
|
gtk_util_getTraySearchLimits( XW_UtilCtxt* XP_UNUSED(uc),
|
|
XP_U16* XP_UNUSED(min), XP_U16* max )
|
|
{
|
|
*max = askNTiles( MAX_TRAY_TILES, *max );
|
|
return XP_TRUE;
|
|
}
|
|
#endif
|
|
|
|
#ifndef XWFEATURE_MINIWIN
|
|
static void
|
|
gtk_util_bonusSquareHeld( XW_UtilCtxt* uc, XWBonusType bonus )
|
|
{
|
|
LOG_FUNC();
|
|
XP_USE( uc );
|
|
XP_USE( bonus );
|
|
}
|
|
|
|
static void
|
|
gtk_util_playerScoreHeld( XW_UtilCtxt* uc, XP_U16 player )
|
|
{
|
|
LOG_FUNC();
|
|
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
|
|
|
|
LastMoveInfo lmi;
|
|
if ( model_getPlayersLastScore( globals->cGlobals.game.model,
|
|
player, &lmi ) ) {
|
|
XP_UCHAR buf[128];
|
|
formatLMI( &lmi, buf, VSIZE(buf) );
|
|
(void)gtkask( globals->window, buf, GTK_BUTTONS_OK, NULL );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef XWFEATURE_BOARDWORDS
|
|
static void
|
|
gtk_util_cellSquareHeld( XW_UtilCtxt* uc, XWStreamCtxt* words )
|
|
{
|
|
XP_USE( uc );
|
|
catOnClose( words, NULL );
|
|
fprintf( stderr, "\n" );
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
gtk_util_userError( XW_UtilCtxt* uc, UtilErrID id )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
|
|
XP_Bool silent;
|
|
const XP_UCHAR* message = linux_getErrString( id, &silent );
|
|
|
|
XP_LOGF( "%s: %s", __func__, message );
|
|
if ( !silent ) {
|
|
gtkUserError( globals, message );
|
|
}
|
|
} /* gtk_util_userError */
|
|
|
|
static XP_Bool
|
|
gtk_util_userQuery( XW_UtilCtxt* uc, UtilQueryID id,
|
|
XWStreamCtxt* stream )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
|
|
XP_Bool result;
|
|
char* question;
|
|
XP_Bool freeMe = XP_FALSE;
|
|
GtkButtonsType buttons = GTK_BUTTONS_YES_NO;
|
|
|
|
switch( id ) {
|
|
|
|
case QUERY_COMMIT_TURN:
|
|
question = strFromStream( stream );
|
|
freeMe = XP_TRUE;
|
|
break;
|
|
case QUERY_ROBOT_TRADE:
|
|
question = strFromStream( stream );
|
|
freeMe = XP_TRUE;
|
|
buttons = GTK_BUTTONS_OK;
|
|
break;
|
|
|
|
default:
|
|
XP_ASSERT( 0 );
|
|
return XP_FALSE;
|
|
}
|
|
gint chosen = gtkask( globals->window, question, buttons, NULL );
|
|
result = GTK_RESPONSE_OK == chosen || chosen == GTK_RESPONSE_YES;
|
|
|
|
if ( freeMe ) {
|
|
free( question );
|
|
}
|
|
|
|
return result;
|
|
} /* gtk_util_userQuery */
|
|
|
|
static XP_Bool
|
|
gtk_util_confirmTrade( XW_UtilCtxt* uc,
|
|
const XP_UCHAR** tiles, XP_U16 nTiles )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
|
|
char question[256];
|
|
formatConfirmTrade( tiles, nTiles, question, sizeof(question) );
|
|
return GTK_RESPONSE_YES == gtkask( globals->window, question,
|
|
GTK_BUTTONS_YES_NO, NULL );
|
|
}
|
|
|
|
static GtkWidget*
|
|
makeShowButtonFromBitmap( void* closure, const gchar* filename,
|
|
const gchar* alt, GCallback func )
|
|
{
|
|
GtkWidget* widget;
|
|
GtkWidget* button;
|
|
|
|
if ( file_exists( filename ) ) {
|
|
widget = gtk_image_new_from_file( filename );
|
|
} else {
|
|
widget = gtk_label_new( alt );
|
|
}
|
|
gtk_widget_show( widget );
|
|
|
|
button = gtk_button_new();
|
|
gtk_container_add (GTK_CONTAINER (button), widget );
|
|
gtk_widget_show (button);
|
|
|
|
if ( func != NULL ) {
|
|
g_signal_connect( GTK_OBJECT(button), "clicked", func, closure );
|
|
}
|
|
|
|
return button;
|
|
} /* makeShowButtonFromBitmap */
|
|
|
|
static GtkWidget*
|
|
makeVerticalBar( GtkGameGlobals* globals, GtkWidget* XP_UNUSED(window) )
|
|
{
|
|
GtkWidget* vbox;
|
|
GtkWidget* button;
|
|
|
|
vbox = gtk_vbutton_box_new();
|
|
|
|
button = makeShowButtonFromBitmap( globals, "../flip.xpm", "f",
|
|
G_CALLBACK(handle_flip_button) );
|
|
gtk_box_pack_start( GTK_BOX(vbox), button, FALSE, TRUE, 0 );
|
|
globals->flip_button = button;
|
|
|
|
button = makeShowButtonFromBitmap( globals, "../value.xpm", "v",
|
|
G_CALLBACK(handle_value_button) );
|
|
gtk_box_pack_start( GTK_BOX(vbox), button, FALSE, TRUE, 0 );
|
|
|
|
button = makeShowButtonFromBitmap( globals, "../hint.xpm", "?-",
|
|
G_CALLBACK(handle_prevhint_button) );
|
|
globals->prevhint_button = button;
|
|
gtk_box_pack_start( GTK_BOX(vbox), button, FALSE, TRUE, 0 );
|
|
button = makeShowButtonFromBitmap( globals, "../hint.xpm", "?+",
|
|
G_CALLBACK(handle_nexthint_button) );
|
|
globals->nexthint_button = button;
|
|
gtk_box_pack_start( GTK_BOX(vbox), button, FALSE, TRUE, 0 );
|
|
|
|
button = makeShowButtonFromBitmap( globals, "../hintNum.xpm", "n",
|
|
G_CALLBACK(handle_nhint_button) );
|
|
gtk_box_pack_start( GTK_BOX(vbox), button, FALSE, TRUE, 0 );
|
|
|
|
button = makeShowButtonFromBitmap( globals, "../colors.xpm", "c",
|
|
G_CALLBACK(handle_colors_button) );
|
|
gtk_box_pack_start( GTK_BOX(vbox), button, FALSE, TRUE, 0 );
|
|
|
|
/* undo and redo buttons */
|
|
button = makeShowButtonFromBitmap( globals, "../undo.xpm", "U",
|
|
G_CALLBACK(handle_undo_button) );
|
|
gtk_box_pack_start( GTK_BOX(vbox), button, FALSE, TRUE, 0 );
|
|
button = makeShowButtonFromBitmap( globals, "../redo.xpm", "R",
|
|
G_CALLBACK(handle_redo_button) );
|
|
gtk_box_pack_start( GTK_BOX(vbox), button, FALSE, TRUE, 0 );
|
|
|
|
button = makeShowButtonFromBitmap( globals, "", "u/r",
|
|
G_CALLBACK(handle_toggle_undo) );
|
|
globals->toggle_undo_button = button;
|
|
gtk_box_pack_start( GTK_BOX(vbox), button, FALSE, TRUE, 0 );
|
|
|
|
/* the four buttons that on palm are beside the tray */
|
|
button = makeShowButtonFromBitmap( globals, "../juggle.xpm", "j",
|
|
G_CALLBACK(handle_juggle_button) );
|
|
gtk_box_pack_start( GTK_BOX(vbox), button, FALSE, TRUE, 0 );
|
|
|
|
button = makeShowButtonFromBitmap( globals, "../trade.xpm", "t",
|
|
G_CALLBACK(handle_trade_button) );
|
|
gtk_box_pack_start( GTK_BOX(vbox), button, FALSE, TRUE, 0 );
|
|
button = makeShowButtonFromBitmap( globals, "../done.xpm", "d",
|
|
G_CALLBACK(handle_done_button) );
|
|
gtk_box_pack_start( GTK_BOX(vbox), button, FALSE, TRUE, 0 );
|
|
button = makeShowButtonFromBitmap( globals, "../done.xpm", "+",
|
|
G_CALLBACK(handle_zoomin_button) );
|
|
globals->zoomin_button = button;
|
|
gtk_box_pack_start( GTK_BOX(vbox), button, FALSE, TRUE, 0 );
|
|
button = makeShowButtonFromBitmap( globals, "../done.xpm", "-",
|
|
G_CALLBACK(handle_zoomout_button) );
|
|
globals->zoomout_button = button;
|
|
gtk_box_pack_start( GTK_BOX(vbox), button, FALSE, TRUE, 0 );
|
|
#ifdef XWFEATURE_CHAT
|
|
button = makeShowButtonFromBitmap( globals, "", "chat",
|
|
G_CALLBACK(handle_chat_button) );
|
|
globals->chat_button = button;
|
|
gtk_box_pack_start( GTK_BOX(vbox), button, FALSE, TRUE, 0 );
|
|
#endif
|
|
|
|
gtk_widget_show( vbox );
|
|
return vbox;
|
|
} /* makeVerticalBar */
|
|
|
|
static GtkWidget*
|
|
addButton( GtkWidget* hbox, gchar* label, GCallback func, GtkGameGlobals* globals )
|
|
|
|
{
|
|
GtkWidget* button = gtk_button_new_with_label( label );
|
|
gtk_widget_show( button );
|
|
g_signal_connect( GTK_OBJECT(button), "clicked", G_CALLBACK(func), globals );
|
|
gtk_box_pack_start( GTK_BOX(hbox), button, FALSE, TRUE, 0);
|
|
return button;
|
|
}
|
|
|
|
static GtkWidget*
|
|
makeButtons( GtkGameGlobals* globals )
|
|
{
|
|
GtkWidget* hbox = gtk_hbox_new( FALSE, 0 );
|
|
globals->buttons_hbox = hbox;
|
|
|
|
(void)addButton( hbox, "Grid", G_CALLBACK(handle_grid_button), globals );
|
|
(void)addButton( hbox, "Hide", G_CALLBACK(handle_hide_button), globals );
|
|
globals->commit_button =
|
|
addButton( hbox, "Commit", G_CALLBACK(handle_commit_button), globals );
|
|
|
|
gtk_widget_show( hbox );
|
|
return hbox;
|
|
} /* makeButtons */
|
|
|
|
static void
|
|
setupGtkUtilCallbacks( GtkGameGlobals* globals, XW_UtilCtxt* util )
|
|
{
|
|
util->vtable->m_util_userError = gtk_util_userError;
|
|
util->vtable->m_util_userQuery = gtk_util_userQuery;
|
|
util->vtable->m_util_confirmTrade = gtk_util_confirmTrade;
|
|
util->vtable->m_util_getVTManager = gtk_util_getVTManager;
|
|
util->vtable->m_util_userPickTileBlank = gtk_util_userPickTileBlank;
|
|
util->vtable->m_util_userPickTileTray = gtk_util_userPickTileTray;
|
|
util->vtable->m_util_askPassword = gtk_util_askPassword;
|
|
util->vtable->m_util_trayHiddenChange = gtk_util_trayHiddenChange;
|
|
util->vtable->m_util_yOffsetChange = gtk_util_yOffsetChange;
|
|
util->vtable->m_util_informMove = gtk_util_informMove;
|
|
util->vtable->m_util_informUndo = gtk_util_informUndo;
|
|
util->vtable->m_util_notifyGameOver = gtk_util_notifyGameOver;
|
|
util->vtable->m_util_informNetDict = gtk_util_informNetDict;
|
|
util->vtable->m_util_setIsServer = gtk_util_setIsServer;
|
|
#ifdef XWFEATURE_HILITECELL
|
|
util->vtable->m_util_hiliteCell = gtk_util_hiliteCell;
|
|
#endif
|
|
util->vtable->m_util_altKeyDown = gtk_util_altKeyDown;
|
|
util->vtable->m_util_engineProgressCallback =
|
|
gtk_util_engineProgressCallback;
|
|
util->vtable->m_util_setTimer = gtk_util_setTimer;
|
|
util->vtable->m_util_clearTimer = gtk_util_clearTimer;
|
|
util->vtable->m_util_requestTime = gtk_util_requestTime;
|
|
util->vtable->m_util_warnIllegalWord = gtk_util_warnIllegalWord;
|
|
util->vtable->m_util_remSelected = gtk_util_remSelected;
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
util->vtable->m_util_makeStreamFromAddr = gtk_util_makeStreamFromAddr;
|
|
#endif
|
|
#ifdef XWFEATURE_CHAT
|
|
util->vtable->m_util_showChat = gtk_util_showChat;
|
|
#endif
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
|
util->vtable->m_util_getTraySearchLimits = gtk_util_getTraySearchLimits;
|
|
#endif
|
|
|
|
#ifndef XWFEATURE_MINIWIN
|
|
util->vtable->m_util_bonusSquareHeld = gtk_util_bonusSquareHeld;
|
|
util->vtable->m_util_playerScoreHeld = gtk_util_playerScoreHeld;
|
|
#endif
|
|
#ifdef XWFEATURE_BOARDWORDS
|
|
util->vtable->m_util_cellSquareHeld = gtk_util_cellSquareHeld;
|
|
#endif
|
|
|
|
util->closure = globals;
|
|
} /* setupGtkUtilCallbacks */
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
typedef struct _SockInfo {
|
|
GIOChannel* channel;
|
|
guint watch;
|
|
int socket;
|
|
} SockInfo;
|
|
|
|
static void
|
|
gtk_socket_added( void* closure, int newSock, GIOFunc proc )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)closure;
|
|
|
|
if ( newSock != -1 ) {
|
|
XP_ASSERT( !!proc );
|
|
GIOChannel* channel = g_io_channel_unix_new( newSock );
|
|
g_io_channel_set_close_on_unref( channel, TRUE );
|
|
#ifdef DEBUG
|
|
guint result =
|
|
#endif
|
|
g_io_add_watch( channel, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI,
|
|
proc, globals );
|
|
XP_LOGF( "g_io_add_watch(%d) => %d", newSock, result );
|
|
}
|
|
/* A hack for the bluetooth case. */
|
|
CommsCtxt* comms = globals->cGlobals.game.comms;
|
|
|
|
CommsAddrRec addr;
|
|
comms_getAddr( comms, &addr );
|
|
if ( (comms != NULL) && (addr_hasType( &addr, COMMS_CONN_BT) ) ) {
|
|
comms_resendAll( comms, XP_FALSE );
|
|
}
|
|
LOG_RETURN_VOID();
|
|
} /* gtk_socket_changed */
|
|
|
|
static gboolean
|
|
acceptorInput( GIOChannel* source, GIOCondition condition, gpointer data )
|
|
{
|
|
gboolean keepSource;
|
|
CommonGlobals* globals = (CommonGlobals*)data;
|
|
LOG_FUNC();
|
|
|
|
if ( (condition & G_IO_IN) != 0 ) {
|
|
int listener = g_io_channel_unix_get_fd( source );
|
|
XP_LOGF( "%s: input on socket %d", __func__, listener );
|
|
keepSource = (*globals->acceptor)( listener, data );
|
|
} else {
|
|
keepSource = FALSE;
|
|
}
|
|
|
|
return keepSource;
|
|
} /* acceptorInput */
|
|
|
|
static void
|
|
gtk_socket_acceptor( int listener, Acceptor func, CommonGlobals* globals,
|
|
void** storage )
|
|
{
|
|
SockInfo* info = (SockInfo*)*storage;
|
|
GIOChannel* channel;
|
|
guint watch;
|
|
|
|
LOG_FUNC();
|
|
|
|
if ( listener == -1 ) {
|
|
XP_ASSERT( !!globals->acceptor );
|
|
globals->acceptor = NULL;
|
|
XP_ASSERT( !!info );
|
|
#ifdef DEBUG
|
|
int oldSock = info->socket;
|
|
#endif
|
|
g_source_remove( info->watch );
|
|
g_io_channel_unref( info->channel );
|
|
XP_FREE( globals->util->mpool, info );
|
|
*storage = NULL;
|
|
XP_LOGF( "Removed listener %d from gtk's list of listened-to sockets", oldSock );
|
|
} else {
|
|
XP_ASSERT( !globals->acceptor || (func == globals->acceptor) );
|
|
globals->acceptor = func;
|
|
|
|
channel = g_io_channel_unix_new( listener );
|
|
g_io_channel_set_close_on_unref( channel, TRUE );
|
|
watch = g_io_add_watch( channel,
|
|
G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI,
|
|
acceptorInput, globals );
|
|
g_io_channel_unref( channel ); /* only main loop holds it now */
|
|
XP_LOGF( "%s: g_io_add_watch(%d) => %d", __func__, listener, watch );
|
|
|
|
XP_ASSERT( NULL == info );
|
|
info = XP_MALLOC( globals->util->mpool, sizeof(*info) );
|
|
info->channel = channel;
|
|
info->watch = watch;
|
|
info->socket = listener;
|
|
*storage = info;
|
|
}
|
|
} /* gtk_socket_acceptor */
|
|
#endif /* #ifndef XWFEATURE_STANDALONE_ONLY */
|
|
|
|
/* int */
|
|
/* board_main( LaunchParams* params ) */
|
|
/* { */
|
|
/* GtkGameGlobals globals; */
|
|
/* initGlobals( &globals, params ); */
|
|
|
|
/* if ( !!params->pipe && !!params->fileName ) { */
|
|
/* read_pipe_then_close( &globals.cGlobals, NULL ); */
|
|
/* } else { */
|
|
/* gtk_widget_show( globals.window ); */
|
|
|
|
/* gtk_main(); */
|
|
/* } */
|
|
/* /\* MONCONTROL(1); *\/ */
|
|
|
|
/* cleanup( &globals ); */
|
|
|
|
/* return 0; */
|
|
/* } */
|
|
|
|
static void
|
|
initGlobalsNoDraw( GtkGameGlobals* globals, LaunchParams* params,
|
|
CurGameInfo* gi )
|
|
{
|
|
memset( globals, 0, sizeof(*globals) );
|
|
|
|
globals->cGlobals.gi = &globals->gi;
|
|
if ( !gi ) {
|
|
gi = ¶ms->pgi;
|
|
}
|
|
gi_copy( MPPARM(params->mpool) globals->cGlobals.gi, gi );
|
|
|
|
globals->cGlobals.params = params;
|
|
globals->cGlobals.lastNTilesToUse = MAX_TRAY_TILES;
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
# ifdef XWFEATURE_RELAY
|
|
globals->cGlobals.relaySocket = -1;
|
|
# endif
|
|
|
|
globals->cGlobals.socketAdded = gtk_socket_added;
|
|
globals->cGlobals.socketAddedClosure = globals;
|
|
globals->cGlobals.onSave = onGameSaved;
|
|
globals->cGlobals.onSaveClosure = globals;
|
|
globals->cGlobals.addAcceptor = gtk_socket_acceptor;
|
|
#endif
|
|
|
|
globals->cGlobals.cp.showBoardArrow = XP_TRUE;
|
|
globals->cGlobals.cp.hideTileValues = params->hideValues;
|
|
globals->cGlobals.cp.skipCommitConfirm = params->skipCommitConfirm;
|
|
globals->cGlobals.cp.sortNewTiles = params->sortNewTiles;
|
|
globals->cGlobals.cp.showColors = params->showColors;
|
|
globals->cGlobals.cp.allowPeek = params->allowPeek;
|
|
globals->cGlobals.cp.showRobotScores = params->showRobotScores;
|
|
#ifdef XWFEATURE_SLOW_ROBOT
|
|
globals->cGlobals.cp.robotThinkMin = params->robotThinkMin;
|
|
globals->cGlobals.cp.robotThinkMax = params->robotThinkMax;
|
|
globals->cGlobals.cp.robotTradePct = params->robotTradePct;
|
|
#endif
|
|
#ifdef XWFEATURE_CROSSHAIRS
|
|
globals->cGlobals.cp.hideCrosshairs = params->hideCrosshairs;
|
|
#endif
|
|
|
|
setupUtil( &globals->cGlobals );
|
|
setupGtkUtilCallbacks( globals, globals->cGlobals.util );
|
|
}
|
|
|
|
void
|
|
initGlobals( GtkGameGlobals* globals, LaunchParams* params, CurGameInfo* gi )
|
|
{
|
|
CommonGlobals* cGlobals = &globals->cGlobals;
|
|
short width, height;
|
|
GtkWidget* window;
|
|
GtkWidget* drawing_area;
|
|
GtkWidget* menubar;
|
|
GtkWidget* vbox;
|
|
GtkWidget* hbox;
|
|
|
|
initGlobalsNoDraw( globals, params, gi );
|
|
if ( !!gi ) {
|
|
XP_ASSERT( !cGlobals->dict );
|
|
cGlobals->dict = linux_dictionary_make( MEMPOOL params,
|
|
gi->dictName, XP_TRUE );
|
|
gi->dictLang = dict_getLangCode( cGlobals->dict );
|
|
}
|
|
|
|
globals->window = window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
|
|
if ( !!params->fileName ) {
|
|
gtk_window_set_title( GTK_WINDOW(window), params->fileName );
|
|
}
|
|
|
|
vbox = gtk_vbox_new (FALSE, 0);
|
|
gtk_container_add( GTK_CONTAINER(window), vbox );
|
|
gtk_widget_show( vbox );
|
|
|
|
g_signal_connect( G_OBJECT (window), "destroy",
|
|
G_CALLBACK( destroy_board_window ), globals );
|
|
|
|
menubar = makeMenus( globals );
|
|
gtk_box_pack_start( GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
|
|
|
|
#if ! defined XWFEATURE_STANDALONE_ONLY && defined DEBUG
|
|
globals->drop_checks_vbox = gtk_vbox_new( FALSE, 0 );
|
|
gtk_box_pack_start( GTK_BOX(vbox), globals->drop_checks_vbox,
|
|
FALSE, TRUE, 0 );
|
|
#endif
|
|
|
|
gtk_box_pack_start( GTK_BOX(vbox), makeButtons( globals ), FALSE, TRUE, 0);
|
|
|
|
drawing_area = gtk_drawing_area_new();
|
|
globals->drawing_area = drawing_area;
|
|
gtk_widget_show( drawing_area );
|
|
|
|
width = GTK_HOR_SCORE_WIDTH + GTK_TIMER_WIDTH + GTK_TIMER_PAD;
|
|
if ( params->verticalScore ) {
|
|
width += GTK_VERT_SCORE_WIDTH;
|
|
}
|
|
height = 196;
|
|
if ( params->nHidden == 0 ) {
|
|
height += GTK_MIN_SCALE * GTK_TRAY_HT_ROWS;
|
|
}
|
|
|
|
gtk_widget_set_size_request( GTK_WIDGET(drawing_area), width, height );
|
|
|
|
hbox = gtk_hbox_new( FALSE, 0 );
|
|
gtk_box_pack_start( GTK_BOX (hbox), drawing_area, TRUE, TRUE, 0);
|
|
|
|
/* install scrollbar even if not needed -- since zooming can make it
|
|
needed later */
|
|
GtkWidget* vscrollbar;
|
|
gint nRows = cGlobals->gi->boardSize;
|
|
globals->adjustment = (GtkAdjustment*)
|
|
gtk_adjustment_new( 0, 0, nRows, 1, 2,
|
|
nRows - params->nHidden );
|
|
vscrollbar = gtk_vscrollbar_new( globals->adjustment );
|
|
g_signal_connect( GTK_OBJECT(globals->adjustment), "value_changed",
|
|
G_CALLBACK(scroll_value_changed), globals );
|
|
gtk_widget_show( vscrollbar );
|
|
gtk_box_pack_start( GTK_BOX(hbox), vscrollbar, TRUE, TRUE, 0 );
|
|
|
|
gtk_box_pack_start( GTK_BOX (hbox),
|
|
makeVerticalBar( globals, window ),
|
|
FALSE, TRUE, 0 );
|
|
gtk_widget_show( hbox );
|
|
|
|
gtk_box_pack_start( GTK_BOX(vbox), hbox/* drawing_area */, TRUE, TRUE, 0);
|
|
|
|
g_signal_connect( GTK_OBJECT(drawing_area), "expose_event",
|
|
G_CALLBACK(expose_event), globals );
|
|
g_signal_connect( GTK_OBJECT(drawing_area),"configure_event",
|
|
G_CALLBACK(configure_event), globals );
|
|
g_signal_connect( GTK_OBJECT(drawing_area), "button_press_event",
|
|
G_CALLBACK(button_press_event), globals );
|
|
g_signal_connect( GTK_OBJECT(drawing_area), "motion_notify_event",
|
|
G_CALLBACK(motion_notify_event), globals );
|
|
g_signal_connect( GTK_OBJECT(drawing_area), "button_release_event",
|
|
G_CALLBACK(button_release_event), globals );
|
|
|
|
setOneSecondTimer( cGlobals );
|
|
|
|
#ifdef KEY_SUPPORT
|
|
# ifdef KEYBOARD_NAV
|
|
g_signal_connect( GTK_OBJECT(window), "key_press_event",
|
|
G_CALLBACK(key_press_event), globals );
|
|
# endif
|
|
g_signal_connect( GTK_OBJECT(window), "key_release_event",
|
|
G_CALLBACK(key_release_event), globals );
|
|
#endif
|
|
|
|
gtk_widget_set_events( drawing_area, GDK_EXPOSURE_MASK
|
|
| GDK_LEAVE_NOTIFY_MASK
|
|
| GDK_BUTTON_PRESS_MASK
|
|
| GDK_POINTER_MOTION_MASK
|
|
| GDK_BUTTON_RELEASE_MASK
|
|
#ifdef KEY_SUPPORT
|
|
# ifdef KEYBOARD_NAV
|
|
| GDK_KEY_PRESS_MASK
|
|
# endif
|
|
| GDK_KEY_RELEASE_MASK
|
|
#endif
|
|
/* | GDK_POINTER_MOTION_HINT_MASK */
|
|
);
|
|
} /* initGlobals */
|
|
|
|
void
|
|
freeGlobals( GtkGameGlobals* globals )
|
|
{
|
|
cleanup( globals );
|
|
}
|
|
|
|
XP_Bool
|
|
loadGameNoDraw( GtkGameGlobals* globals, LaunchParams* params,
|
|
sqlite3_int64 rowid )
|
|
{
|
|
LOG_FUNC();
|
|
sqlite3* pDb = params->pDb;
|
|
initGlobalsNoDraw( globals, params, NULL );
|
|
|
|
TransportProcs procs;
|
|
setTransportProcs( &procs, globals );
|
|
|
|
CommonGlobals* cGlobals = &globals->cGlobals;
|
|
cGlobals->selRow = rowid;
|
|
cGlobals->pDb = pDb;
|
|
XWStreamCtxt* stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
|
|
params->vtMgr, cGlobals,
|
|
CHANNEL_NONE, NULL );
|
|
XP_Bool loaded = loadGame( stream, cGlobals->pDb, rowid );
|
|
if ( loaded ) {
|
|
if ( NULL == cGlobals->dict ) {
|
|
cGlobals->dict = makeDictForStream( cGlobals, stream );
|
|
}
|
|
loaded = game_makeFromStream( MEMPOOL stream, &cGlobals->game,
|
|
cGlobals->gi, cGlobals->dict,
|
|
&cGlobals->dicts, cGlobals->util,
|
|
(DrawCtx*)NULL, &cGlobals->cp, &procs );
|
|
if ( loaded ) {
|
|
XP_LOGF( "%s: game loaded", __func__ );
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
if ( !!globals->cGlobals.game.comms ) {
|
|
comms_resendAll( globals->cGlobals.game.comms, XP_FALSE );
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
stream_destroy( stream );
|
|
return loaded;
|
|
}
|
|
|
|
XP_Bool
|
|
makeNewGame( GtkGameGlobals* globals )
|
|
{
|
|
CommonGlobals* cGlobals = &globals->cGlobals;
|
|
if ( !!cGlobals->game.comms ) {
|
|
comms_getAddr( cGlobals->game.comms, &cGlobals->addr );
|
|
} else {
|
|
LaunchParams* params = globals->cGlobals.params;
|
|
const XP_UCHAR* relayName = params->connInfo.relay.relayName;
|
|
if ( !relayName ) {
|
|
relayName = RELAY_NAME_DEFAULT;
|
|
}
|
|
XP_U16 relayPort = params->connInfo.relay.defaultSendPort;
|
|
if ( 0 == relayPort ) {
|
|
relayPort = RELAY_PORT_DEFAULT;
|
|
}
|
|
comms_getInitialAddr( &cGlobals->addr, relayName, relayPort );
|
|
}
|
|
|
|
CurGameInfo* gi = cGlobals->gi;
|
|
XP_Bool success = newGameDialog( globals, gi, &cGlobals->addr,
|
|
XP_TRUE, XP_FALSE );
|
|
if ( success && !!gi->dictName && !cGlobals->dict ) {
|
|
cGlobals->dict =
|
|
linux_dictionary_make( MEMPOOL cGlobals->params,
|
|
gi->dictName, XP_TRUE );
|
|
gi->dictLang = dict_getLangCode( cGlobals->dict );
|
|
}
|
|
LOG_RETURNF( "%d", success );
|
|
return success;
|
|
}
|
|
|
|
#endif /* PLATFORM_GTK */
|