xwords/xwords4/linux/gtkmain.c

1666 lines
50 KiB
C
Raw Normal View History

2003-11-01 06:35:29 +01:00
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 2000-2003 by Eric House (xwords@eehouse.org). All rights reserved.
2003-11-01 06:35:29 +01:00
*
* 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 <sys/stat.h>
#include <ctype.h>
#include <gdk/gdkkeysyms.h>
2005-09-09 04:57:14 +02:00
#include <errno.h>
2003-11-01 06:35:29 +01:00
#ifndef CLIENT_ONLY
/* # include <prc.h> */
#endif
#include <sys/types.h>
#include <unistd.h>
#include "main.h"
#include "linuxmain.h"
/* #include "gtkmain.h" */
#include "draw.h"
#include "game.h"
#include "gtkask.h"
#include "gtknewgame.h"
#include "gtkletterask.h"
#include "gtkpasswdask.h"
#include "gtkntilesask.h"
2003-11-01 06:35:29 +01:00
/* #include "undo.h" */
#include "gtkdraw.h"
#include "memstream.h"
#include "filestream.h"
/* static guint gtkSetupClientSocket( GtkAppGlobals* globals, int sock ); */
static void sendOnClose( XWStreamCtxt* stream, void* closure );
static XP_Bool file_exists( const char* fileName );
2003-11-01 06:35:29 +01:00
static void gtkListenOnSocket( GtkAppGlobals* globals, int newSock );
static void setCtrlsForTray( GtkAppGlobals* globals );
static void printFinalScores( GtkAppGlobals* globals );
#if 0
static XWStreamCtxt*
lookupClientStream( GtkAppGlobals* 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( GtkAppGlobals* 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 gint
button_press_event( GtkWidget *widget, GdkEventButton *event,
GtkAppGlobals* globals )
{
XP_Bool redraw, handled;
globals->mouseDown = XP_TRUE;
redraw = board_handlePenDown( globals->cGlobals.game.board,
2005-07-14 04:54:47 +02:00
event->x, event->y, &handled );
2003-11-01 06:35:29 +01:00
if ( redraw ) {
board_draw( globals->cGlobals.game.board );
}
return 1;
} /* button_press_event */
static gint
motion_notify_event( GtkWidget *widget, GdkEventMotion *event,
GtkAppGlobals* globals )
2003-11-01 06:35:29 +01:00
{
XP_Bool handled;
if ( globals->mouseDown ) {
handled = board_handlePenMove( globals->cGlobals.game.board, event->x,
event->y );
if ( handled ) {
board_draw( globals->cGlobals.game.board );
}
} else {
handled = XP_FALSE;
2003-11-01 06:35:29 +01:00
}
return handled;
} /* motion_notify_event */
static gint
button_release_event( GtkWidget *widget, GdkEventMotion *event,
GtkAppGlobals* globals )
2003-11-01 06:35:29 +01:00
{
XP_Bool redraw;
if ( globals->mouseDown ) {
redraw = board_handlePenUp( globals->cGlobals.game.board,
event->x,
event->y, event->time );
if ( redraw ) {
board_draw( globals->cGlobals.game.board );
}
globals->mouseDown = XP_FALSE;
2003-11-01 06:35:29 +01:00
}
return 1;
} /* button_release_event */
static gint
key_release_event( GtkWidget *widget, GdkEventKey* event,
GtkAppGlobals* globals )
2003-11-01 06:35:29 +01:00
{
XP_Key xpkey = XP_KEY_NONE;
guint keyval = event->keyval;
XP_LOGF( "got key 0x%x", keyval );
switch( keyval ) {
case GDK_Left:
xpkey = XP_CURSOR_KEY_LEFT;
break;
case GDK_Right:
xpkey = XP_CURSOR_KEY_RIGHT;
break;
case GDK_Up:
xpkey = XP_CURSOR_KEY_UP;
break;
case GDK_Down:
xpkey = XP_CURSOR_KEY_DOWN;
break;
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;
}
return FALSE;
}
if ( xpkey != XP_KEY_NONE ) {
if ( board_handleKey( globals->cGlobals.game.board, xpkey ) ) {
board_draw( globals->cGlobals.game.board );
}
}
return 0;
} /* key_release_event */
#ifdef MEM_DEBUG
# define MEMPOOL globals->cGlobals.params->util->mpool,
#else
# define MEMPOOL
#endif
static XWStreamCtxt*
streamFromFile( GtkAppGlobals* globals, char* name )
{
XP_U8* buf;
struct stat statBuf;
FILE* f;
XWStreamCtxt* stream;
(void)stat( name, &statBuf );
buf = malloc( statBuf.st_size );
f = fopen( name, "r" );
fread( buf, statBuf.st_size, 1, f );
fclose( f );
stream = mem_stream_make( MEMPOOL
globals->cGlobals.params->vtMgr,
globals, CHANNEL_NONE, NULL );
stream_putBytes( stream, buf, statBuf.st_size );
free( buf );
return stream;
} /* streamFromFile */
static void
createOrLoadObjects( GtkAppGlobals* globals )
2003-11-01 06:35:29 +01:00
{
XWStreamCtxt* stream = NULL;
Connectedness serverRole = globals->cGlobals.params->serverRole;
XP_Bool isServer = serverRole != SERVER_ISCLIENT;
LaunchParams* params = globals->cGlobals.params;
globals->draw = (GtkDrawCtx*)gtkDrawCtxtMake( globals->drawing_area,
globals );
2003-11-01 06:35:29 +01:00
if ( !!params->fileName && file_exists( params->fileName ) ) {
stream = streamFromFile( globals, params->fileName );
2003-11-01 06:35:29 +01:00
game_makeFromStream( MEMPOOL stream, &globals->cGlobals.game,
&globals->cGlobals.params->gi,
params->dict, params->util,
(DrawCtx*)globals->draw,
&globals->cp,
linux_tcp_send, globals );
2003-11-01 06:35:29 +01:00
stream_destroy( stream );
2003-11-01 06:35:29 +01:00
} else { /* not reading from a saved file */
XP_U16 gameID;
2003-11-01 06:35:29 +01:00
CommsAddrRec addr;
gameID = (XP_U16)util_getCurSeconds( globals->cGlobals.params->util );
XP_ASSERT( !!params->relayName );
globals->cGlobals.defaultServerName = params->relayName;
2003-11-01 06:35:29 +01:00
params->gi.gameID = util_getCurSeconds(globals->cGlobals.params->util);
XP_STATUSF( "grabbed gameID: %ld\n", params->gi.gameID );
2005-01-13 05:30:50 +01:00
2003-11-01 06:35:29 +01:00
game_makeNewGame( MEMPOOL &globals->cGlobals.game, &params->gi,
params->util, (DrawCtx*)globals->draw,
gameID, &globals->cp, linux_tcp_send, globals );
2003-11-01 06:35:29 +01:00
addr.conType = COMMS_CONN_RELAY;
addr.u.ip_relay.ipAddr = 0;
addr.u.ip_relay.port = params->defaultSendPort;
XP_STRNCPY( addr.u.ip_relay.hostName, params->relayName,
sizeof(addr.u.ip_relay.hostName) - 1 );
XP_STRNCPY( addr.u.ip_relay.cookie, params->cookie,
sizeof(addr.u.ip_relay.cookie) - 1 );
/* This may trigger network activity */
2005-03-25 04:13:10 +01:00
if ( !!globals->cGlobals.game.comms ) {
comms_setAddr( globals->cGlobals.game.comms, &addr );
}
2003-11-01 06:35:29 +01:00
model_setDictionary( globals->cGlobals.game.model, params->dict );
/* params->gi.phoniesAction = PHONIES_DISALLOW; */
#ifdef XWFEATURE_SEARCHLIMIT
params->gi.allowHintRect = XP_TRUE;
#endif
2003-11-01 06:35:29 +01:00
if ( !isServer ) {
XWStreamCtxt* stream =
mem_stream_make( MEMPOOL params->vtMgr, globals, CHANNEL_NONE,
sendOnClose );
server_initClientConnection( globals->cGlobals.game.server,
stream );
}
}
server_do( globals->cGlobals.game.server );
} /* 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* event,
GtkAppGlobals* globals )
2003-11-01 06:35:29 +01:00
{
short width, height, leftMargin, topMargin;
short timerLeft, timerTop;
gint hscale, vscale;
gint trayTop;
gint boardTop = 0;
if ( globals->draw == NULL ) {
createOrLoadObjects( globals );
2003-11-01 06:35:29 +01:00
}
width = widget->allocation.width - (RIGHT_MARGIN + BOARD_LEFT_MARGIN);
if ( globals->cGlobals.params->verticalScore ) {
width -= VERT_SCORE_WIDTH;
}
2003-11-01 06:35:29 +01:00
height = widget->allocation.height - (TOP_MARGIN + BOTTOM_MARGIN)
- MIN_TRAY_SCALEV - BOTTOM_MARGIN;
hscale = width / NUM_COLS;
vscale = (height / (NUM_ROWS + 2)); /* makd tray height 2x cell height */
leftMargin = (width - (hscale*NUM_COLS)) / 2;
topMargin = (height - (vscale*(NUM_ROWS*2))) / 2;
2003-11-01 06:35:29 +01:00
if ( !globals->cGlobals.params->verticalScore ) {
boardTop += HOR_SCORE_HEIGHT;
}
trayTop = boardTop + (vscale * NUM_ROWS) + BOTTOM_MARGIN + 1;
if ( globals->cGlobals.params->trayOverlaps ) {
trayTop -= vscale * 2;
}
board_setPos( globals->cGlobals.game.board, BOARD_LEFT, boardTop,
XP_FALSE );
board_setScale( globals->cGlobals.game.board, hscale, vscale );
board_setShowColors( globals->cGlobals.game.board, XP_TRUE );
globals->gridOn = XP_TRUE;
timerTop = TIMER_TOP;
2003-11-01 06:35:29 +01:00
if ( globals->cGlobals.params->verticalScore ) {
timerLeft = BOARD_LEFT + (hscale*NUM_COLS) + 1;
2003-11-01 06:35:29 +01:00
board_setScoreboardLoc( globals->cGlobals.game.board,
timerLeft,
2003-11-01 06:35:29 +01:00
VERT_SCORE_TOP,
VERT_SCORE_WIDTH,
vscale*NUM_COLS,
2003-11-01 06:35:29 +01:00
XP_FALSE );
} else {
timerLeft = BOARD_LEFT + (hscale*NUM_COLS) - TIMER_WIDTH;
2003-11-01 06:35:29 +01:00
board_setScoreboardLoc( globals->cGlobals.game.board,
BOARD_LEFT, HOR_SCORE_TOP,
timerLeft-BOARD_LEFT,
HOR_SCORE_HEIGHT,
2003-11-01 06:35:29 +01:00
XP_TRUE );
}
board_setTimerLoc( globals->cGlobals.game.board, timerLeft, timerTop,
TIMER_WIDTH, HOR_SCORE_HEIGHT );
board_setTrayLoc( globals->cGlobals.game.board, TRAY_LEFT, trayTop,
hscale * NUM_COLS, vscale * 2, GTK_DIVIDER_WIDTH );
2003-11-01 06:35:29 +01:00
setCtrlsForTray( globals );
board_invalAll( globals->cGlobals.game.board );
return TRUE;
} /* configure_event */
/* Redraw the screen from the backing pixmap */
static gint
expose_event( GtkWidget* widget,
GdkEventExpose* event,
GtkAppGlobals* globals )
2003-11-01 06:35:29 +01:00
{
/*
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 );
*/
2005-09-02 08:24:47 +02:00
/* 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 ); */
2003-11-01 06:35:29 +01:00
2005-09-02 08:24:47 +02:00
board_invalAll( globals->cGlobals.game.board );
2003-11-01 06:35:29 +01:00
board_draw( globals->cGlobals.game.board );
/* 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,
GtkAppGlobals* 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
static void
writeToFile( XWStreamCtxt* stream, void* closure )
{
void* buf;
FILE* file;
XP_U16 len;
GtkAppGlobals* globals = (GtkAppGlobals*)closure;
len = stream_getSize( stream );
buf = malloc( len );
stream_getBytes( stream, buf, len );
file = fopen( globals->cGlobals.params->fileName, "w" );
fwrite( buf, 1, len, file );
fclose( file );
free( buf );
} /* writeToFile */
static void
quit( void* dunno, GtkAppGlobals* globals )
{
if ( !!globals->cGlobals.params->fileName ) {
XWStreamCtxt* outStream;
outStream = mem_stream_make( MEMPOOL globals->cGlobals.params->vtMgr,
globals, 0, writeToFile );
stream_open( outStream );
game_saveToStream( &globals->cGlobals.game,
&globals->cGlobals.params->gi,
outStream );
stream_destroy( outStream );
}
game_dispose( &globals->cGlobals.game ); /* takes care of the dict */
gi_disposePlayerInfo( MEMPOOL &globals->cGlobals.params->gi );
vtmgr_destroy( MEMPOOL globals->cGlobals.params->vtMgr );
mpool_destroy( globals->cGlobals.params->util->mpool );
gtk_exit( 0 );
} /* quit */
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* widget, GtkAppGlobals* globals )
{
if ( !!globals->cGlobals.game.server ) {
XWStreamCtxt* stream =
mem_stream_make( MEMPOOL
globals->cGlobals.params->vtMgr,
globals,
CHANNEL_NONE,
catOnClose );
2004-11-09 03:06:32 +01:00
server_formatDictCounts( globals->cGlobals.game.server, stream, 5 );
2003-11-01 06:35:29 +01:00
stream_putU8( stream, '\n' );
stream_destroy( stream );
}
} /* tile_values */
static void
game_history( GtkWidget* widget, GtkAppGlobals* globals )
{
catGameHistory( &globals->cGlobals );
} /* game_history */
static void
final_scores( GtkWidget* widget, GtkAppGlobals* globals )
{
XP_Bool gameOver = server_getGameIsOver( globals->cGlobals.game.server );
if ( gameOver ) {
printFinalScores( globals );
} else {
XP_Bool confirmed;
confirmed =
gtkask( globals,
"Are you sure everybody wants to end the game now?",
2, "Yes", "No" ) == 0;
if ( confirmed ) {
server_endGame( globals->cGlobals.game.server );
gameOver = TRUE;
}
}
/* the end game listener will take care of printing the final scores */
} /* final_scores */
static void
new_game( GtkWidget* widget, GtkAppGlobals* globals )
{
gboolean confirmed;
confirmed = newGameDialog( globals );
if ( confirmed ) {
CurGameInfo* gi = &globals->cGlobals.params->gi;
XP_Bool isClient = gi->serverRole == SERVER_ISCLIENT;
XP_U32 gameID = util_getCurSeconds( globals->cGlobals.params->util );
XP_STATUSF( "grabbed gameID: %ld\n", gameID );
2005-04-03 04:47:08 +02:00
game_reset( MEMPOOL &globals->cGlobals.game, gi,
globals->cGlobals.params->util,
gameID, &globals->cp, linux_tcp_send, globals );
2003-11-01 06:35:29 +01:00
if ( isClient ) {
XWStreamCtxt* stream =
mem_stream_make( MEMPOOL
globals->cGlobals.params->vtMgr,
globals,
CHANNEL_NONE,
sendOnClose );
server_initClientConnection( globals->cGlobals.game.server,
stream );
}
(void)server_do( globals->cGlobals.game.server ); /* assign tiles, etc. */
board_invalAll( globals->cGlobals.game.board );
board_draw( globals->cGlobals.game.board );
}
} /* new_game */
static void
load_game( GtkWidget* widget, GtkAppGlobals* globals )
{
} /* load_game */
static void
save_game( GtkWidget* widget, GtkAppGlobals* globals )
{
} /* save_game */
static void
load_dictionary( GtkWidget* widget, GtkAppGlobals* globals )
{
} /* load_dictionary */
static void
handle_undo( GtkWidget* widget, GtkAppGlobals* globals )
{
} /* handle_undo */
static void
handle_redo( GtkWidget* widget, GtkAppGlobals* globals )
{
} /* handle_redo */
#ifdef FEATURE_TRAY_EDIT
static void
handle_trayEditToggle( GtkWidget* widget, GtkAppGlobals* globals, XP_Bool on )
{
} /* handle_trayEditToggle */
static void
handle_trayEditToggle_on( GtkWidget* widget, GtkAppGlobals* globals )
{
handle_trayEditToggle( widget, globals, XP_TRUE );
}
static void
handle_trayEditToggle_off( GtkWidget* widget, GtkAppGlobals* globals )
{
handle_trayEditToggle( widget, globals, XP_FALSE );
}
#endif
2003-11-01 06:35:29 +01:00
static void
handle_resend( GtkWidget* widget, GtkAppGlobals* globals )
{
CommsCtxt* comms = globals->cGlobals.game.comms;
2005-09-02 08:24:47 +02:00
if ( comms != NULL ) {
comms_resendAll( comms );
}
2003-11-01 06:35:29 +01:00
} /* handle_resend */
#ifdef DEBUG
static void
handle_commstats( GtkWidget* widget, GtkAppGlobals* 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
#ifdef MEM_DEBUG
static void
handle_memstats( GtkWidget* widget, GtkAppGlobals* 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, GtkAppGlobals* globals )
2003-11-01 06:35:29 +01:00
{
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 );
2003-11-01 06:35:29 +01:00
}
gtk_menu_append( GTK_MENU(parent), item );
gtk_widget_show( item );
return item;
} /* createAddItem */
static GtkWidget*
makeMenus( GtkAppGlobals* globals, int argc, char** argv )
{
GtkWidget* menubar = gtk_menu_bar_new();
GtkWidget* fileMenu;
fileMenu = makeAddSubmenu( menubar, "File" );
(void)createAddItem( fileMenu, "Tile values",
GTK_SIGNAL_FUNC(tile_values), globals );
2003-11-01 06:35:29 +01:00
(void)createAddItem( fileMenu, "Game history",
GTK_SIGNAL_FUNC(game_history), globals );
2003-11-01 06:35:29 +01:00
(void)createAddItem( fileMenu, "Final scores",
GTK_SIGNAL_FUNC(final_scores), globals );
2003-11-01 06:35:29 +01:00
(void)createAddItem( fileMenu, "New game",
GTK_SIGNAL_FUNC(new_game), globals );
2003-11-01 06:35:29 +01:00
(void)createAddItem( fileMenu, "Load game",
GTK_SIGNAL_FUNC(load_game), globals );
2003-11-01 06:35:29 +01:00
(void)createAddItem( fileMenu, "Save game",
GTK_SIGNAL_FUNC(save_game), globals );
2003-11-01 06:35:29 +01:00
(void)createAddItem( fileMenu, "Load dictionary",
GTK_SIGNAL_FUNC(load_dictionary), globals );
2003-11-01 06:35:29 +01:00
fileMenu = makeAddSubmenu( menubar, "Edit" );
(void)createAddItem( fileMenu, "Undo",
GTK_SIGNAL_FUNC(handle_undo), globals );
2003-11-01 06:35:29 +01:00
(void)createAddItem( fileMenu, "Redo",
GTK_SIGNAL_FUNC(handle_redo), globals );
2003-11-01 06:35:29 +01:00
#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
2003-11-01 06:35:29 +01:00
fileMenu = makeAddSubmenu( menubar, "Network" );
(void)createAddItem( fileMenu, "Resend",
GTK_SIGNAL_FUNC(handle_resend), globals );
2003-11-01 06:35:29 +01:00
#ifdef DEBUG
(void)createAddItem( fileMenu, "Stats",
GTK_SIGNAL_FUNC(handle_commstats), globals );
2003-11-01 06:35:29 +01:00
#endif
#ifdef MEM_DEBUG
(void)createAddItem( fileMenu, "Mem stats",
GTK_SIGNAL_FUNC(handle_memstats), globals );
2003-11-01 06:35:29 +01:00
#endif
/* (void)createAddItem( fileMenu, "Print board", */
/* GTK_SIGNAL_FUNC(handle_print_board), globals ); */
2003-11-01 06:35:29 +01:00
/* listAllGames( menubar, argc, argv, globals ); */
2003-11-01 06:35:29 +01:00
gtk_widget_show( menubar );
return menubar;
} /* makeMenus */
static gboolean
handle_flip_button( GtkWidget* widget, gpointer _globals )
2003-11-01 06:35:29 +01:00
{
GtkAppGlobals* globals = (GtkAppGlobals*)_globals;
2003-11-01 06:35:29 +01:00
if ( board_flip( globals->cGlobals.game.board ) ) {
board_draw( globals->cGlobals.game.board );
}
return TRUE;
2003-11-01 06:35:29 +01:00
} /* handle_flip_button */
static gboolean
handle_value_button( GtkWidget* widget, gpointer closure )
2003-11-01 06:35:29 +01:00
{
GtkAppGlobals* globals = (GtkAppGlobals*)closure;
2003-11-01 06:35:29 +01:00
if ( board_toggle_showValues( globals->cGlobals.game.board ) ) {
board_draw( globals->cGlobals.game.board );
}
return TRUE;
2003-11-01 06:35:29 +01:00
} /* handle_value_button */
static void
handle_hint_button( GtkWidget* widget, GtkAppGlobals* globals )
{
XP_Bool redo;
if ( board_requestHint( globals->cGlobals.game.board,
#ifdef XWFEATURE_SEARCHLIMIT
XP_FALSE,
#endif
&redo ) ) {
board_draw( globals->cGlobals.game.board );
}
} /* handle_hint_button */
static void
handle_nhint_button( GtkWidget* widget, GtkAppGlobals* globals )
{
XP_Bool redo;
board_resetEngine( globals->cGlobals.game.board );
if ( board_requestHint( globals->cGlobals.game.board,
#ifdef XWFEATURE_SEARCHLIMIT
XP_TRUE,
#endif
&redo ) ) {
2003-11-01 06:35:29 +01:00
board_draw( globals->cGlobals.game.board );
}
} /* handle_hint_button */
static void
handle_colors_button( GtkWidget* widget, GtkAppGlobals* 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* widget, GtkAppGlobals* globals )
{
if ( board_juggleTray( globals->cGlobals.game.board ) ) {
board_draw( globals->cGlobals.game.board );
}
} /* handle_juggle_button */
static void
handle_undo_button( GtkWidget* widget, GtkAppGlobals* globals )
{
if ( server_handleUndo( globals->cGlobals.game.server ) ) {
board_draw( globals->cGlobals.game.board );
}
} /* handle_undo_button */
static void
handle_redo_button( GtkWidget* widget, GtkAppGlobals* globals )
{
} /* handle_redo_button */
static void
handle_trade_button( GtkWidget* widget, GtkAppGlobals* globals )
{
if ( board_beginTrade( globals->cGlobals.game.board ) ) {
board_draw( globals->cGlobals.game.board );
}
} /* handle_juggle_button */
static void
scroll_value_changed( GtkAdjustment *adj, GtkAppGlobals* globals )
{
XP_U16 curYOffset, newValue;
gfloat newValueF = adj->value;
XP_ASSERT( newValueF >= 0.0 && newValueF <= 2.0 );
curYOffset = board_getYOffset( globals->cGlobals.game.board );
newValue = (XP_U16)newValueF;
if ( newValue != curYOffset ) {
2005-01-25 05:41:47 +01:00
board_setYOffset( globals->cGlobals.game.board, newValue );
2003-11-01 06:35:29 +01:00
board_draw( globals->cGlobals.game.board );
}
} /* scroll_value_changed */
static void
handle_grid_button( GtkWidget* widget, GtkAppGlobals* globals )
{
XP_U16 scaleH, scaleV;
XP_Bool gridOn = globals->gridOn;
board_getScale( globals->cGlobals.game.board, &scaleH, &scaleV );
if ( gridOn ) {
--scaleH;
--scaleV;
} else {
++scaleH;
++scaleV;
}
board_setScale( globals->cGlobals.game.board, scaleH, scaleV );
globals->gridOn = !gridOn;
board_draw( globals->cGlobals.game.board );
} /* handle_grid_button */
static void
handle_hide_button( GtkWidget* widget, GtkAppGlobals* globals )
{
BoardCtxt* board;
XP_Bool draw = XP_FALSE;
2003-11-01 06:35:29 +01:00
if ( globals->cGlobals.params->trayOverlaps ) {
globals->adjustment->page_size = MAX_ROWS;
globals->adjustment->value = 0.0;
2003-11-01 06:35:29 +01:00
gtk_signal_emit_by_name( GTK_OBJECT(globals->adjustment), "changed" );
gtk_adjustment_value_changed( GTK_ADJUSTMENT(globals->adjustment) );
2003-11-01 06:35:29 +01:00
}
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 );
2003-11-01 06:35:29 +01:00
}
} /* handle_hide_button */
static void
handle_commit_button( GtkWidget* widget, GtkAppGlobals* globals )
{
if ( board_commitTurn( globals->cGlobals.game.board ) ) {
board_draw( globals->cGlobals.game.board );
}
} /* handle_commit_button */
static void
gtkUserError( GtkAppGlobals* globals, char* format, ... )
{
char buf[512];
va_list ap;
va_start( ap, format );
vsprintf( buf, format, ap );
(void)gtkask( globals, buf, 1, "OK" );
va_end(ap);
} /* gtkUserError */
static VTableMgr*
gtk_util_getVTManager(XW_UtilCtxt* uc)
{
GtkAppGlobals* globals = (GtkAppGlobals*)uc->closure;
return globals->cGlobals.params->vtMgr;
} /* linux_util_getVTManager */
static XP_S16
2005-01-13 05:30:50 +01:00
gtk_util_userPickTile( XW_UtilCtxt* uc, const PickInfo* pi,
XP_U16 playerNum,
2005-01-13 05:30:50 +01:00
const XP_UCHAR4* texts, XP_U16 nTiles )
2003-11-01 06:35:29 +01:00
{
XP_S16 chosen;
GtkAppGlobals* globals = (GtkAppGlobals*)uc->closure;
XP_UCHAR* name = globals->cGlobals.params->gi.players[playerNum].name;
chosen = gtkletterask( pi->why == PICK_FOR_BLANK, name, nTiles, texts );
return chosen;
} /* gtk_util_userPickTile */
2003-11-01 06:35:29 +01:00
static XP_Bool
gtk_util_askPassword( XW_UtilCtxt* 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( GtkAppGlobals* globals )
{
XW_TrayVisState state =
board_getTrayVisState( globals->cGlobals.game.board );
if ( globals->cGlobals.params->trayOverlaps ) {
globals->adjustment->page_size = state==TRAY_HIDDEN? 15 : 13;
if ( state != TRAY_HIDDEN ) { /* do we need to adjust scrollbar? */
globals->adjustment->value =
board_getYOffset( globals->cGlobals.game.board );
}
gtk_signal_emit_by_name( GTK_OBJECT(globals->adjustment), "changed" );
}
} /* setCtrlsForTray */
static void
gtk_util_trayHiddenChange( XW_UtilCtxt* uc, XW_TrayVisState state )
{
GtkAppGlobals* globals = (GtkAppGlobals*)uc->closure;
setCtrlsForTray( globals );
} /* gtk_util_trayHiddenChange */
static void
gtk_util_yOffsetChange( XW_UtilCtxt* uc, XP_U16 oldOffset, XP_U16 newOffset )
{
GtkAppGlobals* globals = (GtkAppGlobals*)uc->closure;
board_invalAll( globals->cGlobals.game.board );
/* board_draw( globals->board ); */
} /* gtk_util_trayHiddenChange */
static void
printFinalScores( GtkAppGlobals* globals )
{
XWStreamCtxt* stream;
stream = mem_stream_make( MEMPOOL
globals->cGlobals.params->vtMgr,
globals, CHANNEL_NONE, catOnClose );
2003-11-01 06:35:29 +01:00
server_writeFinalScores( globals->cGlobals.game.server, stream );
stream_putU8( stream, '\n' );
stream_destroy( stream );
} /* printFinalScores */
static void
gtk_util_notifyGameOver( XW_UtilCtxt* uc )
{
GtkAppGlobals* globals = (GtkAppGlobals*)uc->closure;
if ( globals->cGlobals.params->printHistory ) {
catGameHistory( &globals->cGlobals );
}
printFinalScores( globals );
if ( globals->cGlobals.params->quitAfter ) {
quit( NULL, globals );
} else if ( globals->cGlobals.params->undoWhenDone ) {
server_handleUndo( globals->cGlobals.game.server );
}
board_draw( globals->cGlobals.game.board );
} /* gtk_util_notifyGameOver */
/* define this to prevent user events during debugging from stopping the engine */
/* #define DONT_ABORT_ENGINE */
static XP_Bool
gtk_util_hiliteCell( XW_UtilCtxt* uc, XP_U16 col, XP_U16 row )
{
GtkAppGlobals* globals = (GtkAppGlobals*)uc->closure;
#ifndef DONT_ABORT_ENGINE
2003-11-01 06:35:29 +01:00
gboolean pending;
#endif
2003-11-01 06:35:29 +01:00
board_hiliteCellAt( globals->cGlobals.game.board, col, row );
if ( globals->cGlobals.params->sleepOnAnchor ) {
}
#ifdef DONT_ABORT_ENGINE
return XP_TRUE; /* keep going */
#else
pending = gdk_events_pending();
if ( pending ) {
XP_DEBUGF( "gtk_util_hiliteCell=>%d", pending );
}
2003-11-01 06:35:29 +01:00
return !pending;
#endif
} /* gtk_util_hiliteCell */
static XP_Bool
gtk_util_engineProgressCallback( XW_UtilCtxt* 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( GtkAppGlobals* globals, XWTimerReason why )
{
guint src = globals->timerSources[why-1];
if ( src != 0 ) {
g_source_remove( src );
globals->timerSources[why-1] = 0;
}
} /* cancelTimer */
2003-11-01 06:35:29 +01:00
static gint
pentimer_idle_func( gpointer data )
{
GtkAppGlobals* globals = (GtkAppGlobals*)data;
struct timeval tv;
XP_Bool callAgain = XP_TRUE;
gettimeofday( &tv, NULL );
2003-11-01 06:35:29 +01:00
if ( (tv.tv_usec - globals->penTv.tv_usec) >= globals->penTimerInterval) {
2006-02-18 07:35:48 +01:00
linuxFireTimer( &globals->cGlobals, TIMER_PENDOWN );
2003-11-01 06:35:29 +01:00
callAgain = XP_FALSE;
}
return callAgain;
} /* timer_idle_func */
static gint
score_timer_func( gpointer data )
{
GtkAppGlobals* globals = (GtkAppGlobals*)data;
2006-02-18 07:35:48 +01:00
linuxFireTimer( &globals->cGlobals, TIMER_TIMERTICK );
2003-11-01 06:35:29 +01:00
return XP_FALSE;
2003-11-01 06:35:29 +01:00
} /* score_timer_func */
static gint
heartbeat_timer_func( gpointer data )
{
GtkAppGlobals* globals = (GtkAppGlobals*)data;
if ( !globals->cGlobals.params->noHeartbeat ) {
2006-02-18 07:35:48 +01:00
linuxFireTimer( &globals->cGlobals, TIMER_HEARTBEAT );
}
return (gint)0;
}
2003-11-01 06:35:29 +01:00
static void
gtk_util_setTimer( XW_UtilCtxt* uc, XWTimerReason why, XP_U16 when,
TimerProc proc, void* closure )
2003-11-01 06:35:29 +01:00
{
GtkAppGlobals* globals = (GtkAppGlobals*)uc->closure;
guint newSrc;
2003-11-01 06:35:29 +01:00
cancelTimer( globals, why );
2003-11-01 06:35:29 +01:00
if ( why == TIMER_PENDOWN ) {
globals->penTimerInterval = 35 * 10000;
(void)gettimeofday( &globals->penTv, NULL );
newSrc = g_idle_add( pentimer_idle_func, globals );
2003-11-01 06:35:29 +01:00
} else if ( why == TIMER_TIMERTICK ) {
globals->scoreTimerInterval = 100 * 10000;
(void)gettimeofday( &globals->scoreTv, NULL );
2003-11-01 06:35:29 +01:00
newSrc = g_timeout_add( 1000, score_timer_func, globals );
} else if ( why == TIMER_HEARTBEAT ) {
newSrc = g_timeout_add( 1000 * when, heartbeat_timer_func, globals );
} else {
XP_ASSERT( 0 );
2003-11-01 06:35:29 +01:00
}
globals->cGlobals.timerProcs[why] = proc;
globals->cGlobals.timerClosures[why] = closure;
XP_ASSERT( newSrc != 0 );
globals->timerSources[why-1] = newSrc;
2003-11-01 06:35:29 +01:00
} /* gtk_util_setTimer */
static gint
idle_func( gpointer data )
{
GtkAppGlobals* globals = (GtkAppGlobals*)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 );
if ( server_do( globals->cGlobals.game.server ) ) {
XP_ASSERT( globals->cGlobals.game.board != NULL );
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 )
{
GtkAppGlobals* globals = (GtkAppGlobals*)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;
GtkAppGlobals* globals = (GtkAppGlobals*)uc->closure;
char buf[300];
if ( turnLost ) {
char wordsBuf[256];
XP_U16 i;
XP_UCHAR* name = globals->cGlobals.params->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;
2003-11-01 06:35:29 +01:00
} else {
XP_ASSERT( bwi->nWords == 1 );
sprintf( buf, "Word \"%s\" not in the current dictionary. "
"Use it anyway?", bwi->words[0] );
result = 0 == gtkask( globals, buf, 2, "Ok", "Cancel" );
2003-11-01 06:35:29 +01:00
}
return result;
} /* gtk_util_warnIllegalWord */
static XWStreamCtxt*
gtk_util_makeStreamFromAddr(XW_UtilCtxt* uc, XP_PlayerAddr channelNo )
{
GtkAppGlobals* globals = (GtkAppGlobals*)uc->closure;
XWStreamCtxt* stream = mem_stream_make( MEMPOOL
globals->cGlobals.params->vtMgr,
uc->closure, channelNo,
sendOnClose );
return stream;
} /* gtk_util_makeStreamFromAddr */
#ifdef XWFEATURE_SEARCHLIMIT
static XP_Bool
gtk_util_getTraySearchLimits( XW_UtilCtxt* uc, XP_U16* min, XP_U16* max )
{
GtkAppGlobals* globals = (GtkAppGlobals*)uc->closure;
*max = askNTiles( globals, MAX_TRAY_TILES, *max );
return XP_TRUE;
}
#endif
2003-11-01 06:35:29 +01:00
static void
gtk_util_userError( XW_UtilCtxt* uc, UtilErrID id )
{
GtkAppGlobals* globals = (GtkAppGlobals*)uc->closure;
XP_Bool silent;
XP_UCHAR* message = linux_getErrString( id, &silent );
2003-11-01 06:35:29 +01:00
if ( silent ) {
XP_LOGF( message );
} else {
gtkUserError( globals, message );
}
2003-11-01 06:35:29 +01:00
} /* gtk_util_userError */
2004-02-17 05:00:12 +01:00
static XP_Bool
2003-11-01 06:35:29 +01:00
gtk_util_userQuery( XW_UtilCtxt* uc, UtilQueryID id, XWStreamCtxt* stream )
{
2004-02-17 05:00:12 +01:00
XP_Bool result;
2003-11-01 06:35:29 +01:00
GtkAppGlobals* globals = (GtkAppGlobals*)uc->closure;
char* question;
char* answers[3];
gint numAnswers = 0;
2004-02-17 05:00:12 +01:00
XP_U16 okIndex = 1;
2003-11-01 06:35:29 +01:00
XP_Bool freeMe = XP_FALSE;
switch( id ) {
case QUERY_COMMIT_TURN:
question = strFromStream( stream );
freeMe = XP_TRUE;
/* len = stream_getSize( stream ); */
/* question = malloc( len + 1 ); */
/* stream_getBytes( stream, question, len ); */
/* question[len] = '\0'; */
answers[numAnswers++] = "Cancel";
answers[numAnswers++] = "Ok";
break;
case QUERY_COMMIT_TRADE:
question = "Are you sure you want to trade the selected tiles?";
answers[numAnswers++] = "Cancel";
answers[numAnswers++] = "Ok";
break;
case QUERY_ROBOT_MOVE:
case QUERY_ROBOT_TRADE:
question = strFromStream( stream );
freeMe = XP_TRUE;
answers[numAnswers++] = "Ok";
2004-02-17 05:00:12 +01:00
okIndex = 0;
2003-11-01 06:35:29 +01:00
break;
default:
XP_ASSERT( 0 );
2004-02-17 05:00:12 +01:00
return XP_FALSE;
2003-11-01 06:35:29 +01:00
}
result = gtkask( globals, question, numAnswers,
2004-02-17 05:00:12 +01:00
answers[0], answers[1], answers[2] ) == okIndex;
2003-11-01 06:35:29 +01:00
if ( freeMe > 0 ) {
free( question );
}
return result;
} /* gtk_util_userQuery */
static XP_Bool
file_exists( const char* fileName )
2003-11-01 06:35:29 +01:00
{
struct stat statBuf;
int statResult = stat( fileName, &statBuf );
return statResult == 0;
} /* file_exists */
static GtkWidget*
makeShowButtonFromBitmap( void* closure, const gchar* filename,
const gchar* alt, GCallback func )
2003-11-01 06:35:29 +01:00
{
GtkWidget* widget;
2003-11-01 06:35:29 +01:00
GtkWidget* button;
if ( file_exists( filename ) ) {
widget = gtk_image_new_from_file (filename);
2003-11-01 06:35:29 +01:00
} else {
widget = gtk_label_new( alt );
2003-11-01 06:35:29 +01:00
}
gtk_widget_show( widget );
button = gtk_button_new();
gtk_container_add (GTK_CONTAINER (button), widget );
gtk_widget_show (button);
2003-11-01 06:35:29 +01:00
if ( func != NULL ) {
g_signal_connect( GTK_OBJECT(button), "clicked", func, closure );
2003-11-01 06:35:29 +01:00
}
return button;
} /* makeShowButtonFromBitmap */
static GtkWidget*
makeVerticalBar( GtkAppGlobals* globals, GtkWidget* window )
{
GtkWidget* vbox;
GtkWidget* button;
GtkWidget* vscrollbar;
vbox = gtk_vbutton_box_new();
2003-11-01 06:35:29 +01:00
button = makeShowButtonFromBitmap( globals, "../flip.xpm", "f",
G_CALLBACK(handle_flip_button) );
2003-11-01 06:35:29 +01:00
gtk_box_pack_start( GTK_BOX(vbox), button, FALSE, TRUE, 0 );
button = makeShowButtonFromBitmap( globals, "../value.xpm", "v",
G_CALLBACK(handle_value_button) );
2003-11-01 06:35:29 +01:00
gtk_box_pack_start( GTK_BOX(vbox), button, FALSE, TRUE, 0 );
button = makeShowButtonFromBitmap( globals, "../hint.xpm", "?",
G_CALLBACK(handle_hint_button) );
2003-11-01 06:35:29 +01:00
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) );
2003-11-01 06:35:29 +01:00
gtk_box_pack_start( GTK_BOX(vbox), button, FALSE, TRUE, 0 );
if ( globals->cGlobals.params->trayOverlaps ) {
globals->adjustment = (GtkAdjustment*)gtk_adjustment_new( 0, 0, 15,
1, 2, 13 );
vscrollbar = gtk_vscrollbar_new( globals->adjustment );
g_signal_connect( GTK_OBJECT(globals->adjustment), "value_changed",
G_CALLBACK(scroll_value_changed), globals );
2003-11-01 06:35:29 +01:00
gtk_widget_show( vscrollbar );
gtk_box_pack_start( GTK_BOX(vbox), vscrollbar, TRUE, TRUE, 0 );
}
/* undo and redo buttons */
button = makeShowButtonFromBitmap( globals, "../undo.xpm", "u",
G_CALLBACK(handle_undo_button) );
2003-11-01 06:35:29 +01:00
gtk_box_pack_start( GTK_BOX(vbox), button, FALSE, TRUE, 0 );
button = makeShowButtonFromBitmap( globals, "../redo.xpm", "r",
G_CALLBACK(handle_redo_button) );
2003-11-01 06:35:29 +01:00
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) );
2003-11-01 06:35:29 +01:00
gtk_box_pack_start( GTK_BOX(vbox), button, FALSE, TRUE, 0 );
button = makeShowButtonFromBitmap( globals, "../trade.xpm", "t",
G_CALLBACK(handle_trade_button) );
2003-11-01 06:35:29 +01:00
gtk_box_pack_start( GTK_BOX(vbox), button, FALSE, TRUE, 0 );
button = makeShowButtonFromBitmap( globals, "../hide.xpm", "h",
G_CALLBACK(handle_hide_button) );
2003-11-01 06:35:29 +01:00
gtk_box_pack_start( GTK_BOX(vbox), button, FALSE, TRUE, 0 );
button = makeShowButtonFromBitmap( globals, "../hide.xpm", "d",
G_CALLBACK(handle_commit_button) );
2003-11-01 06:35:29 +01:00
gtk_box_pack_start( GTK_BOX(vbox), button, FALSE, TRUE, 0 );
gtk_widget_show( vbox );
return vbox;
} /* makeVerticalBar */
static GtkWidget*
makeButtons( GtkAppGlobals* globals, int argc, char** argv )
{
short i;
GtkWidget* hbox;
GtkWidget* button;
struct {
char* name;
GCallback func;
2003-11-01 06:35:29 +01:00
} buttons[] = {
/* { "Flip", handle_flip_button }, */
{ "Grid", G_CALLBACK(handle_grid_button) },
{ "Hide", G_CALLBACK(handle_hide_button) },
{ "Commit", G_CALLBACK(handle_commit_button) },
2003-11-01 06:35:29 +01:00
};
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 );
2003-11-01 06:35:29 +01:00
gtk_box_pack_start( GTK_BOX(hbox), button, FALSE, TRUE, 0);
}
gtk_widget_show( hbox );
return hbox;
} /* makeButtons */
static void
setupGtkUtilCallbacks( GtkAppGlobals* globals, XW_UtilCtxt* util )
{
util->vtable->m_util_userError = gtk_util_userError;
util->vtable->m_util_userQuery = gtk_util_userQuery;
util->vtable->m_util_getVTManager = gtk_util_getVTManager;
util->vtable->m_util_userPickTile = gtk_util_userPickTile;
2003-11-01 06:35:29 +01:00
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_notifyGameOver = gtk_util_notifyGameOver;
util->vtable->m_util_hiliteCell = gtk_util_hiliteCell;
util->vtable->m_util_engineProgressCallback =
gtk_util_engineProgressCallback;
util->vtable->m_util_setTimer = gtk_util_setTimer;
util->vtable->m_util_requestTime = gtk_util_requestTime;
util->vtable->m_util_warnIllegalWord = gtk_util_warnIllegalWord;
util->vtable->m_util_makeStreamFromAddr = gtk_util_makeStreamFromAddr;
#ifdef XWFEATURE_SEARCHLIMIT
util->vtable->m_util_getTraySearchLimits = gtk_util_getTraySearchLimits;
#endif
2003-11-01 06:35:29 +01:00
util->closure = globals;
} /* setupGtkUtilCallbacks */
static gboolean
newConnectionInput( GIOChannel *source,
GIOCondition condition,
gpointer data )
2003-11-01 06:35:29 +01:00
{
int sock = g_io_channel_unix_get_fd( source );
2003-11-01 06:35:29 +01:00
GtkAppGlobals* globals = (GtkAppGlobals*)data;
XP_ASSERT( sock == globals->cGlobals.socket );
if ( (condition & G_IO_HUP) != 0 ) {
globals->cGlobals.socket = -1;
return FALSE; /* remove the event source */
} else if ( (condition & G_IO_IN) != 0 ) {
ssize_t nRead;
unsigned char buf[512];
nRead = linux_receive( &globals->cGlobals, buf, sizeof(buf) );
if ( !globals->dropIncommingMsgs && nRead > 0 ) {
XWStreamCtxt* inboundS;
XP_Bool redraw = XP_FALSE;
inboundS = stream_from_msgbuf( &globals->cGlobals, buf, nRead );
if ( !!inboundS ) {
2005-03-09 16:12:57 +01:00
if ( comms_checkIncomingStream( globals->cGlobals.game.comms,
inboundS, NULL ) ) {
redraw = server_receiveMessage(globals->cGlobals.game.server
, inboundS );
}
stream_destroy( inboundS );
2003-11-01 06:35:29 +01:00
}
/* 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.params->util );
} else {
redraw = server_do( globals->cGlobals.game.server );
}
if ( redraw ) {
board_draw( globals->cGlobals.game.board );
}
2003-11-01 06:35:29 +01:00
} else {
XP_LOGF( "errno from read: %d", errno );
2003-11-01 06:35:29 +01:00
}
}
return TRUE; /* FALSE means to remove event source */
2003-11-01 06:35:29 +01:00
} /* newConnectionInput */
/* Make gtk listen for events on the socket that clients will use to
* connect to us.
*/
static void
gtkListenOnSocket( GtkAppGlobals* globals, int newSock )
{
GIOChannel* channel = g_io_channel_unix_new( newSock );
guint result = g_io_add_watch( channel,
G_IO_IN | G_IO_HUP,
newConnectionInput,
globals );
XP_LOGF( "g_io_add_watch => %d", result );
2003-11-01 06:35:29 +01:00
} /* gtkListenOnSocket */
static void
gtk_socket_changed( void* closure, int oldSock, int newSock )
{
GtkAppGlobals* globals = (GtkAppGlobals*)closure;
if ( oldSock != -1 ) {
g_source_remove( oldSock );
XP_LOGF( "Removed %d from gtk's list of listened-to sockets" );
}
if ( newSock != -1 ) {
gtkListenOnSocket( globals, newSock );
}
}
2003-11-01 06:35:29 +01:00
static void
sendOnClose( XWStreamCtxt* stream, void* closure )
{
XP_S16 result;
GtkAppGlobals* globals = closure;
XP_LOGF( "sendOnClose called" );
result = comms_send( globals->cGlobals.game.comms, stream );
2003-11-01 06:35:29 +01:00
} /* sendOnClose */
static void
drop_msg_toggle( GtkWidget* toggle, GtkAppGlobals* globals )
{
globals->dropIncommingMsgs = gtk_toggle_button_get_active(
GTK_TOGGLE_BUTTON(toggle) );
} /* drop_msg_toggle */
int
gtkmain( XP_Bool isServer, LaunchParams* params, int argc, char *argv[] )
{
short width, height;
GtkWidget* window;
GtkWidget* drawing_area;
GtkWidget* menubar;
GtkWidget* buttonbar;
GtkWidget* vbox;
GtkWidget* hbox;
GtkAppGlobals globals;
GtkWidget* dropCheck;
memset( &globals, 0, sizeof(globals) );
globals.cGlobals.params = params;
globals.cGlobals.lastNTilesToUse = MAX_TRAY_TILES;
globals.cGlobals.socket = -1;
2003-11-01 06:35:29 +01:00
globals.cGlobals.socketChanged = gtk_socket_changed;
globals.cGlobals.socketChangedClosure = &globals;
2003-11-01 06:35:29 +01:00
globals.cp.showBoardArrow = XP_TRUE;
globals.cp.showRobotScores = params->showRobotScores;
setupGtkUtilCallbacks( &globals, params->util );
/* globals.dictionary = params->dict; */
/* globals.trayOverlaps = params->trayOverlaps; */
/* globals.askNewGame = params->askNewGame; */
/* globals.quitWhenDone = params->quitAfter; */
/* globals.sleepOnAnchor = params->sleepOnAnchor; */
/* globals.util = params->util; */
/* globals.fileName = params->fileName; */
/* globals.listenPort = params->listenPort; */
/* Now set up the gtk stuff. This is necessary before we make the
draw ctxt */
gtk_init( &argc, &argv );
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( quit ), &globals );
2003-11-01 06:35:29 +01:00
menubar = makeMenus( &globals, argc, argv );
gtk_box_pack_start( GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
dropCheck = gtk_check_button_new_with_label( "drop incoming messages" );
g_signal_connect( GTK_OBJECT(dropCheck),
"toggled", G_CALLBACK(drop_msg_toggle), &globals );
2003-11-01 06:35:29 +01:00
gtk_box_pack_start( GTK_BOX(vbox), dropCheck, FALSE, TRUE, 0);
gtk_widget_show( dropCheck );
buttonbar = makeButtons( &globals, argc, argv );
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 );
2003-11-01 06:35:29 +01:00
#if 0
width = (MAX_COLS * MIN_SCALE) + LEFT_MARGIN + RIGHT_MARGIN;
height = (MAX_ROWS * MIN_SCALE) + TOP_MARGIN + BOTTOM_MARGIN
+ MIN_TRAY_SCALE + BOTTOM_MARGIN;
#else
width = HOR_SCORE_WIDTH + TIMER_WIDTH + TIMER_PAD;
if ( globals.cGlobals.params->verticalScore ) {
width += VERT_SCORE_WIDTH;
}
2003-11-01 06:35:29 +01:00
height = 196;
if ( !globals.cGlobals.params->trayOverlaps ) {
height += MIN_SCALE * 2;
}
#endif
gtk_widget_set_size_request( GTK_WIDGET(drawing_area), width, height );
2003-11-01 06:35:29 +01:00
hbox = gtk_hbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX (hbox), drawing_area, TRUE, TRUE, 0);
gtk_box_pack_start( GTK_BOX (hbox),
makeVerticalBar( &globals, window ),
FALSE, TRUE, 0 );
2003-11-01 06:35:29 +01:00
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 );
g_signal_connect( GTK_OBJECT(window), "key_release_event",
G_CALLBACK(key_release_event), &globals );
2003-11-01 06:35:29 +01:00
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
| GDK_KEY_PRESS_MASK
| GDK_KEY_RELEASE_MASK
/* | GDK_POINTER_MOTION_HINT_MASK */
);
gtk_widget_show( window );
gtk_main();
/* MONCONTROL(1); */
return 0;
} /* gtkmain */
#endif /* PLATFORM_GTK */