/*
* backend.c -- Common back end for X and Windows NT versions of
- * XBoard $Id$
+ * XBoard $Id: backend.c,v 2.6 2003/11/28 09:37:36 mann Exp $
*
* Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
* Enhancements Copyright 1992-2001 Free Software Foundation, Inc.
*
* See the file ChangeLog for a revision history. */
+/* [AS] For debugging purposes */
+#ifdef WIN32
+#include <windows.h>
+
+#define DoSleep( n ) if( (n) != 0 ) Sleep( (n) );
+
+#else
+
+#define DoSleep( n )
+
+#endif
+
#include "config.h"
+#include <assert.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
int seen_stat; /* 1 if we've seen the stat01: line */
} ChessProgramStats;
+/* [AS] Search stats from chessprogram, for the played move */
+typedef struct {
+ int score;
+ int depth;
+} ChessProgramStats_Move;
+
int establish P((void));
void read_from_player P((InputSourceRef isr, VOIDSTAR closure,
char *buf, int count, int error));
#define TN_SGA 0003
#define TN_PORT 23
+/* [AS] */
+static char * safeStrCpy( char * dst, const char * src, size_t count )
+{
+ assert( dst != NULL );
+ assert( src != NULL );
+ assert( count > 0 );
+
+ strncpy( dst, src, count );
+ dst[ count-1 ] = '\0';
+ return dst;
+}
+
+static char * safeStrCat( char * dst, const char * src, size_t count )
+{
+ size_t dst_len;
+
+ assert( dst != NULL );
+ assert( src != NULL );
+ assert( count > 0 );
+
+ dst_len = strlen(dst);
+
+ assert( count > dst_len ); /* Buffer size must be greater than current length */
+
+ safeStrCpy( dst + dst_len, src, count - dst_len );
+
+ return dst;
+}
+
/* Fake up flags for now, as we aren't keeping track of castling
availability yet */
int
FILE *gameFileFP, *debugFP;
+/*
+ [AS] Note: sometimes, the sscanf() function is used to parse the input
+ into a fixed-size buffer. Because of this, we must be prepared to
+ receive strings as long as the size of the input buffer, which is currently
+ set to 4K for Windows and 8K for the rest.
+ So, we must either allocate sufficiently large buffers here, or
+ reduce the size of the input buffer in the input reading part.
+*/
+
char cmailMove[CMAIL_MAX_GAMES][MOVE_LEN], cmailMsg[MSG_SIZ];
char bookOutput[MSG_SIZ*10], thinkOutput[MSG_SIZ*10], lastHint[MSG_SIZ];
char thinkOutput1[MSG_SIZ*10];
GameMode gameMode = BeginningOfGame;
char moveList[MAX_MOVES][MOVE_LEN], parseList[MAX_MOVES][MOVE_LEN * 2];
char *commentList[MAX_MOVES], *cmailCommentList[CMAIL_MAX_GAMES];
+ChessProgramStats_Move pvInfoList[MAX_MOVES]; /* [AS] Info about engine thinking */
+int hiddenThinkOutputState = 0; /* [AS] */
+int adjudicateLossThreshold = 0; /* [AS] Automatic adjudication */
+int adjudicateLossPlies = 6;
char white_holding[64], black_holding[64];
TimeMark lastNodeCountTime;
long lastNodeCount=0;
int have_sent_ICS_logon = 0;
int movesPerSession;
long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement;
+long timeControl_2; /* [AS] Allow separate time controls */
long timeRemaining[2][MAX_MOVES];
int matchGame = 0;
TimeMark programStartTime;
appData.zippyTalk = appData.zippyPlay = FALSE;
}
+ /* [AS] Initialize pv info list */
+ {
+ int i;
+
+ for( i=0; i<MAX_MOVES; i++ ) {
+ pvInfoList[i].depth = 0;
+ }
+ }
+
/*
* Parse timeControl resource
*/
}
}
+ /* [AS] Adjudication threshold */
+ adjudicateLossThreshold = appData.adjudicateLossThreshold;
+
first.which = "first";
second.which = "second";
first.maybeThinking = second.maybeThinking = FALSE;
first.analyzing = second.analyzing = FALSE;
first.initDone = second.initDone = FALSE;
+ first.scoreIsAbsolute = appData.firstScoreIsAbsolute; /* [AS] */
+ second.scoreIsAbsolute = appData.secondScoreIsAbsolute; /* [AS] */
+
if (appData.firstProtocolVersion > PROTOVER ||
appData.firstProtocolVersion < 1) {
char buf[MSG_SIZ];
}
}
+int NextIntegerFromString( char ** str, long * value )
+{
+ int result = -1;
+ char * s = *str;
+
+ while( *s == ' ' || *s == '\t' ) {
+ s++;
+ }
+
+ *value = 0;
+
+ if( *s >= '0' && *s <= '9' ) {
+ while( *s >= '0' && *s <= '9' ) {
+ *value = *value * 10 + (*s - '0');
+ s++;
+ }
+
+ result = 0;
+ }
+
+ *str = s;
+
+ return result;
+}
+
+int NextTimeControlFromString( char ** str, long * value )
+{
+ long temp;
+ int result = NextIntegerFromString( str, &temp );
+
+ if( result == 0 ) {
+ *value = temp * 60; /* Minutes */
+ if( **str == ':' ) {
+ (*str)++;
+ result = NextIntegerFromString( str, &temp );
+ *value += temp; /* Seconds */
+ }
+ }
+
+ return result;
+}
+
+int GetTimeControlForWhite()
+{
+ int result = timeControl;
+
+ return result;
+}
+
+int GetTimeControlForBlack()
+{
+ int result = timeControl;
+
+ if( timeControl_2 > 0 ) {
+ result = timeControl_2;
+ }
+
+ return result;
+}
+
int
ParseTimeControl(tc, ti, mps)
char *tc;
int ti;
int mps;
{
+#if 0
int matched, min, sec;
matched = sscanf(tc, "%d:%d", &min, &sec);
} else {
return FALSE;
}
+#else
+ long tc1;
+ long tc2;
+
+ if( NextTimeControlFromString( &tc, &tc1 ) != 0 ) {
+ return FALSE;
+ }
+
+ if( *tc == '/' ) {
+ /* Parse second time control */
+ tc++;
+
+ if( NextTimeControlFromString( &tc, &tc2 ) != 0 ) {
+ return FALSE;
+ }
+
+ if( tc2 == 0 ) {
+ return FALSE;
+ }
+
+ timeControl_2 = tc2 * 1000;
+ }
+ else {
+ timeControl_2 = 0;
+ }
+
+ if( tc1 == 0 ) {
+ return FALSE;
+ }
+
+ timeControl = tc1 * 1000;
+#endif
if (ti >= 0) {
timeIncrement = ti * 1000; /* convert to ms */
gameInfo.white = StrSave(white);
gameInfo.black = StrSave(black);
timeControl = basetime * 60 * 1000;
+ timeControl_2 = 0;
timeIncrement = increment * 1000;
movesPerSession = 0;
gameInfo.timeControl = TimeControlTagValue();
int redraw;
{
currentMove = forwardMostMove = backwardMostMove = 0;
+
+ /* [AS] Initialize pv info list */
+ {
+ int i;
+
+ for( i=0; i<MAX_MOVES; i++ ) {
+ pvInfoList[i].depth = 0;
+ }
+ }
+
switch (gameInfo.variant) {
default:
CopyBoard(boards[0], initialPosition);
strcat(machineMove, "\n");
strcpy(moveList[forwardMostMove], machineMove);
+ /* [AS] Save move info and clear stats for next move */
+ pvInfoList[ forwardMostMove ].score = programStats.score;
+ pvInfoList[ forwardMostMove ].depth = programStats.depth;
+ ClearProgramStats();
+ thinkOutput[0] = NULLCHAR;
+ hiddenThinkOutputState = 0;
+
MakeMove(fromX, fromY, toX, toY, promoChar);/*updates forwardMostMove*/
if (gameMode == TwoMachinesPlay) {
}
ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
+
if (!pausing && appData.ringBellAfterMoves) {
RingBell();
}
+
/*
* Reenable menu items that were disabled while
* machine was thinking
*/
if (gameMode != TwoMachinesPlay)
SetUserThinkingEnables();
+
+
+ /* [AS] Adjudicate game if needed (note: forwardMostMove now points past the last move */
+ if( gameMode == TwoMachinesPlay && adjudicateLossThreshold != 0 && forwardMostMove >= adjudicateLossPlies ) {
+ int count = 0;
+
+ while( count < adjudicateLossPlies ) {
+ int score = pvInfoList[ forwardMostMove - count - 1 ].score;
+
+ if( count & 1 ) {
+ score = -score; /* Flip score for winning side */
+ }
+
+ if( score > adjudicateLossThreshold ) {
+ break;
+ }
+
+ count++;
+ }
+
+ if( count >= adjudicateLossPlies ) {
+ GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins,
+ "Xboard adjudication",
+ GE_XBOARD );
+ }
+ }
+
return;
}
/*
* Look for thinking output
*/
- if (appData.showThinking) {
+ if ( appData.showThinking) {
int plylev, mvleft, mvtot, curscore, time;
char mvname[MOVE_LEN];
unsigned long nodes;
case AnalyzeFile:
break;
case TwoMachinesPlay:
- if ((cps->twoMachinesColor[0] == 'w') !=
- WhiteOnMove(forwardMostMove)) {
+ if ((cps->twoMachinesColor[0] == 'w') != WhiteOnMove(forwardMostMove)) {
ignore = TRUE;
}
break;
programStats.score = curscore;
programStats.got_only_move = 0;
+ /* [AS] Negate score if machine is playing black and it's reporting absolute scores */
+ if( cps->scoreIsAbsolute &&
+ ((gameMode == MachinePlaysBlack) || (gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b')) )
+ {
+ programStats.score = -curscore;
+ }
+
/* Buffer overflow protection */
if (buf1[0] != NULLCHAR) {
if (strlen(buf1) >= sizeof(programStats.movelist)
"PV is too long; using the first %d bytes.\n",
sizeof(programStats.movelist) - 1);
}
- strncpy(programStats.movelist, buf1,
- sizeof(programStats.movelist));
- programStats.movelist[sizeof(programStats.movelist) - 1]
- = NULLCHAR;
+
+ safeStrCpy( programStats.movelist, buf1, sizeof(programStats.movelist) );
} else {
sprintf(programStats.movelist, " no PV\n");
}
programStats.line_is_book = 0;
}
- sprintf(thinkOutput, "[%d]%c%+.2f %s%s%s",
+ /*
+ [AS] Protect the thinkOutput buffer from overflow... this
+ is only useful if buf1 hasn't overflowed first!
+ */
+ sprintf(thinkOutput, "[%d]%c%+.2f %s%s",
plylev,
(gameMode == TwoMachinesPlay ?
ToUpper(cps->twoMachinesColor[0]) : ' '),
((double) curscore) / 100.0,
prefixHint ? lastHint : "",
- prefixHint ? " " : "", buf1);
+ prefixHint ? " " : "" );
+
+ if( buf1[0] != NULLCHAR ) {
+ unsigned max_len = sizeof(thinkOutput) - strlen(thinkOutput) - 1;
- if (currentMove == forwardMostMove ||
- gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
+ if( strlen(buf1) > max_len ) {
+ if( appData.debugMode) {
+ fprintf(debugFP,"PV is too long for thinkOutput, truncating.\n");
+ }
+ buf1[max_len+1] = '\0';
+ }
+
+ strcat( thinkOutput, buf1 );
+ }
+
+ if (currentMove == forwardMostMove || gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
DisplayMove(currentMove - 1);
DisplayAnalysis();
}
isn't searching, so stats won't change) */
programStats.line_is_book = 1;
- if (currentMove == forwardMostMove || gameMode==AnalyzeMode ||
- gameMode == AnalyzeFile) {
+ if (currentMove == forwardMostMove || gameMode==AnalyzeMode || gameMode == AnalyzeFile) {
DisplayMove(currentMove - 1);
DisplayAnalysis();
}
} else if (thinkOutput[0] != NULLCHAR &&
strncmp(message, " ", 4) == 0) {
+ unsigned message_len;
+
p = message;
while (*p && *p == ' ') p++;
+
+ message_len = strlen( p );
+
+ /* [AS] Avoid buffer overflow */
+ if( sizeof(thinkOutput) - strlen(thinkOutput) - 1 > message_len ) {
strcat(thinkOutput, " ");
strcat(thinkOutput, p);
+ }
+
+ if( sizeof(programStats.movelist) - strlen(programStats.movelist) - 1 > message_len ) {
strcat(programStats.movelist, " ");
strcat(programStats.movelist, p);
- if (currentMove == forwardMostMove || gameMode==AnalyzeMode ||
- gameMode == AnalyzeFile) {
+ }
+
+ if (currentMove == forwardMostMove || gameMode==AnalyzeMode || gameMode == AnalyzeFile) {
DisplayMove(currentMove - 1);
DisplayAnalysis();
}
TwoMachinesEventIfReady();
}
+void UserAdjudicationEvent( int result )
+{
+ ChessMove gameResult = GameIsDrawn;
+
+ if( result > 0 ) {
+ gameResult = WhiteWins;
+ }
+ else if( result < 0 ) {
+ gameResult = BlackWins;
+ }
+
+ if( gameMode == TwoMachinesPlay ) {
+ GameEnds( gameResult, "User adjudication", GE_XBOARD );
+ }
+}
+
+
void
GameEnds(result, resultDetails, whosays)
ChessMove result;
if (first.pr != NoProc) {
ExitAnalyzeMode();
+ DoSleep( appData.delayBeforeQuit );
SendToProgram("quit\n", &first);
+ DoSleep( appData.delayAfterQuit );
DestroyChildProcess(first.pr, first.useSigterm);
}
first.pr = NoProc;
second.isr = NULL;
if (second.pr != NoProc) {
+ DoSleep( appData.delayBeforeQuit );
SendToProgram("quit\n", &second);
+ DoSleep( appData.delayAfterQuit );
DestroyChildProcess(second.pr, second.useSigterm);
}
second.pr = NoProc;
char *movetext;
char numtext[32];
int movelen, numlen, blank;
+ char move_buffer[100]; /* [AS] Buffer for move+PV info */
tm = time((time_t *) NULL);
/* Get move */
movetext = SavePart(parseList[i]);
+
+ /* [AS] Add PV info if present */
+ if( i > 0 && appData.saveExtendedInfoInPGN && pvInfoList[i].depth > 0 ) {
+ sprintf( move_buffer, "%s {%s%.2f/%d}",
+ movetext,
+ pvInfoList[i].score >= 0 ? "+" : "",
+ pvInfoList[i].score / 100.0,
+ pvInfoList[i].depth );
+ movetext = move_buffer;
+ }
+
movelen = strlen(movetext);
/* Print move */
/* Kill off chess programs */
if (first.pr != NoProc) {
ExitAnalyzeMode();
+
+ DoSleep( appData.delayBeforeQuit );
SendToProgram("quit\n", &first);
+ DoSleep( appData.delayAfterQuit );
DestroyChildProcess(first.pr, first.useSigterm);
}
if (second.pr != NoProc) {
+ DoSleep( appData.delayBeforeQuit );
SendToProgram("quit\n", &second);
+ DoSleep( appData.delayAfterQuit );
DestroyChildProcess(second.pr, second.useSigterm);
}
if (first.isr != NULL) {
"Error reading from %s chess program (%s)",
cps->which, cps->program);
RemoveInputSource(cps->isr);
+
+ /* [AS] Program is misbehaving badly... kill it */
+ if( count == -2 ) {
+ DestroyChildProcess( cps->pr, 9 );
+ cps->pr = NoProc;
+ }
+
DisplayFatalError(buf, error, 1);
}
GameEnds((ChessMove) 0, NULL, GE_PLAYER);
char buf[MSG_SIZ];
int seconds = (tc / 1000) % 60;
+ if( timeControl_2 > 0 ) {
+ if( (gameMode == MachinePlaysBlack) || (gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b') ) {
+ tc = timeControl_2;
+ }
+ }
+
if (st > 0) {
/* Set exact time per move, normally using st command */
if (cps->stKludge) {
if (moveNumber == forwardMostMove - 1 ||
gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
- strcpy(cpThinkOutput, thinkOutput);
- if (strchr(cpThinkOutput, '\n'))
+ safeStrCpy(cpThinkOutput, thinkOutput, sizeof(cpThinkOutput));
+
+ if (strchr(cpThinkOutput, '\n')) {
*strchr(cpThinkOutput, '\n') = NULLCHAR;
+ }
} else {
*cpThinkOutput = NULLCHAR;
}
+ /* [AS] Hide thinking from human user */
+ if( appData.hideThinkingFromHuman && gameMode != TwoMachinesPlay ) {
+ *cpThinkOutput = NULLCHAR;
+ if( thinkOutput[0] != NULLCHAR ) {
+ int i;
+
+ for( i=0; i<=hiddenThinkOutputState; i++ ) {
+ cpThinkOutput[i] = '.';
+ }
+ cpThinkOutput[i] = NULLCHAR;
+ hiddenThinkOutputState = (hiddenThinkOutputState + 1) % 3;
+ }
+ }
+
if (moveNumber == forwardMostMove - 1 &&
gameInfo.resultDetails != NULL) {
if (gameInfo.resultDetails[0] == NULLCHAR) {
DisplayAnalysis()
{
char buf[MSG_SIZ];
+ char lst[MSG_SIZ / 2];
double nps;
static char *xtra[] = { "", " (--)", " (++)" };
int h, m, s, cs;
}
if (programStats.got_only_move) {
- strcpy(buf, programStats.movelist);
+ safeStrCpy(buf, programStats.movelist, sizeof(buf));
} else {
+ safeStrCpy( lst, programStats.movelist, sizeof(lst));
+
nps = (((double)programStats.nodes) /
(((double)programStats.time)/100.0));
programStats.depth,
programStats.nr_moves-programStats.moves_left,
programStats.nr_moves, programStats.move_name,
- ((float)programStats.score)/100.0, programStats.movelist,
- only_one_move(programStats.movelist)?
+ ((float)programStats.score)/100.0, lst,
+ only_one_move(lst)?
xtra[programStats.got_fail] : "",
programStats.nodes, (int)nps, h, m, s, cs);
} else {
programStats.depth,
programStats.nr_moves-programStats.moves_left,
programStats.nr_moves, ((float)programStats.score)/100.0,
- programStats.movelist,
- only_one_move(programStats.movelist)?
+ lst,
+ only_one_move(lst)?
xtra[programStats.got_fail] : "",
programStats.nodes, (int)nps, h, m, s, cs);
}
sprintf(buf, "depth=%d %+.2f %s%s\nNodes: %lu NPS: %d\nTime: %02d:%02d:%02d.%02d",
programStats.depth,
((float)programStats.score)/100.0,
- programStats.movelist,
- only_one_move(programStats.movelist)?
+ lst,
+ only_one_move(lst)?
xtra[programStats.got_fail] : "",
programStats.nodes, (int)nps, h, m, s, cs);
}
switch ((forwardMostMove + 1) % (movesPerSession * 2)) {
case 0:
/* White made time control */
- whiteTimeRemaining += timeControl;
+ whiteTimeRemaining += GetTimeControlForWhite();
break;
case 1:
/* Black made time control */
- blackTimeRemaining += timeControl;
+ blackTimeRemaining += GetTimeControlForBlack();
break;
default:
break;
if (appData.icsActive) {
whiteTimeRemaining = blackTimeRemaining = 0;
} else {
- whiteTimeRemaining = blackTimeRemaining = timeControl;
+ whiteTimeRemaining = GetTimeControlForWhite();
+ blackTimeRemaining = GetTimeControlForBlack();
}
if (whiteFlag || blackFlag) {
DisplayTitle("");
/*
* backend.h -- Interface exported by XBoard back end
- * $Id$
+ * $Id: backend.h,v 2.1 2003/10/27 19:21:00 mann Exp $
*
* Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
* Enhancements Copyright 1992-95 Free Software Foundation, Inc.
void AbortEvent P((void));
void AdjournEvent P((void));
void ResignEvent P((void));
+void UserAdjudicationEvent P((int result));
void StopObservingEvent P((void));
void StopExaminingEvent P((void));
void PonderNextMoveEvent P((int newState));
int analyzing;
int protocolVersion;
int initDone;
+ int scoreIsAbsolute; /* [AS] 0=don't know (standard), 1=score is always from white side */
} ChessProgramState;
extern ChessProgramState first, second;
/*
* common.h -- Common definitions for X and Windows NT versions of XBoard
- * $Id$
+ * $Id: common.h,v 2.1 2003/10/27 19:21:00 mann Exp $
*
* Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
* Enhancements Copyright 1992-95 Free Software Foundation, Inc.
int firstProtocolVersion;
int secondProtocolVersion;
Boolean showButtonBar;
+ /* [AS] New properties */
+ Boolean firstScoreIsAbsolute; /* If true, engine score is always from white side */
+ Boolean secondScoreIsAbsolute; /* If true, engine score is always from white side */
+ Boolean saveExtendedInfoInPGN; /* If true, saved PGN games contain extended info */
+ Boolean hideThinkingFromHuman; /* If true, program thinking is generated but not displayed in human/computer matches */
+ char * liteBackTextureFile; /* Name of texture bitmap for lite squares */
+ char * darkBackTextureFile; /* Name of texture bitmap for dark squares */
+ int liteBackTextureMode;
+ int darkBackTextureMode;
+ char * renderPiecesWithFont; /* Name of font for rendering chess pieces */
+ char * fontToPieceTable; /* Map to translate font character to chess pieces */
+ int fontBackColorWhite;
+ int fontForeColorWhite;
+ int fontBackColorBlack;
+ int fontForeColorBlack;
+ int fontPieceSize; /* Size of font relative to square (percentage) */
+ int overrideLineGap; /* If >= 0 overrides the lineGap value of the board size properties */
+ int adjudicateLossThreshold; /* Adjudicate a two-machine game if both engines agree the score is below this for 6 plies */
+ int delayBeforeQuit;
+ int delayAfterQuit;
+ char * nameOfDebugFile;
#if ZIPPY
char *zippyLines;
char *zippyPinhead;
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">\r
+<html>\r
+<head>\r
+</head>\r
+<body>\r
+<big><span style="font-weight: bold;">Tinkering with Winboard<br>\r
+by Alessandro Scotti<br>\r
+</span></big><br>\r
+Last update: March 22, 2005.<br>\r
+<br>\r
+This readme is about some modifications I made to Winboard 4.2.7. Some\r
+will work in Xboard too, while others are for Winboard only.\r
+Regardless, everything here is absolutely experimental and has been\r
+tested very little... so no warranties ok?<br>\r
+<br>\r
+All modified sources are included in this archive, the rest can be\r
+downloaded from <a href="http://www.tim-mann.org/xboard.html">Tim\r
+Mann's Xboard page</a>. Please feel free to take my changes and use\r
+them as you like, they are released under the GPL.<br>\r
+<br>\r
+<big><span style="font-weight: bold;">Game adjudication</span></big><br>\r
+<br>\r
+User can adjudicate a game between two machines, using the "Action"\r
+menu. Note that this only works if two machines are playing.<br>\r
+<br>\r
+Also, it is possible to adjudicate a game automatically with this\r
+parameter:<br>\r
+<br>\r
+<div style="margin-left: 40px;">/adjudicateLossThreshold=-700<br>\r
+</div>\r
+<br>\r
+if set to a negative value, Winboard will adjudicate a game if a engine\r
+can't get a score above the specified threshold for 3 full moves (6\r
+plies). Note that both engines must agree on the score value, and also\r
+the game is only adjudicated after the last move from the losing engine.<br>\r
+<br>\r
+Adjudication is still under test!<br>\r
+<br>\r
+<big><span style="font-weight: bold;">Background textures</span></big><br>\r
+<br>\r
+Add the following to winboard.ini:<br>\r
+<br>\r
+<div style="margin-left: 40px;">/liteBackTextureFile="sandstone_w.bmp"<br>\r
+/darkBackTextureFile="sandstone_b.bmp"<br>\r
+/liteBackTextureMode=1<br>\r
+/darkBackTextureMode=1<br>\r
+</div>\r
+<br>\r
+Of course you can choose any file you like for texturing the light and\r
+dark squares, as long as it's a valid BMP (Windows bitmap) file. <br>\r
+<br>\r
+Valid texture modes are 1 (default) and 2. In mode 1 the squares are\r
+taken from portions of the texture bitmap and copied without further\r
+processing. In mode 2, squares can also be rotated, mirrored and so on\r
+in order to provide a little more variety to the texture. The\r
+operations are selected at random so the board will look slightly\r
+different every time the program is run.<br>\r
+<br>\r
+Note: to "comment out" a filename, put an asterisk in front of it, i.e.\r
+"*sandstone.bmp" will be skipped.<br>\r
+<br>\r
+Additionally it is possible to override the size of the "gap" between\r
+the squares, which is otherwise selected automatically. Add this to\r
+winboard.ini:<br>\r
+<br>\r
+<div style="margin-left: 40px;">/overrideLineGap=2<br>\r
+</div>\r
+<br>\r
+I have added this because textures usually look better with a smaller\r
+or no gap. Any negative value will ignore the override and use the\r
+standard value from Winboard.<br>\r
+<br>\r
+The combination of these parameters can produce very interesting\r
+effects, see for example <a\r
+ href="http://usuarios.lycos.es/alexwbtm/Test/">Alex Guerrero's page</a>.<br>\r
+<br>\r
+<big><span style="font-weight: bold;">Font-based rendering of chess\r
+pieces</span></big><br>\r
+<br>\r
+It is possible to use chess fonts to draw pieces, in place of the usual\r
+bitmaps. You have to specify the font name by adding the following to\r
+winboard.ini:<br>\r
+<br>\r
+<div style="margin-left: 40px;">/renderPiecesWithFont="Chess Leipzig"<br>\r
+</div>\r
+<br>\r
+Chances are the program will automatically recognize the font and\r
+select the proper mapping, i.e. the correspondence between a chess\r
+piece and the corresponding font character. So if the board looks good\r
+you're set and there's nothing else to do, otherwise you'll have to\r
+find the proper mapping and tell it to the program with this\r
+setting:<br>\r
+<br>\r
+<div style="margin-left: 40px;">/fontPieceToCharTable="phbrqkojntwl"<br>\r
+</div>\r
+<br>\r
+The piece are in order: white pawn, knight, bishop, rook, queen, king\r
+and black pawn, knight, bishop, rook, queen, king. So the example above\r
+tells the program to use the letter "p" for a white pawn, the letter\r
+"h" for a white knight, the letter "t" for a black rook and so on.<br>\r
+<br>\r
+Note: to "comment out" a font name, put an asterisk in front of it,\r
+i.e. "*Chess Merida" will be skipped because of the asterisk.<br>\r
+<br>\r
+It is possible to customize the font foreground and background color,\r
+with these settings:<br>\r
+<br>\r
+<div style="margin-left: 40px;">/fontPieceBackColorWhite=#ffffcc<br>\r
+/fontPieceForeColorWhite=#402010<br>\r
+/fontPieceBackColorBlack=#ffffcc<br>\r
+/fontPieceForeColorBlack=#301008<br>\r
+</div>\r
+<br>\r
+Colors are expressed in RGB notation. If you are not familiar with the\r
+RGB notation I would suggest to leave them alone until I manage\r
+to update the GUI. In this case, the program will take the board\r
+settings as for the usual piece set.<br>\r
+<br>\r
+It is also possible to control the size of the piece with respect to\r
+the square, for example this entry:<br>\r
+<br>\r
+<div style="margin-left: 40px;">/fontPieceSize=80<br>\r
+</div>\r
+<br>\r
+tells the program to make the pieces about 20% smaller than a square\r
+(i.e. the piece size is 80%). Note that different fonts may require\r
+different values for this parameter. Usually values in the 70-80 range\r
+provide the best values, however the program accepts anything from 50\r
+to 150.<br>\r
+<br>\r
+Font-based pieces are automatically given a "roundish" look. For now,\r
+this option is not mapped to a user-definable setting.<br>\r
+<br>\r
+<big><span style="font-weight: bold;">Fast clipboard pasting</span></big><br>\r
+<br>\r
+Press Ctrl-V to paste a PGN game or a FEN position from the clipboard,\r
+the program will try to autodetect the proper type.<br>\r
+<br>\r
+<big><span style="font-weight: bold;">Thread initialization bug</span></big><br>\r
+<br>\r
+I've tried to fix a bug reported (and fixed) by Anastasios Milikas (of\r
+AICE), where a thread could try to access a not yet initialized\r
+variable. I've used a different approach here, where threads are\r
+started in a "suspended" state and then released only when the variable\r
+has been initialized (this has the advantage of putting all the\r
+required fix code in one place, and the disadvantage of not having been\r
+tested by Anastasios).<br>\r
+<br>\r
+<big><span style="font-weight: bold;">Protection from some buffer\r
+overflows</span></big><br>\r
+<br>\r
+In just a few cases, I've tried to put some guard against buffer\r
+overflows. These are just quick attempts... a lot more work would be\r
+needed to provide some measurable benefits. I hope that bug **353 is\r
+finally fixed now. The buffer overflows were propagated across several\r
+functions so as soon as one was fixed another would crash!<br>\r
+<br>\r
+<big><span style="font-weight: bold;">Absolute scores can be converted\r
+to relative</span></big><br>\r
+<br>\r
+Some engines (e.g. Crafty, Delphi) always report scores from the white\r
+side (i.e. absolute), rather than the engine side (i.e. relative). This\r
+leads to confusion and breaks features such as automatic adjudication.\r
+In order to convert the scores to relative add the following to\r
+winboard.ini:<br>\r
+<br>\r
+<div style="margin-left: 40px;">/firstScoreAbs=false<br>\r
+</div>\r
+<br>\r
+If true, the score of the first program is absolute and will be negated\r
+when playing black. And similarly:<br>\r
+<br>\r
+<div style="margin-left: 40px;">/secondScoreAbs=false<br>\r
+</div>\r
+<br>\r
+if true, the score of the second program is absolute and will be\r
+negated when playing black.<br>\r
+<br>\r
+<big><span style="font-weight: bold;">Engine search info can be saved\r
+in a PGN file</span></big><br>\r
+<br>\r
+Add the following to winboard.ini:<br>\r
+<br>\r
+<div style="margin-left: 40px;">/pgnExtendedInfo=true<br>\r
+</div>\r
+<br>\r
+If true, and the PGN save game option is enabled, each move will be\r
+followed by a comment containing the engine score and thinking\r
+depth (when available).<br>\r
+<br>\r
+Note: can also be set from the General options dialog.<br>\r
+<br>\r
+<big><span style="font-weight: bold;">Engine search info can be hidden\r
+in human-computer games</span></big><br>\r
+<br>\r
+When "show thinking" is disabled, engine thinking is not generated at\r
+all, rather than simply hidden. It is now possible to generate the\r
+think lines (e.g. for logging or saving in the PGN) yet hide them when\r
+the engine is playing against a human player. Add the following to\r
+winboard.ini:<br>\r
+<br>\r
+<div style="margin-left: 40px;">/hideThinkingFromHuman=true<br>\r
+</div>\r
+<br>\r
+Note: can also be set from the General options dialog.<br>\r
+<br>\r
+<big><span style="font-weight: bold;">Asymmetric time controls</span></big><br>\r
+<br>\r
+Very experimental! You can enter two time controls in the usual dialog,\r
+separated by "/". If so, white will be assigned the first time and\r
+black the second. So for example "0:30/2:00" will give 30 seconds to\r
+white and 2 minutes to black.<br>\r
+<br>\r
+Note: for now these times stay with black and white, rather than the\r
+corresponding player.<br>\r
+<br>\r
+<big><span style="font-weight: bold;">User interface</span></big><br>\r
+<br>\r
+Options to "hide thinking from human" and "save extended info in PGN"\r
+now appears in the "General" options dialog.<br>\r
+<br>\r
+</body>\r
+</html>\r
#define IDM_GeneralOptions 1299\r
#define IDM_BoardOptions 1300\r
#define IDM_Fonts 1301\r
+#define IDM_UserAdjudication_White 1302
+#define IDM_UserAdjudication_Black 1303
+#define IDM_UserAdjudication_Draw 1304
#define PB_King 1307\r
#define OPT_Bold 1312\r
#define OPT_Italic 1313\r
#define OPT_DefaultFonts 1419\r
#define OPT_AutoRaiseBoard 1421\r
#define OPT_ShowButtonBar 1422\r
+#define OPT_SaveExtPGN 1423
+#define OPT_HideThinkFromHuman 1424
#define IDC_STATIC -1\r
\r
// Next default values for new objects\r
#ifndef APSTUDIO_READONLY_SYMBOLS\r
#define _APS_NO_MFC 1\r
#define _APS_NEXT_RESOURCE_VALUE 457\r
-#define _APS_NEXT_COMMAND_VALUE 1302\r
-#define _APS_NEXT_CONTROL_VALUE 1423\r
+#define _APS_NEXT_COMMAND_VALUE 1305
+#define _APS_NEXT_CONTROL_VALUE 1425
#define _APS_NEXT_SYMED_VALUE 1404\r
#endif\r
#endif\r
/*\r
* wclipbrd.c -- Clipboard routines for WinBoard\r
- * $Id$\r
+ * $Id: wclipbrd.c,v 2.1 2003/10/27 19:21:02 mann Exp $
*\r
* Copyright 2000 Free Software Foundation, Inc.\r
*\r
return TRUE;\r
}\r
\r
+/* [AS] Reworked paste functions so they can work with strings too */
+
+VOID PasteFENFromString( char * fen )
+{
+ if (appData.debugMode) {
+ fprintf(debugFP, "PasteFenFromString(): fen '%s'\n", fen);
+ }
+ EditPositionPasteFEN(fen); /* call into backend */
+ free(fen);
+}
\r
\r
VOID\r
DisplayError("Unable to paste FEN from clipboard.", 0);\r
return;\r
}\r
- if (appData.debugMode) {\r
- fprintf(debugFP, "PasteFenFromClipboard(): fen '%s'\n", fen);\r
- }\r
- EditPositionPasteFEN(fen); /* call into backend */\r
- free(fen);\r
+ PasteFENFromString( fen );
}\r
\r
-\r
-VOID\r
-PasteGameFromClipboard()\r
+VOID PasteGameFromString( char * buf )
{\r
- /* Write the clipboard to a temp file, then let LoadGameFromFile()\r
- * do all the work. */\r
- char *buf;\r
FILE *f;\r
size_t len;\r
- if (!PasteTextFromClipboard(&buf)) {\r
- return;\r
- }\r
if (!pasteTemp) {\r
pasteTemp = tempnam(NULL, "wbpt");\r
}\r
f = fopen(pasteTemp, "w");\r
if (!f) {\r
DisplayError("Unable to create temporary file.", 0);\r
+ free(buf); /* [AS] */
return;\r
}\r
len = fwrite(buf, sizeof(char), strlen(buf), f);\r
fclose(f);\r
if (len != strlen(buf)) {\r
DisplayError("Error writing to temporary file.", 0);\r
+ free(buf); /* [AS] */
return;\r
}\r
LoadGameFromFile(pasteTemp, 0, "Clipboard", TRUE);\r
+ free( buf ); /* [AS] */
+}
+
+
+VOID
+PasteGameFromClipboard()
+{
+ /* Write the clipboard to a temp file, then let LoadGameFromFile()
+ * do all the work. */
+ char *buf;
+ if (!PasteTextFromClipboard(&buf)) {
+ return;
+ }
+ PasteGameFromString( buf );
}\r
\r
+/* [AS] Try to detect whether the clipboard contains FEN or PGN data */
+VOID PasteGameOrFENFromClipboard()
+{
+ char *buf;
+ char *tmp;
+
+ if (!PasteTextFromClipboard(&buf)) {
+ return;
+ }
+
+ tmp = buf;
+ while( *tmp == ' ' || *tmp == '\t' || *tmp == '\r' || *tmp == '\n' ) {
+ tmp++;
+ }
+
+ if( *tmp == '[' ) {
+ PasteGameFromString( buf );
+ }
+ else {
+ PasteFENFromString( buf );
+ }
+}
\r
int \r
PasteTextFromClipboard(char **text)\r
/*\r
* wclipbrd.c -- Clipboard routines for WinBoard\r
- * $Id$\r
+ * $Id: wclipbrd.h,v 2.1 2003/10/27 19:21:02 mann Exp $
*\r
* Copyright 2000 Free Software Foundation, Inc.\r
*\r
int PasteTextFromClipboard(char **text);\r
\r
VOID DeleteClipboardTempFiles();\r
+
+VOID PasteGameOrFENFromClipboard(); /* [AS] */
/* \r
* WinBoard.c -- Windows NT front end to XBoard\r
- * $Id$\r
+ * $Id: winboard.c,v 2.3 2003/11/25 05:25:20 mann Exp $
*\r
* Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.\r
* Enhancements Copyright 1992-2001 Free Software Foundation, Inc.\r
\r
#include <stdio.h>\r
#include <stdlib.h>\r
+#include <time.h>
#include <malloc.h>\r
#include <sys/stat.h>\r
#include <fcntl.h>\r
#include "wsockerr.h"\r
#include "defaults.h"\r
\r
+int myrandom(void);
+void mysrandom(unsigned int seed);
+
typedef struct {\r
ChessSquare piece; \r
POINT pos; /* window coordinates of current pos */\r
char *firstChessProgramNames;\r
char *secondChessProgramNames;\r
\r
-#define ARG_MAX 20000\r
+#define ARG_MAX 64*1024 /* [AS] For Roger Brown's very long list! */
\r
#define PALETTESIZE 256\r
\r
static int lastSizing = 0;\r
static int prevStderrPort;\r
\r
+/* [AS] Support for background textures */
+#define BACK_TEXTURE_MODE_DISABLED 0
+#define BACK_TEXTURE_MODE_PLAIN 1
+#define BACK_TEXTURE_MODE_FULL_RANDOM 2
+
+static HBITMAP liteBackTexture = NULL;
+static HBITMAP darkBackTexture = NULL;
+static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
+static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
+static int backTextureSquareSize = 0;
+static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];
+
#if __GNUC__ && !defined(_winmajor)\r
#define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
#else\r
}\r
InitAppData(lpCmdLine); /* Get run-time parameters */\r
if (appData.debugMode) {\r
- debugFP = fopen("winboard.debug", "w");\r
+ debugFP = fopen(appData.nameOfDebugFile, "w");
setbuf(debugFP, NULL);\r
}\r
\r
InitMenuChecks();\r
buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
\r
+ /* [AS] Load textures if specified */
+ ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
+
+ if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
+ liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
+ liteBackTextureMode = appData.liteBackTextureMode;
+
+ if (liteBackTexture == NULL && appData.debugMode) {
+ fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
+ }
+ }
+
+ if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
+ darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
+ darkBackTextureMode = appData.darkBackTextureMode;
+
+ if (darkBackTexture == NULL && appData.debugMode) {
+ fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
+ }
+ }
+
+ mysrandom( (unsigned) time(NULL) );
+
/* Make a console window if needed */\r
if (appData.icsActive) {\r
ConsoleCreate();\r
{ "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
{ "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
{ "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
- { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion,\r
- FALSE },\r
- { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,\r
- FALSE },\r
+ { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },
+ { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },
{ "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
{ "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
{ "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
{ "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
+ /* [AS] New features */
+ { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, TRUE },
+ { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, TRUE },
+ { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },
+ { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },
+ { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },
+ { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },
+ { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },
+ { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },
+ { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },
+ { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },
+ { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },
+ { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },
+ { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },
+ { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },
+ { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },
+ { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },
+ { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },
+ { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },
+ { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },
+ { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },
+ { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },
+ { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },
#ifdef ZIPPY\r
{ "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
{ "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
appData.firstProtocolVersion = PROTOVER;\r
appData.secondProtocolVersion = PROTOVER;\r
appData.showButtonBar = TRUE;\r
+
+ /* [AS] New properties (see comments in header file) */
+ appData.firstScoreIsAbsolute = FALSE;
+ appData.secondScoreIsAbsolute = FALSE;
+ appData.saveExtendedInfoInPGN = FALSE;
+ appData.hideThinkingFromHuman = FALSE;
+ appData.liteBackTextureFile = "";
+ appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
+ appData.darkBackTextureFile = "";
+ appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
+ appData.renderPiecesWithFont = "";
+ appData.fontToPieceTable = "";
+ appData.fontBackColorWhite = 0;
+ appData.fontForeColorWhite = 0;
+ appData.fontBackColorBlack = 0;
+ appData.fontForeColorBlack = 0;
+ appData.fontPieceSize = 80;
+ appData.overrideLineGap = 1;
+ appData.adjudicateLossThreshold = 0;
+ appData.delayBeforeQuit = 0;
+ appData.delayAfterQuit = 0;
+ appData.nameOfDebugFile = "winboard.debug";
+ appData.pgnEventHeader = "Computer Chess Game";
+
#ifdef ZIPPY\r
appData.zippyTalk = ZIPPY_TALK;\r
appData.zippyPlay = ZIPPY_PLAY;\r
*\r
\*---------------------------------------------------------------------------*/\r
\r
+/* [AS] Draw square using background texture */
+static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
+{
+ XFORM x;
+
+ if( mode == 0 ) {
+ return; /* Should never happen! */
+ }
+
+ SetGraphicsMode( dst, GM_ADVANCED );
+
+ switch( mode ) {
+ case 1:
+ /* Identity */
+ break;
+ case 2:
+ /* X reflection */
+ x.eM11 = -1.0;
+ x.eM12 = 0;
+ x.eM21 = 0;
+ x.eM22 = 1.0;
+ x.eDx = (FLOAT) dw + dx - 1;
+ x.eDy = 0;
+ dx = 0;
+ SetWorldTransform( dst, &x );
+ break;
+ case 3:
+ /* Y reflection */
+ x.eM11 = 1.0;
+ x.eM12 = 0;
+ x.eM21 = 0;
+ x.eM22 = -1.0;
+ x.eDx = 0;
+ x.eDy = (FLOAT) dh + dy - 1;
+ dy = 0;
+ SetWorldTransform( dst, &x );
+ break;
+ case 4:
+ /* X/Y flip */
+ x.eM11 = 0;
+ x.eM12 = 1.0;
+ x.eM21 = 1.0;
+ x.eM22 = 0;
+ x.eDx = (FLOAT) dx;
+ x.eDy = (FLOAT) dy;
+ dx = 0;
+ dy = 0;
+ SetWorldTransform( dst, &x );
+ break;
+ }
+
+ BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
+
+ x.eM11 = 1.0;
+ x.eM12 = 0;
+ x.eM21 = 0;
+ x.eM22 = 1.0;
+ x.eDx = 0;
+ x.eDy = 0;
+ SetWorldTransform( dst, &x );
+
+ ModifyWorldTransform( dst, 0, MWT_IDENTITY );
+}
+
+/* [AS] */
+enum {
+ PM_WP = 0,
+ PM_WN = 1,
+ PM_WB = 2,
+ PM_WR = 3,
+ PM_WQ = 4,
+ PM_WK = 5,
+ PM_BP = 6,
+ PM_BN = 7,
+ PM_BB = 8,
+ PM_BR = 9,
+ PM_BQ = 10,
+ PM_BK = 11
+};
+
+static HFONT hPieceFont = NULL;
+static HBITMAP hPieceMask[12];
+static HBITMAP hPieceFace[12];
+static int fontBitmapSquareSize = 0;
+static char pieceToFontChar[12] = { 'p', 'n', 'b', 'r', 'q', 'k', 'o', 'm', 'v', 't', 'w', 'l' };
+
+static BOOL SetPieceToFontCharTable( const char * map )
+{
+ BOOL result = FALSE;
+
+ if( map != NULL && strlen(map) == 12 ) {
+ int i;
+
+ for( i=0; i<12; i++ ) {
+ pieceToFontChar[i] = map[i];
+ }
+
+ result = TRUE;
+ }
+
+ return result;
+}
+
+static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
+{
+ HBRUSH hbrush;
+ BYTE r1 = GetRValue( color );
+ BYTE g1 = GetGValue( color );
+ BYTE b1 = GetBValue( color );
+ BYTE r2 = r1 / 2;
+ BYTE g2 = g1 / 2;
+ BYTE b2 = b1 / 2;
+ RECT rc;
+
+ /* Create a uniform background first */
+ hbrush = CreateSolidBrush( color );
+ SetRect( &rc, 0, 0, squareSize, squareSize );
+ FillRect( hdc, &rc, hbrush );
+ DeleteObject( hbrush );
+
+ if( mode == 1 ) {
+ /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
+ int steps = squareSize / 2;
+ int i;
+
+ for( i=0; i<steps; i++ ) {
+ BYTE r = r1 - (r1-r2) * i / steps;
+ BYTE g = g1 - (g1-g2) * i / steps;
+ BYTE b = b1 - (b1-b2) * i / steps;
+
+ hbrush = CreateSolidBrush( RGB(r,g,b) );
+ SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
+ FillRect( hdc, &rc, hbrush );
+ DeleteObject(hbrush);
+ }
+ }
+ else if( mode == 2 ) {
+ /* Diagonal gradient, good more or less for every piece */
+ POINT triangle[3];
+ HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
+ HBRUSH hbrush_old;
+ int steps = squareSize;
+ int i;
+
+ triangle[0].x = squareSize - steps;
+ triangle[0].y = squareSize;
+ triangle[1].x = squareSize;
+ triangle[1].y = squareSize;
+ triangle[2].x = squareSize;
+ triangle[2].y = squareSize - steps;
+
+ for( i=0; i<steps; i++ ) {
+ BYTE r = r1 - (r1-r2) * i / steps;
+ BYTE g = g1 - (g1-g2) * i / steps;
+ BYTE b = b1 - (b1-b2) * i / steps;
+
+ hbrush = CreateSolidBrush( RGB(r,g,b) );
+ hbrush_old = SelectObject( hdc, hbrush );
+ Polygon( hdc, triangle, 3 );
+ SelectObject( hdc, hbrush_old );
+ DeleteObject(hbrush);
+ triangle[0].x++;
+ triangle[2].y++;
+ }
+
+ SelectObject( hdc, hpen );
+ }
+}
+
+/*
+ [AS] The method I use to create the bitmaps it a bit tricky, but it
+ seems to work ok. The main problem here is to find the "inside" of a chess
+ piece: follow the steps as explained below.
+*/
+static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
+{
+ HBITMAP hbm;
+ HBITMAP hbm_old;
+ COLORREF chroma = RGB(0xFF,0x00,0xFF);
+ RECT rc;
+ SIZE sz;
+ POINT pt;
+ int backColor = whitePieceColor;
+ int foreColor = blackPieceColor;
+ int shapeIndex = index < 6 ? index+6 : index;
+
+ if( index < 6 && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
+ backColor = appData.fontBackColorWhite;
+ foreColor = appData.fontForeColorWhite;
+ }
+ else if( index >= 6 && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
+ backColor = appData.fontBackColorBlack;
+ foreColor = appData.fontForeColorBlack;
+ }
+
+ /* Mask */
+ hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
+
+ hbm_old = SelectObject( hdc, hbm );
+
+ rc.left = 0;
+ rc.top = 0;
+ rc.right = squareSize;
+ rc.bottom = squareSize;
+
+ /* Step 1: background is now black */
+ FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
+
+ GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
+
+ pt.x = (squareSize - sz.cx) / 2;
+ pt.y = (squareSize - sz.cy) / 2;
+
+ SetBkMode( hdc, TRANSPARENT );
+ SetTextColor( hdc, chroma );
+ /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
+ TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );
+
+ SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
+ /* Step 3: the area outside the piece is filled with white */
+ FloodFill( hdc, 0, 0, chroma );
+ SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
+ /*
+ Step 4: this is the tricky part, the area inside the piece is filled with black,
+ but if the start point is not inside the piece we're lost!
+ There should be a better way to do this... if we could create a region or path
+ from the fill operation we would be fine for example.
+ */
+ FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
+
+ SetTextColor( hdc, 0 );
+ /*
+ Step 5: some fonts have "disconnected" areas that are skipped by the fill:
+ draw the piece again in black for safety.
+ */
+ TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );
+
+ SelectObject( hdc, hbm_old );
+
+ if( hPieceMask[index] != NULL ) {
+ DeleteObject( hPieceMask[index] );
+ }
+
+ hPieceMask[index] = hbm;
+
+ /* Face */
+ hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
+
+ SelectObject( hdc, hbm );
+
+ {
+ HDC dc1 = CreateCompatibleDC( hdc_window );
+ HDC dc2 = CreateCompatibleDC( hdc_window );
+ HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
+
+ SelectObject( dc1, hPieceMask[index] );
+ SelectObject( dc2, bm2 );
+ FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
+ BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
+
+ /*
+ Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
+ the piece background and deletes (makes transparent) the rest.
+ Thanks to that mask, we are free to paint the background with the greates
+ freedom, as we'll be able to mask off the unwanted parts when finished.
+ We use this, to make gradients and give the pieces a "roundish" look.
+ */
+ SetPieceBackground( hdc, backColor, 2 );
+ BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
+
+ DeleteDC( dc2 );
+ DeleteDC( dc1 );
+ DeleteObject( bm2 );
+ }
+
+ SetTextColor( hdc, foreColor );
+ TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );
+
+ SelectObject( hdc, hbm_old );
+
+ hPieceFace[index] = hbm;
+}
+
+static int TranslatePieceToFontPiece( int piece )
+{
+ switch( piece ) {
+ case BlackPawn:
+ return PM_BP;
+ case BlackKnight:
+ return PM_BN;
+ case BlackBishop:
+ return PM_BB;
+ case BlackRook:
+ return PM_BR;
+ case BlackQueen:
+ return PM_BQ;
+ case BlackKing:
+ return PM_BK;
+ case WhitePawn:
+ return PM_WP;
+ case WhiteKnight:
+ return PM_WN;
+ case WhiteBishop:
+ return PM_WB;
+ case WhiteRook:
+ return PM_WR;
+ case WhiteQueen:
+ return PM_WQ;
+ case WhiteKing:
+ return PM_WK;
+ }
+
+ return 0;
+}
+
+void CreatePiecesFromFont()
+{
+ LOGFONT lf;
+ HDC hdc_window = NULL;
+ HDC hdc = NULL;
+ HFONT hfont_old;
+ int fontHeight;
+ int i;
+
+ if( fontBitmapSquareSize < 0 ) {
+ /* Something went seriously wrong in the past: do not try to recreate fonts! */
+ return;
+ }
+
+ if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
+ fontBitmapSquareSize = -1;
+ return;
+ }
+
+ if( fontBitmapSquareSize != squareSize ) {
+ hdc_window = GetDC( hwndMain );
+ hdc = CreateCompatibleDC( hdc_window );
+
+ if( hPieceFont != NULL ) {
+ DeleteObject( hPieceFont );
+ }
+ else {
+ for( i=0; i<12; i++ ) {
+ hPieceMask[i] = NULL;
+ }
+ }
+
+ fontHeight = 75;
+
+ if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
+ fontHeight = appData.fontPieceSize;
+ }
+
+ fontHeight = (fontHeight * squareSize) / 100;
+
+ lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
+ lf.lfWidth = 0;
+ lf.lfEscapement = 0;
+ lf.lfOrientation = 0;
+ lf.lfWeight = FW_NORMAL;
+ lf.lfItalic = 0;
+ lf.lfUnderline = 0;
+ lf.lfStrikeOut = 0;
+ lf.lfCharSet = DEFAULT_CHARSET;
+ lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
+ lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ lf.lfQuality = PROOF_QUALITY;
+ lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+ strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
+ lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
+
+ hPieceFont = CreateFontIndirect( &lf );
+
+ if( hPieceFont == NULL ) {
+ fontBitmapSquareSize = -2;
+ }
+ else {
+ /* Setup font-to-piece character table */
+ if( ! SetPieceToFontCharTable(appData.fontToPieceTable) ) {
+ /* No (or wrong) global settings, try to detect the font */
+ if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
+ /* Alpha */
+ SetPieceToFontCharTable("phbrqkojntwl");
+ }
+ else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
+ /* DiagramTT* family */
+ SetPieceToFontCharTable("PNLRQKpnlrqk");
+ }
+ else {
+ /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
+ SetPieceToFontCharTable("pnbrqkomvtwl");
+ }
+ }
+
+ /* Create bitmaps */
+ hfont_old = SelectObject( hdc, hPieceFont );
+
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );
+
+ SelectObject( hdc, hfont_old );
+
+ fontBitmapSquareSize = squareSize;
+ }
+ }
+
+ if( hdc != NULL ) {
+ DeleteDC( hdc );
+ }
+
+ if( hdc_window != NULL ) {
+ ReleaseDC( hwndMain, hdc_window );
+ }
+}
+
HBITMAP\r
DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
{\r
whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
+
+ /* [AS] Force rendering of the font-based pieces */
+ if( fontBitmapSquareSize > 0 ) {
+ fontBitmapSquareSize = 0;
+ }
}\r
\r
\r
int\r
BoardWidth(int boardSize)\r
{\r
- return (BOARD_SIZE + 1) * sizeInfo[boardSize].lineGap +\r
+ int lineGap = sizeInfo[boardSize].lineGap;
+
+ if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
+ lineGap = appData.overrideLineGap;
+ }
+
+ return (BOARD_SIZE + 1) * lineGap +
BOARD_SIZE * sizeInfo[boardSize].squareSize;\r
}\r
\r
squareSize = sizeInfo[boardSize].squareSize;\r
lineGap = sizeInfo[boardSize].lineGap;\r
\r
+ if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
+ lineGap = appData.overrideLineGap;
+ }
+
if (tinyLayout != oldTinyLayout) {\r
long style = GetWindowLong(hwndMain, GWL_STYLE);\r
if (tinyLayout) {\r
\r
if (appData.blindfold) return;\r
\r
+ /* [AS] Use font-based pieces if needed */
+ if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {
+ /* Create piece bitmaps, or do nothing if piece set is up to data */
+ CreatePiecesFromFont();
+
+ if( fontBitmapSquareSize == squareSize ) {
+ int index = TranslatePieceToFontPiece( piece );
+
+ SelectObject( tmphdc, hPieceMask[ index ] );
+
+ BitBlt( hdc,
+ x, y,
+ squareSize, squareSize,
+ tmphdc,
+ 0, 0,
+ SRCAND );
+
+ SelectObject( tmphdc, hPieceFace[ index ] );
+
+ BitBlt( hdc,
+ x, y,
+ squareSize, squareSize,
+ tmphdc,
+ 0, 0,
+ SRCPAINT );
+
+ return;
+ }
+ }
+
if (appData.monoMode) {\r
SelectObject(tmphdc, PieceBitmap(piece, \r
color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
}\r
}\r
\r
+/* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
+int GetBackTextureMode( int algo )
+{
+ int result = BACK_TEXTURE_MODE_DISABLED;
+
+ switch( algo )
+ {
+ case BACK_TEXTURE_MODE_PLAIN:
+ result = 1; /* Always use identity map */
+ break;
+ case BACK_TEXTURE_MODE_FULL_RANDOM:
+ result = 1 + (myrandom() % 3); /* Pick a transformation at random */
+ break;
+ }
+
+ return result;
+}
+
+/*
+ [AS] Compute and save texture drawing info, otherwise we may not be able
+ to handle redraws cleanly (as random numbers would always be different).
+*/
+VOID RebuildTextureSquareInfo()
+{
+ BITMAP bi;
+ int lite_w = 0;
+ int lite_h = 0;
+ int dark_w = 0;
+ int dark_h = 0;
+ int row;
+ int col;
+
+ ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
+
+ if( liteBackTexture != NULL ) {
+ if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
+ lite_w = bi.bmWidth;
+ lite_h = bi.bmHeight;
+ }
+ }
+
+ if( darkBackTexture != NULL ) {
+ if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
+ dark_w = bi.bmWidth;
+ dark_h = bi.bmHeight;
+ }
+ }
+
+ for( row=0; row<BOARD_SIZE; row++ ) {
+ for( col=0; col<BOARD_SIZE; col++ ) {
+ if( (col + row) & 1 ) {
+ /* Lite square */
+ if( lite_w >= squareSize && lite_h >= squareSize ) {
+ backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / BOARD_SIZE;
+ backTextureSquareInfo[row][col].y = row * (lite_h - squareSize) / BOARD_SIZE;
+ backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
+ }
+ }
+ else {
+ /* Dark square */
+ if( dark_w >= squareSize && dark_h >= squareSize ) {
+ backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / BOARD_SIZE;
+ backTextureSquareInfo[row][col].y = row * (dark_h - squareSize) / BOARD_SIZE;
+ backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
+ }
+ }
+ }
+ }
+}
+
VOID\r
DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
{\r
int row, column, x, y, square_color, piece_color;\r
ChessSquare piece;\r
HBRUSH oldBrush;\r
+ HDC texture_hdc = NULL;
+
+ /* [AS] Initialize background textures if needed */
+ if( liteBackTexture != NULL || darkBackTexture != NULL ) {
+ if( backTextureSquareSize != squareSize ) {
+ backTextureSquareSize = squareSize;
+ RebuildTextureSquareInfo();
+ }
+
+ texture_hdc = CreateCompatibleDC( hdc );
+ }
\r
for (row = 0; row < BOARD_SIZE; row++) {\r
for (column = 0; column < BOARD_SIZE; column++) {\r
} else {\r
DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
}\r
- } else {\r
+ }
+ else if( backTextureSquareInfo[row][column].mode > 0 ) {
+ /* [AS] Draw the square using a texture bitmap */
+ HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
+
+ DrawTile( x, y,
+ squareSize, squareSize,
+ hdc,
+ texture_hdc,
+ backTextureSquareInfo[row][column].mode,
+ backTextureSquareInfo[row][column].x,
+ backTextureSquareInfo[row][column].y );
+
+ SelectObject( texture_hdc, hbm );
+
+ if (piece != EmptySquare) {
+ DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
+ }
+ }
+ else {
oldBrush = SelectObject(hdc, square_color ?\r
lightSquareBrush : darkSquareBrush);\r
BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
}\r
}\r
}\r
+
+ if( texture_hdc != NULL ) {
+ DeleteDC( texture_hdc );
+ }
}\r
\r
#define MAX_CLIPS 200 /* more than enough */\r
PasteGameFromClipboard();\r
break;\r
\r
+ /* [AS] Autodetect FEN or PGN data */
+ case IDM_Paste:
+ PasteGameOrFENFromClipboard();
+ break;
+
+ /* [AS] User adjudication */
+ case IDM_UserAdjudication_White:
+ UserAdjudicationEvent( +1 );
+ break;
+
+ case IDM_UserAdjudication_Black:
+ UserAdjudicationEvent( -1 );
+ break;
+
+ case IDM_UserAdjudication_Draw:
+ UserAdjudicationEvent( 0 );
+ break;
+
case IDM_CopyPosition:\r
CopyFENToClipboard();\r
break;\r
char dir[MSG_SIZ];\r
GetCurrentDirectory(MSG_SIZ, dir);\r
SetCurrentDirectory(installDir);\r
- debugFP = fopen("WinBoard.debug", "w");\r
+ debugFP = fopen(appData.nameOfDebugFile, "w");
SetCurrentDirectory(dir);\r
setbuf(debugFP, NULL);\r
} else {\r
SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
}\r
- if (chessProgram) {\r
- CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
- } else if (appData.icsActive) {\r
+
+ if (appData.icsActive) {
CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
- } else if (appData.noChessProgram) {\r
+ }
+ else if (appData.noChessProgram) {
CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
}\r
+ else {
+ CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
+ }
+
SetStartupDialogEnables(hDlg);\r
return TRUE;\r
\r
return err;\r
}\r
\r
+/* [AS] If input is line by line and a line exceed the buffer size, force an error */
+void CheckForInputBufferFull( InputSource * is )
+{
+ if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
+ /* Look for end of line */
+ char * p = is->buf;
+
+ while( p < is->next && *p != '\n' ) {
+ p++;
+ }
+
+ if( p >= is->next ) {
+ if (appData.debugMode) {
+ fprintf( debugFP, "Input line exceeded buffer size (source id=%u)\n", is->id );
+ }
+
+ is->error = ERROR_BROKEN_PIPE; /* [AS] Just a non-successful code! */
+ is->count = (DWORD) -2;
+ is->next = is->buf;
+ }
+ }
+}
\r
DWORD\r
InputThread(LPVOID arg)\r
is->count = (DWORD) -1;\r
}\r
}\r
+
+ CheckForInputBufferFull( is );
+
SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
if (is->count <= 0) break; /* Quit on EOF or error */\r
}\r
is->count = (DWORD) -1;\r
}\r
}\r
+
+ CheckForInputBufferFull( is );
+
SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
if (is->count < 0) break; /* Quit on error */\r
}\r
p = q;\r
}\r
}\r
+
/* Move any partial line to the start of the buffer */\r
q = is->buf;\r
while (p < is->next) {\r
*q++ = *p++;\r
}\r
is->next = q;\r
+
if (is->error != NO_ERROR || is->count == 0) {\r
/* Notify backend of the error. Note: If there was a partial\r
line at the end, it is not flushed through. */\r
VOID\r
DisplayError(char *str, int error)\r
{\r
- FARPROC lpProc;\r
char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
int len;\r
- char *p, *q;\r
\r
if (error == 0) {\r
strcpy(buf, str);\r
we could arrange for this even though neither WinBoard\r
nor the chess program uses a console for stdio? */\r
/*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
+
+ /* [AS] Special termination modes for misbehaving programs... */
+ if( signal == 9 ) {
+ if ( appData.debugMode) {
+ fprintf( debugFP, "Terminating process %u\n", cp->pid );
+ }
+
+ TerminateProcess( cp->hProcess, 0 );
+ }
+ else if( signal == 10 ) {
+ DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
+
+ if( dw != WAIT_OBJECT_0 ) {
+ if ( appData.debugMode) {
+ fprintf( debugFP, "Process %u still alive after timeout, killing...\n", cp->pid );
+ }
+
+ TerminateProcess( cp->hProcess, 0 );
+ }
+ }
+
CloseHandle(cp->hProcess);\r
break;\r
\r
AddInputSource(ProcRef pr, int lineByLine,\r
InputCallback func, VOIDSTAR closure)\r
{\r
- InputSource *is, *is2;\r
+ InputSource *is, *is2 = NULL;
ChildProc *cp = (ChildProc *) pr;\r
\r
is = (InputSource *) calloc(1, sizeof(InputSource));\r
consoleInputSource = is;\r
} else {\r
is->kind = cp->kind;\r
+ /*
+ [AS] Try to avoid a race condition if the thread is given control too early:
+ we create all threads suspended to that the is->hThread variable can be
+ safely assigned, then let the threads start with ResumeThread.
+ */
switch (cp->kind) {\r
case CPReal:\r
is->hFile = cp->hFrom;\r
cp->hFrom = NULL; /* now owned by InputThread */\r
is->hThread =\r
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
- (LPVOID) is, 0, &is->id);\r
+ (LPVOID) is, CREATE_SUSPENDED, &is->id);
break;\r
\r
case CPComm:\r
cp->hFrom = NULL; /* now owned by InputThread */\r
is->hThread =\r
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
- (LPVOID) is, 0, &is->id);\r
+ (LPVOID) is, CREATE_SUSPENDED, &is->id);
break;\r
\r
case CPSock:\r
is->sock = cp->sock;\r
is->hThread =\r
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
- (LPVOID) is, 0, &is->id);\r
+ (LPVOID) is, CREATE_SUSPENDED, &is->id);
break;\r
\r
case CPRcmd:\r
is2->second = is2;\r
is->hThread =\r
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
- (LPVOID) is, 0, &is->id);\r
+ (LPVOID) is, CREATE_SUSPENDED, &is->id);
is2->hThread =\r
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
- (LPVOID) is2, 0, &is2->id);\r
+ (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
break;\r
}\r
+
+ if( is->hThread != NULL ) {
+ ResumeThread( is->hThread );
}\r
+
+ if( is2 != NULL && is2->hThread != NULL ) {
+ ResumeThread( is2->hThread );
+ }
+ }
+
return (InputSourceRef) is;\r
}\r
\r
--- /dev/null
+;
+; WinBoard 4.2.7 Save Settings file
+;
+; You can edit the values of options that are already set in this file,
+; but if you add other options, the next Save Settings will not save them.
+; Use a shortcut, an @indirection file, or a .bat file instead.
+;
+/whitePieceColor=#ffffcc
+/blackPieceColor=#202020
+/lightSquareColor=#c8c365
+/darkSquareColor=#77a26d
+/highlightSquareColor=#ffff00
+/premoveHighlightColor=#ff0000
+/movesPerSession=40
+/timeDelay=1
+/timeControl="5"
+/timeIncrement=-1
+/saveGameFile=""
+/autoSaveGames=false
+/monoMode=false
+/showCoords=false
+/showThinking=false
+/ponderNextMove=true
+/periodicUpdates=true
+/popupExitMessage=true
+/popupMoveErrors=false
+/size=tiny /clockFont="Arial:9 b"
+/size=teeny /clockFont="Arial:9 b"
+/size=dinky /clockFont="Arial:10 b"
+/size=petite /clockFont="Arial:10 b"
+/size=slim /clockFont="Arial:12 b"
+/size=small /clockFont="Arial:14 b"
+/size=mediocre /clockFont="Arial:14 b"
+/size=middling /clockFont="Arial:14 b"
+/size=average /clockFont="Arial:15 b"
+/size=moderate /clockFont="Arial:16 b"
+/size=medium /clockFont="Arial:16 b"
+/size=bulky /clockFont="Arial:17 b"
+/size=large /clockFont="Arial:19 b"
+/size=big /clockFont="Arial:20 b"
+/size=huge /clockFont="Arial:21 b"
+/size=giant /clockFont="Arial:22 b"
+/size=colossal /clockFont="Arial:23 b"
+/size=titanic /clockFont="Arial:24 b"
+/size=tiny /messageFont="Small Fonts:6"
+/size=teeny /messageFont="Small Fonts:6"
+/size=dinky /messageFont="Small Fonts:7"
+/size=petite /messageFont="Small Fonts:7"
+/size=slim /messageFont="Arial:8 b"
+/size=small /messageFont="Arial:9 b"
+/size=mediocre /messageFont="Arial:9 b"
+/size=middling /messageFont="Arial:9 b"
+/size=average /messageFont="Arial:10 b"
+/size=moderate /messageFont="Arial:10 b"
+/size=medium /messageFont="Arial:10 b"
+/size=bulky /messageFont="Arial:10 b"
+/size=large /messageFont="Arial:10 b"
+/size=big /messageFont="Arial:11 b"
+/size=huge /messageFont="Arial:11 b"
+/size=giant /messageFont="Arial:11 b"
+/size=colossal /messageFont="Arial:12 b"
+/size=titanic /messageFont="Arial:12 b"
+/size=tiny /coordFont="Small Fonts:4"
+/size=teeny /coordFont="Small Fonts:4"
+/size=dinky /coordFont="Small Fonts:5"
+/size=petite /coordFont="Small Fonts:5"
+/size=slim /coordFont="Small Fonts:6"
+/size=small /coordFont="Small Fonts:7"
+/size=mediocre /coordFont="Small Fonts:7"
+/size=middling /coordFont="Small Fonts:7"
+/size=average /coordFont="Arial:7 b"
+/size=moderate /coordFont="Arial:7 b"
+/size=medium /coordFont="Arial:7 b"
+/size=bulky /coordFont="Arial:7 b"
+/size=large /coordFont="Arial:7 b"
+/size=big /coordFont="Arial:8 b"
+/size=huge /coordFont="Arial:8 b"
+/size=giant /coordFont="Arial:8 b"
+/size=colossal /coordFont="Arial:9 b"
+/size=titanic /coordFont="Arial:9 b"
+/size=tiny /tagsFont="Courier New:8"
+/size=teeny /tagsFont="Courier New:8"
+/size=dinky /tagsFont="Courier New:8"
+/size=petite /tagsFont="Courier New:8"
+/size=slim /tagsFont="Courier New:8"
+/size=small /tagsFont="Courier New:8"
+/size=mediocre /tagsFont="Courier New:8"
+/size=middling /tagsFont="Courier New:8"
+/size=average /tagsFont="Courier New:8"
+/size=moderate /tagsFont="Courier New:8"
+/size=medium /tagsFont="Courier New:8"
+/size=bulky /tagsFont="Courier New:8"
+/size=large /tagsFont="Courier New:8"
+/size=big /tagsFont="Courier New:8"
+/size=huge /tagsFont="Courier New:8"
+/size=giant /tagsFont="Courier New:8"
+/size=colossal /tagsFont="Courier New:8"
+/size=titanic /tagsFont="Courier New:8"
+/size=tiny /commentFont="Arial:9"
+/size=teeny /commentFont="Arial:9"
+/size=dinky /commentFont="Arial:9"
+/size=petite /commentFont="Arial:9"
+/size=slim /commentFont="Arial:9"
+/size=small /commentFont="Arial:9"
+/size=mediocre /commentFont="Arial:9"
+/size=middling /commentFont="Arial:9"
+/size=average /commentFont="Arial:9"
+/size=moderate /commentFont="Arial:9"
+/size=medium /commentFont="Arial:9"
+/size=bulky /commentFont="Arial:9"
+/size=large /commentFont="Arial:9"
+/size=big /commentFont="Arial:9"
+/size=huge /commentFont="Arial:9"
+/size=giant /commentFont="Arial:9"
+/size=colossal /commentFont="Arial:9"
+/size=titanic /commentFont="Arial:9"
+/size=tiny /icsFont="Courier New:8"
+/size=teeny /icsFont="Courier New:8"
+/size=dinky /icsFont="Courier New:8"
+/size=petite /icsFont="Courier New:8"
+/size=slim /icsFont="Courier New:8"
+/size=small /icsFont="Courier New:8"
+/size=mediocre /icsFont="Courier New:8"
+/size=middling /icsFont="Courier New:8"
+/size=average /icsFont="Courier New:8"
+/size=moderate /icsFont="Courier New:8"
+/size=medium /icsFont="Courier New:8"
+/size=bulky /icsFont="Courier New:8"
+/size=large /icsFont="Courier New:8"
+/size=big /icsFont="Courier New:8"
+/size=huge /icsFont="Courier New:8"
+/size=giant /icsFont="Courier New:8"
+/size=colossal /icsFont="Courier New:8"
+/size=titanic /icsFont="Courier New:8"
+/boardSize=giant
+/alwaysOnTop=false
+/autoCallFlag=false
+/autoComment=false
+/autoObserve=false
+/autoFlipView=true
+/autoRaiseBoard=true
+/alwaysPromoteToQueen=false
+/oldSaveStyle=false
+/quietPlay=false
+/getMoveList=true
+/testLegality=true
+/premove=true
+/premoveWhite=false
+/premoveWhiteText=""
+/premoveBlack=false
+/premoveBlackText=""
+/icsAlarm=true
+/icsAlarmTime=5000
+/animateMoving=true
+/animateSpeed=10
+/animateDragging=true
+/blindfold=false
+/highlightLastMove=true
+/highlightDragging=false
+/colorizeMessages=true
+/colorShout="#209000"
+/colorSShout="b #289808"
+/colorChannel1="#2020e0"
+/colorChannel="b #4040ff"
+/colorKibitz="b #ff00ff"
+/colorTell="b #ff0000"
+/colorChallenge="bi #ff0000"
+/colorRequest="bi #ff0000"
+/colorSeek="#980808"
+/colorNormal="#000000"
+/colorBackground=#ffffff
+/soundShout=""
+/soundSShout=""
+/soundChannel1=""
+/soundChannel=""
+/soundKibitz=""
+/soundTell=""
+/soundChallenge=""
+/soundRequest=""
+/soundSeek=""
+/soundMove=""
+/soundBell="$"
+/soundIcsWin=""
+/soundIcsLoss=""
+/soundIcsDraw=""
+/soundIcsUnfinished=""
+/soundIcsAlarm=""
+/comPortSettings=9600,7,Space,1,None
+/x=0
+/y=2
+/icsX=-2147483648
+/icsY=-2147483648
+/icsW=-2147483648
+/icsH=-2147483648
+/analysisX=-2147483648
+/analysisY=-2147483648
+/analysisW=-2147483648
+/analysisH=-2147483648
+/commentX=-2147483648
+/commentY=-2147483648
+/commentW=-2147483648
+/commentH=-2147483648
+/tagsX=-2147483648
+/tagsY=-2147483648
+/tagsW=-2147483648
+/tagsH=-2147483648
+/gameListX=-2147483648
+/gameListY=-2147483648
+/gameListW=-2147483648
+/gameListH=-2147483648
+/saveSettingsOnExit=true
+/icsMenu={-
+&Who,who,0,1
+Playe&rs,players,0,1
+&Games,games,0,1
+&Sought,sought,0,1
+|&Tell (name),tell,1,0
+M&essage (name),message,1,0
+-
+&Finger (name),finger,1,1
+&Vars (name),vars,1,1
+&Observe (name),observe,1,1
+&Match (name),match,1,1
+Pl&ay (name),play,1,1
+}
+/icsNames={chessclub.com /icsport=5000 /icshelper=timestamp
+freechess.org /icsport=5000 /icshelper=timeseal
+global.chessparlor.com /icsport=6000 /icshelper=timeseal
+chessanytime.com /icsport=5000
+chess.net /icsport=5000
+zics.org /icsport=5000
+jogo.cex.org.br /icsport=5000
+ajedrez.cec.uchile.cl /icsport=5000
+fly.cc.fer.hr /icsport=7890
+freechess.nl /icsport=5000 /icshelper=timeseal
+jeu.echecs.com /icsport=5000
+chess.unix-ag.uni-kl.de /icsport=5000 /icshelper=timeseal
+chess.mds.mdh.se /icsport=5000
+}
+/firstChessProgramNames={GNUChess
+"GNUChes5 xboard"
+}
+/secondChessProgramNames={GNUChess
+"GNUChes5 xboard"
+}
+/showButtonBar=true
+/firstScoreAbs=false
+/secondScoreAbs=false
+/pgnExtendedInfo=false
+/hideThinkingFromHuman=false
+/liteBackTextureFile=""
+/darkBackTextureFile=""
+/liteBackTextureMode=1
+/darkBackTextureMode=1
+/renderPiecesWithFont=""
+/fontPieceToCharTable=""
+/fontPieceBackColorWhite=#000000
+/fontPieceForeColorWhite=#000000
+/fontPieceBackColorBlack=#000000
+/fontPieceForeColorBlack=#000000
+/fontPieceSize=80
+/overrideLineGap=1
+/adjudicateLossThreshold=0
+/delayBeforeQuit=0
+/delayAfterQuit=0
+/pgnEventHeader="Computer Chess Game"
FONT 8, "MS Sans Serif"\r
BEGIN\r
DEFPUSHBUTTON "OK",IDOK,131,47,32,14,WS_GROUP\r
-#if __GNUC__\r
- ICON icon_white,-1,3,6,20,20,0\r
-#else\r
- ICON "icon_white",-1,3,6,20,20,0\r
-#endif\r
+ ICON "icon_white",-1,3,6,20,20
LTEXT "Chessboard for Windows",400,25,15,121,8\r
LTEXT "Copyright 1991 Digital Equipment Corporation",201,6,34,\r
149,8\r
LTEXT "WinBoard 0.0.0",ABOUTBOX_Version,25,5,142,8\r
END\r
\r
-DLG_TimeControl DIALOG DISCARDABLE 6, 18, 147, 113\r
+DLG_TimeControl DIALOG DISCARDABLE 6, 18, 165, 114
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU\r
CAPTION "Time Control"\r
FONT 8, "MS Sans Serif"\r
BS_AUTORADIOBUTTON | WS_TABSTOP,6,42,107,10\r
EDITTEXT OPT_TCMoves,14,20,22,12,ES_AUTOHSCROLL | WS_GROUP\r
LTEXT "moves in",OPT_TCtext1,40,22,30,8,NOT WS_GROUP\r
- EDITTEXT OPT_TCTime,74,20,32,12,ES_AUTOHSCROLL\r
- LTEXT "minutes",OPT_TCtext2,111,22,26,8,NOT WS_GROUP\r
+ EDITTEXT OPT_TCTime,74,20,50,12,ES_AUTOHSCROLL
+ LTEXT "minutes",OPT_TCtext2,129,22,26,8,NOT WS_GROUP
EDITTEXT OPT_TCTime2,14,56,32,12,ES_AUTOHSCROLL | WS_GROUP\r
LTEXT "minutes initially,",405,51,57,73,8,NOT WS_GROUP\r
LTEXT "plus",406,19,74,15,8,NOT WS_GROUP\r
EDITTEXT OPT_TCInc,37,72,32,12,ES_AUTOHSCROLL\r
LTEXT "seconds per move",408,74,74,67,8,NOT WS_GROUP\r
- PUSHBUTTON "OK",IDOK,32,95,40,14,WS_GROUP\r
- PUSHBUTTON "Cancel",IDCANCEL,88,95,40,14\r
+ PUSHBUTTON "OK",IDOK,34,95,40,14,WS_GROUP
+ PUSHBUTTON "Cancel",IDCANCEL,90,95,40,14
END\r
\r
DLG_LoadOptions DIALOG DISCARDABLE 10, 18, 144, 55\r
STYLE WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CAPTION | WS_SYSMENU | \r
WS_THICKFRAME\r
CAPTION "ICS Interaction"\r
-#if __GNUC__\r
-CLASS WBConsole\r
-#else\r
CLASS "WBConsole"\r
-#endif\r
FONT 8, "Courier New"\r
BEGIN\r
CONTROL "",OPT_ConsoleText,"RICHEDIT",ES_MULTILINE | \r
ES_AUTOVSCROLL | ES_NOHIDESEL | ES_READONLY | ES_NUMBER | \r
WS_BORDER | WS_VSCROLL | WS_TABSTOP,0,0,335,119\r
- CONTROL "",OPT_ConsoleInput,"RICHEDIT",ES_AUTOHSCROLL | \r
- ES_AUTOVSCROLL | ES_MULTILINE | \r
- ES_NOHIDESEL | ES_NUMBER | WS_BORDER | \r
- WS_TABSTOP,0,120,335,13,WS_EX_TRANSPARENT\r
+ CONTROL "",OPT_ConsoleInput,"RICHEDIT",ES_MULTILINE |
+ ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_NOHIDESEL |
+ ES_NUMBER | WS_BORDER | WS_TABSTOP,0,120,335,13,
+ WS_EX_TRANSPARENT
END\r
\r
DLG_Analysis DIALOG DISCARDABLE 0, 0, 294, 62\r
DLG_Colorize DIALOGEX 0, 0, 183, 52\r
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU\r
CAPTION "ICS Interaction Colors"\r
-FONT 8, "MS Sans Serif"\r
+FONT 8, "MS Sans Serif", 0, 0, 0x1
BEGIN\r
PUSHBUTTON "&Choose Color...",OPT_ChooseColor,15,29,51,14,WS_GROUP\r
CONTROL "&Bold",OPT_Bold,"Button",BS_AUTOCHECKBOX | WS_GROUP | \r
END\r
\r
DLG_IndexNumber DIALOG DISCARDABLE 0, 0, 236, 18\r
-#if __GNUC__\r
-STYLE DS_3DLOOK | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS |\r
- NOT WS_POPUP | NOT WS_BORDER | NOT WS_SYSMENU\r
-#else\r
STYLE DS_3DLOOK | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS\r
-#endif\r
FONT 8, "MS Sans Serif"\r
BEGIN\r
LTEXT "Inde&x number:",IDC_STATIC,5,2,46,8\r
LTEXT "Event:",IDC_STATIC,19,9,26,9\r
END\r
\r
-DLG_GeneralOptions DIALOG DISCARDABLE 0, 0, 271, 135\r
+DLG_GeneralOptions DIALOG DISCARDABLE 0, 0, 271, 150
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU\r
CAPTION "General Options"\r
FONT 8, "MS Sans Serif"\r
WS_TABSTOP,16,104,79,10\r
CONTROL "&Highlight Dragging",OPT_HighlightDragging,"Button",\r
BS_AUTOCHECKBOX | WS_TABSTOP,16,118,79,10\r
+ CONTROL "Extended PGN Info",OPT_SaveExtPGN,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,16,132,79,10
CONTROL "Highlight Last &Move",OPT_HighlightLastMove,"Button",\r
BS_AUTOCHECKBOX | WS_TABSTOP,109,6,79,10\r
CONTROL "Periodic &Updates",OPT_PeriodicUpdates,"Button",\r
BS_AUTOCHECKBOX | WS_TABSTOP,109,104,79,10\r
CONTROL "Test &Legality",OPT_TestLegality,"Button",\r
BS_AUTOCHECKBOX | WS_TABSTOP,109,118,79,10\r
+ CONTROL "Hide Thinking From Human",OPT_HideThinkFromHuman,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,109,132,102,10
END\r
\r
DLG_IcsOptions DIALOGEX 0, 0, 318, 271\r
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU\r
CAPTION "ICS Options"\r
-FONT 8, "MS Sans Serif"\r
+FONT 8, "MS Sans Serif", 0, 0, 0x1
BEGIN\r
DEFPUSHBUTTON "OK",IDOK,88,250,60,15\r
PUSHBUTTON "Cancel",IDCANCEL,168,250,60,15\r
LEFTMARGIN, 7\r
RIGHTMARGIN, 264\r
TOPMARGIN, 7\r
- BOTTOMMARGIN, 128\r
+ BOTTOMMARGIN, 143
END\r
\r
DLG_IcsOptions, DIALOG\r
MENUITEM SEPARATOR\r
MENUITEM "Stop &Observing\tF10", IDM_StopObserving\r
MENUITEM "Stop E&xamining\tF11", IDM_StopExamining\r
+ MENUITEM SEPARATOR
+ MENUITEM "Adjudicate to White", IDM_UserAdjudication_White
+ MENUITEM "Adjudicate to Black", IDM_UserAdjudication_Black
+ MENUITEM "Adjudicate Draw", IDM_UserAdjudication_Draw
END\r
POPUP "&Step"\r
BEGIN\r
"1", IDM_DirectCommand1, VIRTKEY, ALT, NOINVERT\r
"2", IDM_DirectCommand2, VIRTKEY, ALT, NOINVERT\r
"C", IDM_CopyGame, VIRTKEY, ALT, NOINVERT\r
- "C", IDM_CopyPosition, VIRTKEY, SHIFT, ALT, \r
- NOINVERT\r
+ "C", IDM_CopyPosition, VIRTKEY, SHIFT, ALT, NOINVERT
+ "V", IDM_Paste, VIRTKEY, CONTROL, NOINVERT
"V", IDM_PasteGame, VIRTKEY, ALT, NOINVERT\r
- "V", IDM_PastePosition, VIRTKEY, SHIFT, ALT, \r
- NOINVERT\r
+ "V", IDM_PastePosition, VIRTKEY, SHIFT, ALT, NOINVERT
VK_DELETE, IDM_RetractMove, VIRTKEY, ALT, NOINVERT\r
VK_DOWN, IDM_ToEnd, VIRTKEY, ALT, NOINVERT\r
VK_END, IDM_TruncateGame, VIRTKEY, ALT, NOINVERT\r
TELL WAVE DISCARDABLE "..\\sounds\\tell.wav"\r
UNFINISHED WAVE DISCARDABLE "..\\sounds\\unfinished.wav"\r
WIN WAVE DISCARDABLE "..\\sounds\\win.wav"\r
-\r
#endif // English (U.S.) resources\r
/////////////////////////////////////////////////////////////////////////////\r
\r
/*\r
* woptions.c -- Options dialog box routines for WinBoard\r
- * $Id$\r
+ * $Id: woptions.c,v 2.1 2003/10/27 19:21:02 mann Exp $
*\r
* Copyright 2000 Free Software Foundation, Inc.\r
*\r
CHECK_BOX(OPT_ShowCoordinates, appData.showCoords);\r
CHECK_BOX(OPT_ShowThinking, appData.showThinking);\r
CHECK_BOX(OPT_TestLegality, appData.testLegality);\r
+ CHECK_BOX(OPT_HideThinkFromHuman, appData.hideThinkingFromHuman);
+ CHECK_BOX(OPT_SaveExtPGN, appData.saveExtendedInfoInPGN);
\r
#undef CHECK_BOX\r
\r
appData.showCoords = IS_CHECKED(OPT_ShowCoordinates);\r
ShowThinkingEvent( IS_CHECKED(OPT_ShowThinking));\r
appData.testLegality = IS_CHECKED(OPT_TestLegality);\r
+ appData.hideThinkingFromHuman= IS_CHECKED(OPT_HideThinkFromHuman);
+ appData.saveExtendedInfoInPGN= IS_CHECKED(OPT_SaveExtPGN);
\r
#undef IS_CHECKED\r
\r