mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-02 06:20:14 +01:00
9f3250cb3f
UI for the DB and the assumption is only one game will be stored but this should let the test script be modified to test the new protocol.
2632 lines
81 KiB
C
2632 lines
81 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 "gtkask.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 );
|
|
|
|
|
|
#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;
|
|
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 )
|
|
{
|
|
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 ) {
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)closure;
|
|
(void)gtkask_timeout( globals->window, buf, GTK_BUTTONS_OK, 500 );
|
|
}
|
|
}
|
|
|
|
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:
|
|
gtkask_timeout( globals->window,
|
|
"relay says another device deleted game.",
|
|
GTK_BUTTONS_OK, 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;
|
|
if ( !!globals->cGlobals.pDb && !globals->draw ) {
|
|
XP_S16 nSent = relaycon_sendnoconn( globals->cGlobals.params, msg, len,
|
|
relayID, globals->cGlobals.selRow );
|
|
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
|
|
}
|
|
|
|
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 ) ) {
|
|
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;
|
|
if ( 0 ) {
|
|
#ifdef XWFEATURE_RELAY
|
|
} else if ( addr.conType == 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 ); */
|
|
#endif
|
|
#ifdef XWFEATURE_BLUETOOTH
|
|
} else if ( addr.conType == 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) );
|
|
#endif
|
|
#ifdef XWFEATURE_IP_DIRECT
|
|
} else if ( addr.conType == 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;
|
|
#endif
|
|
#ifdef XWFEATURE_SMS
|
|
} else if ( addr.conType == COMMS_CONN_SMS ) {
|
|
XP_STRNCPY( addr.u.sms.phone, params->connInfo.sms.serverPhone,
|
|
sizeof(addr.u.sms.phone) - 1 );
|
|
addr.u.sms.port = params->connInfo.sms.port;
|
|
#endif
|
|
}
|
|
|
|
/* 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 ) {
|
|
XP_ASSERT( COMMS_CONN_RELAY == addr.conType );
|
|
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 );
|
|
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 );
|
|
|
|
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 )
|
|
{
|
|
short bdWidth, bdHeight;
|
|
short timerLeft, timerTop;
|
|
gint hscale, vscale;
|
|
gint trayTop;
|
|
gint boardTop = 0;
|
|
XP_U16 netStatWidth = 0;
|
|
gint nCols;
|
|
gint nRows;
|
|
|
|
if ( globals->draw == NULL ) {
|
|
createOrLoadObjects( globals );
|
|
}
|
|
|
|
nCols = globals->cGlobals.gi.boardSize;
|
|
nRows = nCols;
|
|
bdWidth = widget->allocation.width - (GTK_RIGHT_MARGIN
|
|
+ GTK_BOARD_LEFT_MARGIN);
|
|
if ( globals->cGlobals.params->verticalScore ) {
|
|
bdWidth -= GTK_VERT_SCORE_WIDTH;
|
|
}
|
|
bdHeight = widget->allocation.height - (GTK_TOP_MARGIN + GTK_BOTTOM_MARGIN)
|
|
- GTK_MIN_TRAY_SCALEV - GTK_BOTTOM_MARGIN;
|
|
|
|
hscale = bdWidth / nCols;
|
|
if ( 0 != globals->cGlobals.params->nHidden ) {
|
|
vscale = hscale;
|
|
} else {
|
|
vscale = (bdHeight / (nCols + GTK_TRAY_HT_ROWS)); /* makd tray height
|
|
3x cell height */
|
|
}
|
|
|
|
if ( !globals->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 * globals->cGlobals.params->nHidden;
|
|
board_setPos( globals->cGlobals.game.board, GTK_BOARD_LEFT, boardTop,
|
|
hscale * nCols, vscale * nRows, hscale * 4, XP_FALSE );
|
|
/* board_setScale( globals->cGlobals.game.board, hscale, vscale ); */
|
|
globals->gridOn = XP_TRUE;
|
|
|
|
if ( !!globals->cGlobals.game.comms ) {
|
|
netStatWidth = GTK_NETSTAT_WIDTH;
|
|
}
|
|
|
|
timerTop = GTK_TIMER_TOP;
|
|
if ( globals->cGlobals.params->verticalScore ) {
|
|
timerLeft = GTK_BOARD_LEFT + (hscale*nCols) + 1;
|
|
board_setScoreboardLoc( globals->cGlobals.game.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( globals->cGlobals.game.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 ( globals->cGlobals.game.comms ) {
|
|
globals->netStatLeft = timerLeft + GTK_TIMER_WIDTH;
|
|
globals->netStatTop = 0;
|
|
}
|
|
|
|
board_setTimerLoc( globals->cGlobals.game.board, timerLeft, timerTop,
|
|
GTK_TIMER_WIDTH, GTK_HOR_SCORE_HEIGHT );
|
|
|
|
board_setTrayLoc( globals->cGlobals.game.board, GTK_TRAY_LEFT, trayTop,
|
|
hscale * nCols, vscale * GTK_TRAY_HT_ROWS + 10,
|
|
GTK_DIVIDER_WIDTH );
|
|
|
|
setCtrlsForTray( globals );
|
|
|
|
board_invalAll( globals->cGlobals.game.board );
|
|
|
|
XP_Bool inOut[2];
|
|
board_zoom( globals->cGlobals.game.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 )
|
|
{
|
|
saveGame( &globals->cGlobals );
|
|
|
|
#ifdef XWFEATURE_BLUETOOTH
|
|
linux_bt_close( &globals->cGlobals );
|
|
#endif
|
|
#ifdef XWFEATURE_SMS
|
|
linux_sms_close( &globals->cGlobals );
|
|
#endif
|
|
#ifdef XWFEATURE_IP_DIRECT
|
|
linux_udp_close( &globals->cGlobals );
|
|
#endif
|
|
#ifdef XWFEATURE_RELAY
|
|
linux_close_socket( &globals->cGlobals );
|
|
#endif
|
|
game_dispose( &globals->cGlobals.game ); /* takes care of the dict */
|
|
gi_disposePlayerInfo( MEMPOOL &globals->cGlobals.gi );
|
|
|
|
linux_util_vt_destroy( globals->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 )
|
|
{
|
|
if ( !!globals->cGlobals.game.server ) {
|
|
XWStreamCtxt* stream =
|
|
mem_stream_make( MEMPOOL
|
|
globals->cGlobals.params->vtMgr,
|
|
globals,
|
|
CHANNEL_NONE,
|
|
catOnClose );
|
|
server_formatDictCounts( globals->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 ( gtkask( globals->window,
|
|
"Are you sure you want to resign?",
|
|
GTK_BUTTONS_YES_NO ) ) {
|
|
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 );
|
|
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
|
|
|
|
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
|
|
|
|
/* (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_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 );
|
|
|
|
XP_Bool canHing = board_canHint( globals->cGlobals.game.board );
|
|
gtk_widget_set_sensitive( globals->prevhint_button, canHing );
|
|
gtk_widget_set_sensitive( globals->nexthint_button, canHing );
|
|
|
|
#ifdef XWFEATURE_CHAT
|
|
XP_Bool canChat = !!globals->cGlobals.game.comms
|
|
&& comms_canChat( globals->cGlobals.game.comms );
|
|
gtk_widget_set_sensitive( globals->chat_button, 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_done_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_done_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
|
|
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 );
|
|
|
|
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 : 500;
|
|
(void)gtkask_timeout( globals->window, text, GTK_BUTTONS_OK, timeout );
|
|
|
|
free( text );
|
|
} /* 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 );
|
|
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, 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 )
|
|
{
|
|
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 );
|
|
}
|
|
|
|
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. */
|
|
gtk_idle_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 = gtk_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 = gtkask( globals->window, buf, GTK_BUTTONS_YES_NO );
|
|
}
|
|
|
|
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 );
|
|
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 );
|
|
}
|
|
#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;
|
|
|
|
XP_UCHAR scoreExpl[48] = {0};
|
|
XP_U16 explLen = sizeof(scoreExpl);
|
|
|
|
if ( model_getPlayersLastScore( globals->cGlobals.game.model,
|
|
player, scoreExpl, &explLen ) ) {
|
|
XP_LOGF( "got: %s", scoreExpl );
|
|
}
|
|
}
|
|
#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(%d)", __func__, id );
|
|
|
|
if ( silent ) {
|
|
XP_LOGF( "%s", message );
|
|
} else {
|
|
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;
|
|
}
|
|
|
|
result = gtkask( globals->window, question, buttons );
|
|
|
|
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 gtkask( globals->window, question, GTK_BUTTONS_YES_NO );
|
|
}
|
|
|
|
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*
|
|
makeButtons( GtkGameGlobals* globals )
|
|
{
|
|
short i;
|
|
GtkWidget* hbox;
|
|
GtkWidget* button;
|
|
|
|
struct {
|
|
char* name;
|
|
GCallback func;
|
|
} buttons[] = {
|
|
/* { "Flip", handle_flip_button }, */
|
|
{ "Grid", G_CALLBACK(handle_grid_button) },
|
|
{ "Hide", G_CALLBACK(handle_hide_button) },
|
|
{ "Commit", G_CALLBACK(handle_commit_button) },
|
|
};
|
|
|
|
hbox = gtk_hbox_new( FALSE, 0 );
|
|
|
|
for ( i = 0; i < sizeof(buttons)/sizeof(*buttons); ++i ) {
|
|
button = gtk_button_new_with_label( buttons[i].name );
|
|
gtk_widget_show( button );
|
|
g_signal_connect( GTK_OBJECT(button), "clicked",
|
|
G_CALLBACK(buttons[i].func), globals );
|
|
|
|
gtk_box_pack_start( GTK_BOX(hbox), button, FALSE, TRUE, 0);
|
|
}
|
|
|
|
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
|
|
static gboolean
|
|
newConnectionInput( GIOChannel *source,
|
|
GIOCondition condition,
|
|
gpointer data )
|
|
{
|
|
gboolean keepSource = TRUE;
|
|
int sock = g_io_channel_unix_get_fd( source );
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)data;
|
|
|
|
XP_LOGF( "%s(%p):condition = 0x%x", __func__, source, (int)condition );
|
|
|
|
/* XP_ASSERT( sock == globals->cGlobals.socket ); */
|
|
|
|
if ( (condition & G_IO_IN) != 0 ) {
|
|
ssize_t nRead;
|
|
unsigned char buf[512];
|
|
CommsAddrRec* addrp = NULL;
|
|
#if defined XWFEATURE_IP_DIRECT || defined XWFEATURE_SMS
|
|
CommsAddrRec addr;
|
|
#endif
|
|
|
|
switch ( comms_getConType( globals->cGlobals.game.comms ) ) {
|
|
#ifdef XWFEATURE_RELAY
|
|
case COMMS_CONN_RELAY:
|
|
XP_ASSERT( globals->cGlobals.socket == sock );
|
|
nRead = linux_relay_receive( &globals->cGlobals, buf, sizeof(buf) );
|
|
break;
|
|
#endif
|
|
#ifdef XWFEATURE_BLUETOOTH
|
|
case COMMS_CONN_BT:
|
|
nRead = linux_bt_receive( sock, buf, sizeof(buf) );
|
|
break;
|
|
#endif
|
|
#ifdef XWFEATURE_SMS
|
|
case COMMS_CONN_SMS:
|
|
addrp = &addr;
|
|
nRead = linux_sms_receive( &globals->cGlobals, sock,
|
|
buf, sizeof(buf), addrp );
|
|
break;
|
|
#endif
|
|
#ifdef XWFEATURE_IP_DIRECT
|
|
case COMMS_CONN_IP_DIRECT:
|
|
addrp = &addr;
|
|
nRead = linux_udp_receive( sock, buf, sizeof(buf), addrp, &globals->cGlobals );
|
|
break;
|
|
#endif
|
|
default:
|
|
XP_ASSERT( 0 );
|
|
}
|
|
|
|
if ( !globals->dropIncommingMsgs && nRead > 0 ) {
|
|
XWStreamCtxt* inboundS;
|
|
XP_Bool redraw = XP_FALSE;
|
|
|
|
inboundS = stream_from_msgbuf( &globals->cGlobals, buf, nRead );
|
|
if ( !!inboundS ) {
|
|
XP_LOGF( "%s: got %d bytes", __func__, nRead );
|
|
if ( comms_checkIncomingStream( globals->cGlobals.game.comms,
|
|
inboundS, addrp ) ) {
|
|
redraw =
|
|
server_receiveMessage(globals->cGlobals.game.server,
|
|
inboundS );
|
|
if ( redraw ) {
|
|
saveGame( &globals->cGlobals );
|
|
}
|
|
}
|
|
stream_destroy( inboundS );
|
|
}
|
|
|
|
/* if there's something to draw resulting from the message, we
|
|
need to give the main loop time to reflect that on the screen
|
|
before giving the server another shot. So just call the idle
|
|
proc. */
|
|
if ( redraw ) {
|
|
gtk_util_requestTime( globals->cGlobals.util );
|
|
} else {
|
|
redraw = server_do( globals->cGlobals.game.server );
|
|
}
|
|
if ( redraw ) {
|
|
board_draw( globals->cGlobals.game.board );
|
|
}
|
|
} else {
|
|
XP_LOGF( "errno from read: %d/%s", errno, strerror(errno) );
|
|
}
|
|
}
|
|
|
|
if ( (condition & (G_IO_HUP | G_IO_ERR)) != 0 ) {
|
|
XP_LOGF( "dropping socket %d", sock );
|
|
close( sock );
|
|
#ifdef XWFEATURE_RELAY
|
|
globals->cGlobals.socket = -1;
|
|
#endif
|
|
if ( 0 ) {
|
|
#ifdef XWFEATURE_BLUETOOTH
|
|
} else if ( COMMS_CONN_BT == globals->cGlobals.params->conType ) {
|
|
linux_bt_socketclosed( &globals->cGlobals, sock );
|
|
#endif
|
|
#ifdef XWFEATURE_IP_DIRECT
|
|
} else if ( COMMS_CONN_IP_DIRECT == globals->cGlobals.params->conType ) {
|
|
linux_udp_socketclosed( &globals->cGlobals, sock );
|
|
#endif
|
|
}
|
|
keepSource = FALSE; /* remove the event source */
|
|
}
|
|
|
|
return keepSource; /* FALSE means to remove event source */
|
|
} /* newConnectionInput */
|
|
|
|
typedef struct _SockInfo {
|
|
GIOChannel* channel;
|
|
guint watch;
|
|
int socket;
|
|
} SockInfo;
|
|
|
|
static void
|
|
gtk_socket_changed( void* closure, int oldSock, int newSock, void** storage )
|
|
{
|
|
GtkGameGlobals* globals = (GtkGameGlobals*)closure;
|
|
SockInfo* info = (SockInfo*)*storage;
|
|
XP_LOGF( "%s(old:%d; new:%d)", __func__, oldSock, newSock );
|
|
|
|
if ( oldSock != -1 ) {
|
|
XP_ASSERT( info != NULL );
|
|
g_source_remove( info->watch );
|
|
g_io_channel_unref( info->channel );
|
|
XP_FREE( globals->cGlobals.util->mpool, info );
|
|
*storage = NULL;
|
|
XP_LOGF( "Removed socket %d from gtk's list of listened-to sockets",
|
|
oldSock );
|
|
}
|
|
if ( newSock != -1 ) {
|
|
info = (SockInfo*)XP_MALLOC( globals->cGlobals.util->mpool,
|
|
sizeof(*info) );
|
|
GIOChannel* channel = g_io_channel_unix_new( newSock );
|
|
g_io_channel_set_close_on_unref( channel, TRUE );
|
|
guint result = g_io_add_watch( channel,
|
|
G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI,
|
|
newConnectionInput,
|
|
globals );
|
|
info->channel = channel;
|
|
info->watch = result;
|
|
*storage = info;
|
|
XP_LOGF( "g_io_add_watch(%d) => %d", newSock, result );
|
|
}
|
|
#ifdef XWFEATURE_RELAY
|
|
globals->cGlobals.socket = newSock;
|
|
#endif
|
|
/* A hack for the bluetooth case. */
|
|
CommsCtxt* comms = globals->cGlobals.game.comms;
|
|
if ( (comms != NULL) && (comms_getConType(comms) == 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 */
|
|
|
|
static void
|
|
drop_msg_toggle( GtkWidget* toggle, GtkGameGlobals* globals )
|
|
{
|
|
globals->dropIncommingMsgs = gtk_toggle_button_get_active(
|
|
GTK_TOGGLE_BUTTON(toggle) );
|
|
} /* drop_msg_toggle */
|
|
#endif
|
|
|
|
/* 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 )
|
|
{
|
|
memset( globals, 0, sizeof(*globals) );
|
|
|
|
globals->cGlobals.params = params;
|
|
globals->cGlobals.lastNTilesToUse = MAX_TRAY_TILES;
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
# ifdef XWFEATURE_RELAY
|
|
globals->cGlobals.socket = -1;
|
|
# endif
|
|
|
|
globals->cGlobals.socketChanged = gtk_socket_changed;
|
|
globals->cGlobals.socketChangedClosure = 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 )
|
|
{
|
|
short width, height;
|
|
GtkWidget* window;
|
|
GtkWidget* drawing_area;
|
|
GtkWidget* menubar;
|
|
GtkWidget* buttonbar;
|
|
GtkWidget* vbox;
|
|
GtkWidget* hbox;
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
GtkWidget* dropCheck;
|
|
#endif
|
|
|
|
initGlobalsNoDraw( globals, params );
|
|
|
|
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);
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
dropCheck = gtk_check_button_new_with_label( "drop incoming messages" );
|
|
g_signal_connect( GTK_OBJECT(dropCheck),
|
|
"toggled", G_CALLBACK(drop_msg_toggle), globals );
|
|
gtk_box_pack_start( GTK_BOX(vbox), dropCheck, FALSE, TRUE, 0);
|
|
gtk_widget_show( dropCheck );
|
|
#endif
|
|
|
|
buttonbar = makeButtons( globals );
|
|
gtk_box_pack_start( GTK_BOX(vbox), buttonbar, 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 ( globals->cGlobals.params->verticalScore ) {
|
|
width += GTK_VERT_SCORE_WIDTH;
|
|
}
|
|
height = 196;
|
|
if ( globals->cGlobals.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 = globals->cGlobals.gi.boardSize;
|
|
globals->adjustment = (GtkAdjustment*)
|
|
gtk_adjustment_new( 0, 0, nRows, 1, 2,
|
|
nRows - globals->cGlobals.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( &globals->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* pDb, sqlite3_int64 rowid )
|
|
{
|
|
LOG_FUNC();
|
|
initGlobalsNoDraw( globals, params );
|
|
|
|
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 {
|
|
comms_getInitialAddr( &cGlobals->addr, RELAY_NAME_DEFAULT,
|
|
RELAY_PORT_DEFAULT );
|
|
}
|
|
|
|
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 */
|