cleaned up some more copyright notices
[xboard.git] / winboard / jaws.c
1 /*\r
2  * JAWS.c -- Code for Windows front end to XBoard to use it with JAWS\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts.\r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009 Free Software Foundation, Inc.\r
9  *\r
10  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
11  * which was written and is copyrighted by Wayne Christopher.\r
12  *\r
13  * The following terms apply to Digital Equipment Corporation's copyright\r
14  * interest in XBoard:\r
15  * ------------------------------------------------------------------------\r
16  * All Rights Reserved\r
17  *\r
18  * Permission to use, copy, modify, and distribute this software and its\r
19  * documentation for any purpose and without fee is hereby granted,\r
20  * provided that the above copyright notice appear in all copies and that\r
21  * both that copyright notice and this permission notice appear in\r
22  * supporting documentation, and that the name of Digital not be\r
23  * used in advertising or publicity pertaining to distribution of the\r
24  * software without specific, written prior permission.\r
25  *\r
26  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
27  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
28  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
29  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
30  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
31  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
32  * SOFTWARE.\r
33  * ------------------------------------------------------------------------\r
34  *\r
35  * The following terms apply to the enhanced version of XBoard\r
36  * distributed by the Free Software Foundation:\r
37  * ------------------------------------------------------------------------\r
38  *\r
39  * GNU XBoard is free software: you can redistribute it and/or modify\r
40  * it under the terms of the GNU General Public License as published by\r
41  * the Free Software Foundation, either version 3 of the License, or (at\r
42  * your option) any later version.\r
43  *\r
44  * GNU XBoard is distributed in the hope that it will be useful, but\r
45  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
46  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
47  * General Public License for more details.\r
48  *\r
49  * You should have received a copy of the GNU General Public License\r
50  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
51  *\r
52  *------------------------------------------------------------------------\r
53  ** See the file ChangeLog for a revision history.  */\r
54 \r
55 // This file collects all patches for the JAWS version, so they can all be included in winboard.c\r
56 // in one big swoop. At the bottom of this file you can read instructions for how to patch\r
57 // WinBoard to work with JAWS with the aid of this file. Note that the code in this file\r
58 // is for WinBoard 4.3 and later; for older WB versions you would have to throw out the\r
59 // piece names for all pieces from Guard to Unicorn, #define ONE as '1', AAA as 'a',\r
60 // BOARD_LEFT as 0, BOARD_RGHT and BOARD_HEIGHT as 8, and set holdingssizes to 0.\r
61 // You will need to build with jaws.rc in stead of winboard.rc.\r
62 \r
63 // from common.h, but 'extern' added to it, so the actual declaraton can remain in backend.c\r
64 \r
65 extern long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement;\r
66 \r
67 #if 0\r
68 // from moves.h, but no longer needed, as the new routines are all moved to winboard.c\r
69 \r
70 extern char* PieceToName P((ChessSquare p, int i));\r
71 extern char* SquareToChar P((int Xpos)); \r
72 extern char* SquareToNum P((int Ypos));\r
73 extern int CoordToNum P((char c));\r
74 \r
75 #endif\r
76 \r
77 // from moves.c, added WinBoard_F piece types and ranks / files\r
78 \r
79 char *squareToChar[] = { "ay", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l" };\r
80 \r
81 char *squareToNum[] = {"naught", "1", "2", "3", "4", "5", "6", "7", "8", "9" };\r
82 \r
83 char *ordinals[] = {"zeroth", "first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth", "nineth"};\r
84 \r
85 char *pieceToName[] = {\r
86                 "White Pawn", "White Knight", "White Bishop", "White Rook", "White Queen", \r
87                 "White Guard", "White Elephant", "White Arch Bishop", "White Chancellor",\r
88                 "White General", "White Man", "White Cannon", "White Night Rider",\r
89                 "White Crowned Bishop", "White Crowned Rook", "White Grass Hopper", "White Veteran",\r
90                 "White Falcon", "White Amazon", "White Snake", "White Unicorn",\r
91                 "White King",\r
92                 "Black Pawn", "Black Knight", "Black Bishop", "Black Rook", "Black Queen",\r
93                 "Black Guard", "Black Elephant", "Black Arch Bishop", "Black Chancellor",\r
94                 "Black General", "Black Man", "Black Cannon", "Black Night Rider",\r
95                 "Black Crowned Bishop", "Black Crowned Rook", "Black Grass Hopper", "Black Veteran",\r
96                 "Black Falcon", "Black Amazon", "Black Snake", "Black Unicorn",\r
97                 "Black King",\r
98                 "Empty"\r
99         };\r
100 \r
101 int CoordToNum(c)\r
102                 char c;\r
103 {\r
104         if(isdigit(c)) return c - ONE;\r
105         if(c >= 'a') return c - AAA;\r
106         return 0;\r
107 }\r
108 \r
109 char* PieceToName(p, i)\r
110         ChessSquare p;\r
111         int i;\r
112 {\r
113         if(i) return pieceToName[(int) p];\r
114                 return pieceToName[(int) p]+6;\r
115 }\r
116 \r
117 char* SquareToChar(x)\r
118                         int x;\r
119 {\r
120                 return squareToChar[x - BOARD_LEFT];\r
121 }\r
122 \r
123 char* SquareToNum(y)\r
124                         int y;\r
125 {\r
126                 return squareToNum[y + (gameInfo.boardHeight < 10)];\r
127 }\r
128 \r
129 \r
130 // from winboard.c: all new routines\r
131 \r
132 #include "jfwapi.h"\r
133 #include "jaws.h"\r
134 \r
135 typedef JFWAPI BOOL (WINAPI *PSAYSTRING)(LPCTSTR lpszStrinToSpeak, BOOL bInterrupt);\r
136 \r
137 PSAYSTRING RealSayString;\r
138 \r
139 VOID SayString(char *mess, BOOL flag)\r
140 { // for debug file\r
141         char buf[MSG_SIZ], *p;\r
142         if(appData.debugMode) fprintf(debugFP, "SAY '%s'\n", mess);\r
143         strcpy(buf, mess);\r
144         if(p = StrCaseStr(buf, "Xboard adjudication:")) {\r
145                 int i;\r
146                 for(i=19; i>1; i--) p[i] = p[i-1];\r
147                 p[1] = ' ';\r
148         }\r
149         RealSayString(buf, flag);\r
150 }\r
151 \r
152 //static int fromX = 0, fromY = 0;\r
153 static int oldFromX, oldFromY;\r
154 static int timeflag;\r
155 static int suppressClocks = 0;\r
156 static int suppressOneKey = 0;\r
157 \r
158 BOOL\r
159 InitJAWS()\r
160 {       // to be called at beginning of WinMain, after InitApplication and InitInstance\r
161         HINSTANCE hApi = LoadLibrary("jfwapi32.dll");\r
162         if(!hApi) {\r
163                 DisplayInformation("Missing jfwapi32.dll");        \r
164                 return (FALSE);\r
165         }\r
166 \r
167         RealSayString = (PSAYSTRING)GetProcAddress(hApi, "JFWSayString");\r
168         if(!RealSayString) {\r
169                 DisplayInformation("SayString returned a null pointer");\r
170                 return (FALSE);\r
171         }\r
172 \r
173         {\r
174                 // [HGM] kludge to reduce need for modification of winboard.c: mak tinyLayout menu identical\r
175                 // to standard layout, so that code for switching between them does not have to be deleted\r
176                 HMENU hmenu = GetMenu(hwndMain);\r
177                 int i;\r
178 \r
179                 menuBarText[0][5] = "&JAWS";\r
180                 for(i=0; i<7; i++) menuBarText[1][i] = menuBarText[0][i];\r
181                 for (i=0; menuBarText[tinyLayout][i]; i++) {\r
182                         ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
183                                         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
184                 }\r
185                 DrawMenuBar(hwndMain);\r
186         }\r
187 \r
188         /* initialize cursor position */\r
189         fromX = fromY = 0;\r
190         SetHighlights(fromX, fromY, -1, -1);\r
191         DrawPosition(FALSE, NULL);\r
192         oldFromX = oldFromY = -1;\r
193 \r
194         if(hwndConsole) SetFocus(hwndConsole);\r
195         return TRUE;\r
196 }\r
197 \r
198 VOID\r
199 KeyboardEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
200 {\r
201         ChessSquare currentPiece;\r
202         char *piece, *xchar, *ynum ;\r
203         int n;\r
204 \r
205         if(fromX == -1 || fromY == -1) {\r
206                 fromX = BOARD_LEFT; fromY = 0;\r
207         }\r
208         switch(wParam) {\r
209         case VK_LEFT:\r
210                 if(fromX == BOARD_RGHT+1) fromX -= 2; else\r
211                 if(fromX == BOARD_LEFT) { if(fromY >= BOARD_HEIGHT - gameInfo.holdingsSize) fromX -= 2; } else\r
212                 if(fromX > BOARD_LEFT) fromX--;\r
213                 break;\r
214         case VK_RIGHT:\r
215                 if(fromX == BOARD_LEFT-2) fromX += 2; else\r
216                 if(fromX == BOARD_RGHT-1) { if(fromY < gameInfo.holdingsSize) fromX += 2; } else\r
217                 if(fromX < BOARD_RGHT-1) fromX++;\r
218                 break;\r
219         case VK_UP:\r
220                 if(fromX == BOARD_RGHT+1) { if(fromY < gameInfo.holdingsSize - 1) fromY++; } else\r
221                 if(fromY < BOARD_HEIGHT-1) fromY++;\r
222                 break;\r
223         case VK_DOWN:\r
224                 if(fromX == BOARD_LEFT-2) { if(fromY > BOARD_HEIGHT - gameInfo.holdingsSize) fromY--; } else\r
225                 if(fromY > 0) fromY--;\r
226                 break;\r
227         }\r
228         SetHighlights(fromX, fromY, -1, -1);\r
229         DrawPosition(FALSE, NULL);\r
230         currentPiece = boards[currentMove][fromY][fromX];\r
231         piece = PieceToName(currentPiece,1);\r
232         if(currentPiece != EmptySquare) MessageBeep(currentPiece < (int)BlackPawn ? MB_OK : MB_ICONEXCLAMATION);\r
233         if(fromX == BOARD_LEFT - 2) {\r
234                 SayString("black holdings", FALSE);\r
235                 if(currentPiece != EmptySquare) {\r
236                         char buf[MSG_SIZ];\r
237                         n = boards[currentMove][fromY][1];\r
238                         sprintf(buf, "%d %s%s", n, piece+6, n == 1 ? "" : "s");\r
239                         SayString(buf, TRUE);\r
240                 }\r
241         } else\r
242         if(fromX == BOARD_RGHT + 1) {\r
243                 SayString("white holdings", FALSE);\r
244                 if(currentPiece != EmptySquare) {\r
245                         char buf[MSG_SIZ];\r
246                         n = boards[currentMove][fromY][BOARD_WIDTH-2];\r
247                         sprintf(buf, "%d %s%s", n, piece+6, n == 1 ? "" : "s");\r
248                         SayString(buf, TRUE);\r
249                 }\r
250         } else\r
251         if(fromX >= BOARD_LEFT && fromX < BOARD_RGHT) {\r
252                 char buf[MSG_SIZ];\r
253                 xchar = SquareToChar(fromX);\r
254                 ynum = SquareToNum(fromY);\r
255                 if(currentPiece != EmptySquare) {\r
256 //                      SayString(piece[0] == 'W' ? "white" : "black", TRUE);\r
257                         sprintf(buf, "%s %s %s", piece, xchar, ynum);\r
258                 } else sprintf(buf, "%s %s", xchar, ynum);\r
259                 SayString(buf, TRUE);\r
260         }\r
261         return;\r
262 }\r
263 \r
264 extern char castlingRights[MAX_MOVES][BOARD_SIZE];\r
265 int PosFlags(int nr);\r
266 \r
267 typedef struct {\r
268     int rf, ff, rt, ft;\r
269     int onlyCaptures;\r
270     int count;\r
271 } ReadClosure;\r
272 \r
273 extern void ReadCallback P((Board board, int flags, ChessMove kind,\r
274                                 int rf, int ff, int rt, int ft,\r
275                                 VOIDSTAR closure));\r
276 \r
277 void ReadCallback(board, flags, kind, rf, ff, rt, ft, closure)\r
278      Board board;\r
279      int flags;\r
280      ChessMove kind;\r
281      int rf, ff, rt, ft;\r
282      VOIDSTAR closure;\r
283 {\r
284     register ReadClosure *cl = (ReadClosure *) closure;\r
285     ChessSquare possiblepiece;\r
286     char *piece, *xchar, *ynum ;\r
287 \r
288 //if(appData.debugMode) fprintf(debugFP, "%c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);\r
289     if(cl->ff == ff && cl->rf == rf) {\r
290         possiblepiece = board[rt][ft];\r
291         if(possiblepiece != EmptySquare) {\r
292                 piece = PieceToName(possiblepiece,1);\r
293                 xchar = SquareToChar(ft);\r
294                 ynum  = SquareToNum(rt);\r
295                 SayString(xchar , FALSE);\r
296                 SayString(ynum, FALSE);\r
297                 SayString(piece, FALSE);\r
298                 cl->count++;\r
299         }\r
300     }\r
301     if(cl->ft == ft && cl->rt == rt) {\r
302         possiblepiece = board[rf][ff];\r
303                 piece = PieceToName(possiblepiece,1);\r
304                 xchar = SquareToChar(ff);\r
305                 ynum  = SquareToNum(rf);\r
306                 SayString(xchar , FALSE);\r
307                 SayString(ynum, FALSE);\r
308                 SayString(piece, FALSE);\r
309                 cl->count++;\r
310     }\r
311 }\r
312 \r
313 VOID\r
314 PossibleAttackMove()\r
315 {\r
316         ReadClosure cl;\r
317         ChessSquare piece, victim;\r
318         int removedSelectedPiece = 0, swapColor;\r
319 \r
320 //if(appData.debugMode) fprintf(debugFP, "PossibleAttackMove %d %d %d %d\n", fromX, fromY, oldFromX, oldFromY);\r
321         if(fromY < 0 || fromY >= BOARD_HEIGHT) return;\r
322         if(fromX < BOARD_LEFT || fromX >= BOARD_RGHT) { SayString("holdings",FALSE); return; }\r
323 \r
324         piece = boards[currentMove][fromY][fromX];\r
325         if(piece == EmptySquare) { // if square is empty, try to substitute selected piece\r
326             if(oldFromX >= 0 && oldFromY >= 0) {\r
327                 piece = boards[currentMove][oldFromY][oldFromX];\r
328                 boards[currentMove][oldFromY][oldFromX] = EmptySquare;\r
329                 removedSelectedPiece = 1;\r
330                 SayString("Your", FALSE);\r
331                 SayString(PieceToName(piece, 0), FALSE);\r
332                 SayString("would have", FALSE);\r
333             } else { SayString("You must select a piece first", FALSE); return; }\r
334         }\r
335 \r
336         victim = boards[currentMove][fromY][fromX];\r
337         boards[currentMove][fromY][fromX] = piece; // make sure piece is actally there\r
338         SayString("possible captures from here are", FALSE);\r
339 \r
340         swapColor = piece <  (int)BlackPawn && !WhiteOnMove(currentMove) ||\r
341                     piece >= (int)BlackPawn &&  WhiteOnMove(currentMove);\r
342         cl.count = 0; cl.rf = fromY; cl.ff = fromX; cl.rt = cl.ft = -1;\r
343         GenLegal(boards[currentMove], PosFlags(currentMove + swapColor), EP_NONE, \r
344                                                 castlingRights[currentMove], ReadCallback, (VOIDSTAR) &cl);\r
345         if(cl.count == 0) SayString("None", FALSE);\r
346         boards[currentMove][fromY][fromX] = victim; // repair\r
347 \r
348         if( removedSelectedPiece ) boards[currentMove][oldFromY][oldFromX] = piece;\r
349 }\r
350 \r
351 \r
352 VOID\r
353 PossibleAttacked()\r
354 {\r
355         ReadClosure cl;\r
356         ChessSquare piece = EmptySquare, victim;\r
357 \r
358         if(fromY < 0 || fromY >= BOARD_HEIGHT) return;\r
359         if(fromX < BOARD_LEFT || fromX >= BOARD_RGHT) { SayString("holdings",FALSE); return; }\r
360 \r
361         if(oldFromX >= 0 && oldFromY >= 0) { // if piece is selected, remove it\r
362                 piece = boards[currentMove][oldFromY][oldFromX];\r
363                 boards[currentMove][oldFromY][oldFromX] = EmptySquare;\r
364         }\r
365 \r
366         SayString("Pieces that can capture you are", FALSE);\r
367 \r
368         victim = boards[currentMove][fromY][fromX]; // put dummy piece on target square, to activate Pawn captures\r
369         boards[currentMove][fromY][fromX] = WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen;\r
370         cl.count = 0; cl.rt = fromY; cl.ft = fromX; cl.rf = cl.ff = -1;\r
371         GenLegal(boards[currentMove], PosFlags(currentMove+1), EP_NONE, \r
372                                                 castlingRights[currentMove], ReadCallback, (VOIDSTAR) &cl);\r
373         if(cl.count == 0) SayString("None", FALSE);\r
374 \r
375         SayString("You are defended by", FALSE);\r
376 \r
377         boards[currentMove][fromY][fromX] = WhiteOnMove(currentMove) ? BlackQueen : WhiteQueen;\r
378         cl.count = 0; cl.rt = fromY; cl.ft = fromX; cl.rf = cl.ff = -1;\r
379         GenLegal(boards[currentMove], PosFlags(currentMove), EP_NONE, \r
380                                                 castlingRights[currentMove], ReadCallback, (VOIDSTAR) &cl);\r
381         if(cl.count == 0) SayString("None", FALSE);\r
382         boards[currentMove][fromY][fromX] = victim; // put back original occupant\r
383 \r
384         if(oldFromX >= 0 && oldFromY >= 0) { // put back possibl selected piece\r
385                 boards[currentMove][oldFromY][oldFromX] = piece;\r
386         }\r
387 }\r
388 \r
389 VOID\r
390 ReadRow()\r
391 {\r
392         ChessSquare currentpiece; \r
393         char *piece, *xchar, *ynum ;\r
394         int xPos, count=0;\r
395         ynum = SquareToNum(fromY);\r
396         \r
397         if(fromY < 0) return;\r
398 \r
399         for (xPos=BOARD_LEFT; xPos<BOARD_RGHT; xPos++) {\r
400                 currentpiece = boards[currentMove][fromY][xPos];        \r
401                 if(currentpiece != EmptySquare) {\r
402                         piece = PieceToName(currentpiece,1);\r
403                         xchar = SquareToChar(xPos);\r
404                         SayString(xchar , FALSE);\r
405                         SayString(ynum, FALSE);\r
406                         SayString(piece, FALSE);\r
407                         count++;\r
408                 }\r
409         }\r
410         if(count == 0) {\r
411                 SayString("rank", FALSE);\r
412                 SayString(ynum, FALSE);\r
413                 SayString("empty", FALSE);\r
414         }\r
415 }\r
416 \r
417 VOID\r
418 ReadColumn()\r
419 {\r
420         ChessSquare currentpiece; \r
421         char *piece, *xchar, *ynum ;\r
422         int yPos, count=0;\r
423         xchar = SquareToChar(fromX);\r
424         \r
425         if(fromX < 0) return;\r
426 \r
427         for (yPos=0; yPos<BOARD_HEIGHT; yPos++) {\r
428                 currentpiece = boards[currentMove][yPos][fromX];        \r
429                 if(currentpiece != EmptySquare) {\r
430                         piece = PieceToName(currentpiece,1);\r
431                         ynum = SquareToNum(yPos);\r
432                         SayString(xchar , FALSE);\r
433                         SayString(ynum, FALSE);\r
434                         SayString(piece, FALSE);\r
435                         count++;\r
436                 }\r
437         }\r
438         if(count == 0) {\r
439                 SayString(xchar, FALSE);\r
440                 SayString("file empty", FALSE);\r
441         }\r
442 }\r
443 \r
444 VOID\r
445 SayUpperDiagnols()\r
446 {\r
447         ChessSquare currentpiece; \r
448         char *piece, *xchar, *ynum ;\r
449         int yPos, xPos;\r
450         \r
451         if(fromX < 0 || fromY < 0) return;\r
452 \r
453         if(fromX < BOARD_RGHT-1 && fromY < BOARD_HEIGHT-1) {\r
454                 SayString("The diagnol squares to your upper right contain", FALSE);\r
455                 yPos = fromY+1;\r
456                 xPos = fromX+1;\r
457                 while(yPos<BOARD_HEIGHT && xPos<BOARD_RGHT) {\r
458                         currentpiece = boards[currentMove][yPos][xPos]; \r
459                         piece = PieceToName(currentpiece,1);\r
460                         xchar = SquareToChar(xPos);\r
461                         ynum = SquareToNum(yPos);\r
462                         SayString(xchar , FALSE);\r
463                         SayString(ynum, FALSE);\r
464                         SayString(piece, FALSE);\r
465                         yPos++;\r
466                         xPos++;\r
467                 }\r
468         }\r
469         else SayString("There is no squares to your upper right", FALSE);\r
470 \r
471         if(fromX > BOARD_LEFT && fromY < BOARD_HEIGHT-1) {\r
472                 SayString("The diagnol squares to your upper left contain", FALSE);\r
473                 yPos = fromY+1;\r
474                 xPos = fromX-1;\r
475                 while(yPos<BOARD_HEIGHT && xPos>=BOARD_LEFT) {\r
476                         currentpiece = boards[currentMove][yPos][xPos]; \r
477                         piece = PieceToName(currentpiece,1);\r
478                         xchar = SquareToChar(xPos);\r
479                         ynum = SquareToNum(yPos);\r
480                         SayString(xchar , FALSE);\r
481                         SayString(ynum, FALSE);\r
482                         SayString(piece, FALSE);\r
483                         yPos++;\r
484                         xPos--;\r
485                 }\r
486         }\r
487         else SayString("There is no squares to your upper left", FALSE);\r
488 }\r
489 \r
490 VOID\r
491 SayLowerDiagnols()\r
492 {\r
493         ChessSquare currentpiece; \r
494         char *piece, *xchar, *ynum ;\r
495         int yPos, xPos;\r
496         \r
497         if(fromX < 0 || fromY < 0) return;\r
498 \r
499         if(fromX < BOARD_RGHT-1 && fromY > 0) {\r
500                 SayString("The diagnol squares to your lower right contain", FALSE);\r
501                 yPos = fromY-1;\r
502                 xPos = fromX+1;\r
503                 while(yPos>=0 && xPos<BOARD_RGHT) {\r
504                         currentpiece = boards[currentMove][yPos][xPos]; \r
505                         piece = PieceToName(currentpiece,1);\r
506                         xchar = SquareToChar(xPos);\r
507                         ynum = SquareToNum(yPos);\r
508                         SayString(xchar , FALSE);\r
509                         SayString(ynum, FALSE);\r
510                         SayString(piece, FALSE);\r
511                         yPos--;\r
512                         xPos++;\r
513                 }\r
514         }\r
515         else SayString("There is no squares to your lower right", FALSE);\r
516 \r
517         if(fromX > BOARD_LEFT && fromY > 0) {\r
518                 SayString("The diagnol squares to your lower left contain", FALSE);\r
519                 yPos = fromY-1;\r
520                 xPos = fromX-1;\r
521                 while(yPos>=0 && xPos>=BOARD_LEFT) {\r
522                         currentpiece = boards[currentMove][yPos][xPos]; \r
523                         piece = PieceToName(currentpiece,1);\r
524                         xchar = SquareToChar(xPos);\r
525                         ynum = SquareToNum(yPos);\r
526                         SayString(xchar , FALSE);\r
527                         SayString(ynum, FALSE);\r
528                         SayString(piece, FALSE);\r
529                         yPos--;\r
530                         xPos--;\r
531                 }\r
532         }\r
533         else SayString("There is no squares to your lower left", FALSE);\r
534 }\r
535 \r
536 VOID\r
537 SayKnightMoves()\r
538 {\r
539         ChessSquare currentpiece, oldpiece; \r
540         char *piece, *xchar, *ynum ;\r
541 \r
542         oldpiece = boards[currentMove][fromY][fromX];\r
543         if(oldpiece == WhiteKnight || oldpiece == BlackKnight) \r
544                 SayString("The possible squares a Knight could move to are", FALSE);\r
545         else\r
546                 SayString("The squares a Knight could possibly attack from are", FALSE);\r
547 \r
548         if (fromY+2 < BOARD_HEIGHT && fromX-1 >= BOARD_LEFT) {\r
549                 currentpiece = boards[currentMove][fromY+2][fromX-1];\r
550                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
551                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
552                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
553                 {\r
554                         piece = PieceToName(currentpiece,1);\r
555                         xchar = SquareToChar(fromX-1);\r
556                         ynum = SquareToNum(fromY+2);\r
557                         SayString(xchar , FALSE);\r
558                         SayString(ynum, FALSE);\r
559                         SayString(piece, FALSE);\r
560                 }\r
561         }\r
562 \r
563         if (fromY+2 < BOARD_HEIGHT && fromX+1 < BOARD_RGHT) {\r
564                 currentpiece = boards[currentMove][fromY+2][fromX+1];\r
565                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
566                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
567                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
568                 {\r
569                         piece = PieceToName(currentpiece,1);\r
570                         xchar = SquareToChar(fromX+1);\r
571                         ynum = SquareToNum(fromY+2);\r
572                         SayString(xchar , FALSE);\r
573                         SayString(ynum, FALSE);\r
574                         SayString(piece, FALSE);\r
575                 }\r
576         }\r
577         \r
578         if (fromY+1 < BOARD_HEIGHT && fromX+2 < BOARD_RGHT) {\r
579                 currentpiece = boards[currentMove][fromY+1][fromX+2];\r
580                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
581                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
582                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
583                 {\r
584                         piece = PieceToName(currentpiece,1);\r
585                         xchar = SquareToChar(fromX+2);\r
586                         ynum = SquareToNum(fromY+1);\r
587                         SayString(xchar , FALSE);\r
588                         SayString(ynum, FALSE);\r
589                         SayString(piece, FALSE);\r
590                 }\r
591         }\r
592         \r
593         if (fromY-1 >= 0 && fromX+2 < BOARD_RGHT) {\r
594                 currentpiece = boards[currentMove][fromY-1][fromX+2];\r
595                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
596                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
597                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
598                 {\r
599                         piece = PieceToName(currentpiece,1);\r
600                         xchar = SquareToChar(fromX+2);\r
601                         ynum = SquareToNum(fromY-1);\r
602                         SayString(xchar , FALSE);\r
603                         SayString(ynum, FALSE);\r
604                         SayString(piece, FALSE);\r
605                 }\r
606         }\r
607         \r
608         if (fromY-2 >= 0 && fromX+1 < BOARD_RGHT) {\r
609                 currentpiece = boards[currentMove][fromY-2][fromX+1];\r
610                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
611                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
612                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
613                 {\r
614                         piece = PieceToName(currentpiece,1);\r
615                         xchar = SquareToChar(fromX+1);\r
616                         ynum = SquareToNum(fromY-2);\r
617                         SayString(xchar , FALSE);\r
618                         SayString(ynum, FALSE);\r
619                         SayString(piece, FALSE);\r
620                 }\r
621         }\r
622         \r
623         if (fromY-2 >= 0 && fromX-1 >= BOARD_LEFT) {\r
624                 currentpiece = boards[currentMove][fromY-2][fromX-1];\r
625                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
626                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
627                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
628                 {\r
629                         piece = PieceToName(currentpiece,1);\r
630                         xchar = SquareToChar(fromX-1);\r
631                         ynum = SquareToNum(fromY-2);\r
632                         SayString(xchar , FALSE);\r
633                         SayString(ynum, FALSE);\r
634                         SayString(piece, FALSE);\r
635                 }\r
636         }\r
637         \r
638         if (fromY-1 >= 0 && fromX-2 >= BOARD_LEFT) {\r
639                 currentpiece = boards[currentMove][fromY-1][fromX-2];\r
640                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
641                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
642                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
643                 {\r
644                         piece = PieceToName(currentpiece,1);\r
645                         xchar = SquareToChar(fromX-2);\r
646                         ynum = SquareToNum(fromY-1);\r
647                         SayString(xchar , FALSE);\r
648                         SayString(ynum, FALSE);\r
649                         SayString(piece, FALSE);\r
650                 }\r
651         }\r
652         \r
653         if (fromY+1 < BOARD_HEIGHT && fromX-2 >= BOARD_LEFT) {\r
654                 currentpiece = boards[currentMove][fromY+1][fromX-2];\r
655                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
656                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
657                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
658                 {\r
659                         piece = PieceToName(currentpiece,1);\r
660                         xchar = SquareToChar(fromX-2);\r
661                         ynum = SquareToNum(fromY+1);\r
662                         SayString(xchar , FALSE);\r
663                         SayString(ynum, FALSE);\r
664                         SayString(piece, FALSE);\r
665                 }\r
666         }\r
667 }\r
668 \r
669 VOID\r
670 SayPieces(ChessSquare p)\r
671 {\r
672         ChessSquare currentpiece;  \r
673         char *piece, *xchar, *ynum ;\r
674         int yPos, xPos, count = 0;\r
675         char buf[50];\r
676 \r
677         if(p == WhitePlay)   SayString("White pieces", FALSE); else\r
678         if(p == BlackPlay)   SayString("Black pieces", FALSE); else\r
679         if(p == EmptySquare) SayString("Pieces", FALSE); else {\r
680                 sprintf(buf, "%ss", PieceToName(p,1));\r
681                 SayString(buf, FALSE);\r
682         }\r
683         SayString("are located", FALSE);\r
684         for(yPos=0; yPos<BOARD_HEIGHT; yPos++) {\r
685                 for(xPos=BOARD_LEFT; xPos<BOARD_RGHT; xPos++) {\r
686                         currentpiece = boards[currentMove][yPos][xPos]; \r
687                         if(p == BlackPlay && currentpiece >= BlackPawn && currentpiece <= BlackKing ||\r
688                            p == WhitePlay && currentpiece >= WhitePawn && currentpiece <= WhiteKing   )\r
689                                 piece = PieceToName(currentpiece,0);\r
690                         else if(p == EmptySquare && currentpiece != EmptySquare)\r
691                                 piece = PieceToName(currentpiece,1);\r
692                         else if(p == currentpiece)\r
693                                 piece = NULL;\r
694                         else continue;\r
695                                 \r
696                                 if(count == 0) SayString("at", FALSE);\r
697                                 xchar = SquareToChar(xPos);\r
698                                 ynum = SquareToNum(yPos);\r
699                                 SayString(xchar , FALSE);\r
700                                 SayString(ynum, FALSE);\r
701                                 if(piece) SayString(piece, FALSE);\r
702                                 count++;\r
703                 }\r
704         }\r
705         if(count == 0) SayString("nowhere", FALSE);\r
706 }\r
707 \r
708 VOID\r
709 SayCurrentPos()\r
710 {\r
711         ChessSquare currentpiece;\r
712         char *piece, *xchar, *ynum ;\r
713         if(fromX <  BOARD_LEFT) { SayString("You strayed into the white holdings", FALSE); return; }\r
714         if(fromX >= BOARD_RGHT) { SayString("You strayed into the black holdings", FALSE); return; }\r
715         currentpiece = boards[currentMove][fromY][fromX];       \r
716         piece = PieceToName(currentpiece,1);\r
717         ynum = SquareToNum(fromY);\r
718         xchar = SquareToChar(fromX);\r
719         SayString("Your current position is", FALSE);\r
720         SayString(xchar, FALSE);\r
721         SayString(ynum, FALSE);\r
722         SayString(piece, FALSE);\r
723         if((fromX-BOARD_LEFT) ^ fromY)\r
724                 SayString("on a dark square",FALSE);\r
725         else \r
726                 SayString("on a light square",FALSE);\r
727 \r
728         PossibleAttacked();\r
729         return;\r
730 }\r
731 \r
732 VOID\r
733 SayAllBoard()\r
734 {\r
735         int Xpos, Ypos;\r
736         ChessSquare currentpiece;\r
737         char *piece, *xchar, *ynum ;\r
738         \r
739         if(gameInfo.holdingsWidth) {\r
740                 int first = 0;\r
741                 for(Ypos=0; Ypos<gameInfo.holdingsSize; Ypos++) {\r
742                         int n = boards[currentMove][Ypos][BOARD_WIDTH-2];\r
743                         if(n) {  char buf[MSG_SIZ];\r
744                                 if(!first++) SayString("white holds", FALSE);\r
745                                 currentpiece = boards[currentMove][Ypos][BOARD_WIDTH-1];        \r
746                                 piece = PieceToName(currentpiece,0);\r
747                                 sprintf(buf, "%d %s%s", n, piece, (n==1 ? "" : "s") );\r
748                                 SayString(buf, FALSE);\r
749                         }\r
750                 }\r
751                 first = 0;\r
752                 for(Ypos=BOARD_HEIGHT-1; Ypos>=BOARD_HEIGHT - gameInfo.holdingsSize; Ypos--) {\r
753                         int n = boards[currentMove][Ypos][1];\r
754                         if(n) {  char buf[MSG_SIZ];\r
755                                 if(!first++) SayString("black holds", FALSE);\r
756                                 currentpiece = boards[currentMove][Ypos][0];    \r
757                                 piece = PieceToName(currentpiece,0);\r
758                                 sprintf(buf, "%d %s%s", n, piece, (n==1 ? "" : "s") );\r
759                                 SayString(buf, FALSE);\r
760                         }\r
761                 }\r
762         }\r
763 \r
764         for(Ypos=BOARD_HEIGHT-1; Ypos>=0; Ypos--) {\r
765                 ynum = ordinals[Ypos + (gameInfo.boardHeight < 10)];\r
766                 SayString(ynum, FALSE);\r
767                 SayString("rank", FALSE);\r
768                 for(Xpos=BOARD_LEFT; Xpos<BOARD_RGHT; Xpos++) {\r
769                         currentpiece = boards[currentMove][Ypos][Xpos]; \r
770                         if(currentpiece != EmptySquare) {\r
771                                 int count = 0, oldX = Xpos;\r
772                                 char buf[50];\r
773                                 piece = PieceToName(currentpiece,1);\r
774                                 while(Xpos < BOARD_RGHT && boards[currentMove][Ypos][Xpos] == currentpiece)\r
775                                         Xpos++, count++;\r
776                                 if(count > 1) { \r
777                                         sprintf(buf, "%d %ss", count, piece);\r
778                                 } else  sprintf(buf, "%s", piece);\r
779                                 Xpos--;\r
780                                 SayString(buf, FALSE);\r
781                         } else {\r
782                                 int count = 0, oldX = Xpos;\r
783                                 while(Xpos < BOARD_RGHT && boards[currentMove][Ypos][Xpos] == EmptySquare)\r
784                                         Xpos++, count++;\r
785                                 if(Xpos == BOARD_RGHT && oldX == BOARD_LEFT)\r
786                                         SayString("all", FALSE);\r
787                                 else{\r
788                                     if(count > 1) { \r
789                                         char buf[10];\r
790                                         sprintf(buf, "%d", count);\r
791                                         SayString(buf, FALSE);\r
792                                     }\r
793                                     Xpos--;\r
794                                 }\r
795                                 SayString("empty", FALSE);\r
796                         }\r
797                 }\r
798         }\r
799         \r
800 }\r
801 \r
802 VOID\r
803 SayWhosTurn()\r
804 {\r
805         if(gameMode == MachinePlaysBlack || gameMode == IcsPlayingBlack) {\r
806                 if(WhiteOnMove(currentMove))\r
807                         SayString("It is your turn", FALSE);\r
808                 else    SayString("It is your opponents turn", FALSE);\r
809         } else if(gameMode == MachinePlaysWhite || gameMode == IcsPlayingWhite) {\r
810                 if(WhiteOnMove(currentMove))\r
811                         SayString("It is your opponents turn", FALSE);\r
812                 else    SayString("It is your turn", FALSE);\r
813         } else {\r
814                 if(WhiteOnMove(currentMove)) \r
815                         SayString("White is on move here", FALSE);\r
816                 else    SayString("Black is on move here", FALSE);\r
817         }\r
818 }\r
819         \r
820 \r
821 VOID\r
822 SayMachineMove(int evenIfDuplicate)\r
823 {\r
824         int len, xPos, yPos, moveNr, secondSpace = 0, castle = 0, n;\r
825         ChessSquare currentpiece;\r
826         char *piece, *xchar, *ynum, *p;\r
827         char c, buf[MSG_SIZ], comment[MSG_SIZ];\r
828         static char disambiguation[2];\r
829         static int previousMove = 0;\r
830 \r
831         if(appData.debugMode) fprintf(debugFP, "Message = '%s'\n", messageText);\r
832         if(gameMode == BeginningOfGame) return;\r
833         if(messageText[0] == '[') return;\r
834         comment[0]= 0;\r
835             if(isdigit(messageText[0])) { // message is move, possibly with thinking output\r
836                 int dotCount = 0, spaceCount = 0;\r
837                 sscanf(messageText, "%d", &moveNr);\r
838                 len = 0;\r
839                 // [HGM] show: better extraction of move\r
840                 while (messageText[len] != NULLCHAR) {\r
841                     if(messageText[len] == '.' && spaceCount == 0) dotCount++;\r
842                     if(messageText[len] == ' ') { if(++spaceCount == 2) secondSpace = len; }\r
843                     if(messageText[len] == '{') { // we detected a comment\r
844                         if(isalpha(messageText[len+1]) ) sscanf(messageText+len, "{%[^}]}", comment);\r
845                         break;\r
846                     }\r
847                     if(messageText[len] == '[') { // we detected thinking output\r
848                         int depth; float score=0; char c, lastMover = (dotCount == 3 ? 'B' : 'W');\r
849                         if(sscanf(messageText+len+1, "%d]%c%f", &depth, &c, &score) > 1) {\r
850                             if(c == ' ') { // if not explicitly specified, figure out source of thinking output\r
851                                 switch(gameMode) {\r
852                                   case MachinePlaysWhite:\r
853                                   case IcsPlayingWhite:\r
854                                     c = 'W'; break;\r
855                                   case IcsPlayingBlack:\r
856                                   case MachinePlaysBlack:\r
857                                     c = 'B'; \r
858                                   default:\r
859                                     break;\r
860                                 }\r
861                             }\r
862                             if(c != lastMover) return; // line is thinking output of future move, ignore.\r
863                             if(2*moveNr - (dotCount < 2) == previousMove)\r
864                                 return; // do not repeat same move; likely ponder output\r
865                             sprintf(buf, "score %s %d at %d ply", \r
866                                         score > 0 ? "plus" : score < 0 ? "minus" : "",\r
867                                         (int) (fabs(score)*100+0.5),\r
868                                         depth );\r
869                             SayString(buf, FALSE); // move + thinking output describing it; say it.\r
870                         }\r
871                         while(messageText[len-1] == ' ') len--; // position just behind move;\r
872                         break;\r
873                     }\r
874                     if(messageText[len] == '(') { // ICS time printed behind move\r
875                         while(messageText[len+1] && messageText[len] != ')') len++; // skip it\r
876                     }\r
877                     len++;\r
878                 }\r
879                 if(secondSpace) len = secondSpace; // position behind move\r
880                 if(messageText[len-1] == '+' || messageText[len-1] == '#') {  /* you are in checkmate */\r
881                         len--; // strip off check or mate indicator\r
882                 }\r
883                 if(messageText[len-2] == '=') {  /* promotion */\r
884                         len-=2; // strip off promotion piece\r
885                         SayString("promotion", FALSE);\r
886                 }\r
887 \r
888                 n = 2*moveNr - (dotCount < 2);\r
889 \r
890                 if(previousMove != 2*moveNr + (dotCount > 1) || evenIfDuplicate) { \r
891                     char number[20];\r
892                     previousMove = 2*moveNr + (dotCount > 1); // remember move nr of move last spoken\r
893                     sprintf(number, "%d", moveNr);\r
894 \r
895                     yPos = CoordToNum(messageText[len-1]);  /* turn char coords to ints */\r
896                     xPos = CoordToNum(messageText[len-2]);\r
897                     if(xPos < 0 || xPos > 11) return; // prevent crashes if no coord string available to speak\r
898                     if(yPos < 0 || yPos > 9)  return;\r
899                     currentpiece = boards[n][yPos][xPos];       \r
900                     piece = PieceToName(currentpiece,0);\r
901                     ynum = SquareToNum(yPos);\r
902                     xchar = SquareToChar(xPos);\r
903                     c = messageText[len-3];\r
904                     if(c == 'x') c = messageText[len-4];\r
905                     if(!isdigit(c) && c < 'a' && c != '@') c = 0;\r
906                     disambiguation[0] = c;\r
907                     SayString(WhiteOnMove(n) ? "Black" : "White", FALSE);\r
908                     SayString("move", FALSE);\r
909                     SayString(number, FALSE);\r
910 //                  if(c==0 || c=='@') SayString("a", FALSE);\r
911                     // intercept castling moves\r
912                     p = StrStr(messageText, "O-O-O");\r
913                     if(p && p-messageText < len) {\r
914                         SayString("queen side castling",FALSE);\r
915                         castle = 1;\r
916                     } else {\r
917                         p = StrStr(messageText, "O-O");\r
918                         if(p && p-messageText < len) {\r
919                             SayString("king side castling",FALSE);\r
920                             castle = 1;\r
921                         }\r
922                     }\r
923                     if(!castle) {\r
924                         SayString(piece, FALSE);\r
925                         if(c == '@') SayString("dropped on", FALSE); else\r
926                         if(c) SayString(disambiguation, FALSE);\r
927                         SayString("to", FALSE);\r
928                         SayString(xchar, FALSE);\r
929                         SayString(ynum, FALSE);\r
930                         if(messageText[len-3] == 'x') {\r
931                                 currentpiece = boards[n-1][yPos][xPos];\r
932                                 if(currentpiece != EmptySquare) {\r
933                                         piece = PieceToName(currentpiece,0);\r
934                                         SayString("Capturing a",FALSE);\r
935                                         SayString(piece, FALSE);\r
936                                 } else SayString("Capturing onn passann",FALSE);\r
937                         }\r
938                         if(messageText[len] == '+') SayString("check", FALSE); else\r
939                         if(messageText[len] == '#') {\r
940                                 SayString("finishing off", FALSE);\r
941                                 SayString(WhiteOnMove(n) ? "White" : "Black", FALSE);\r
942                         }\r
943                     }\r
944                 }\r
945 \r
946                 /* say comment after move, possibly with result */\r
947                 p = NULL;\r
948                 if(StrStr(messageText, " 1-0")) p = "white wins"; else\r
949                 if(StrStr(messageText, " 0-1")) p = "black wins"; else\r
950                 if(StrStr(messageText, " 1/2-1/2")) p = "game ends in a draw";\r
951                 if(comment[0]) {\r
952                     if(p) {\r
953                         if(!StrCaseStr(comment, "draw") && \r
954                            !StrCaseStr(comment, "white") && \r
955                            !StrCaseStr(comment, "black") ) {\r
956                                 SayString(p, FALSE);\r
957                                 SayString("due to", FALSE);\r
958                         }\r
959                     }\r
960                     SayString(comment, FALSE); // alphabetic comment (usually game end)\r
961                 } else if(p) SayString(p, FALSE);\r
962 \r
963             } else {\r
964                 /* starts not with digit */\r
965                 if(StrCaseStr(messageText, "illegal")) PlayIcsUnfinishedSound();\r
966                 SayString(messageText, FALSE);\r
967             }\r
968 \r
969 }\r
970 \r
971 VOID\r
972 SayClockTime()\r
973 {\r
974         char buf1[50], buf2[50];\r
975         char *str1, *str2;\r
976         static long int lastWhiteTime, lastBlackTime;\r
977 \r
978         suppressClocks = 1; // if user is using alt+T command, no reason to display them\r
979         if(abs(lastWhiteTime - whiteTimeRemaining) < 1000 && abs(lastBlackTime - blackTimeRemaining) < 1000)\r
980                 suppressClocks = 0; // back on after two requests in rapid succession\r
981         sprintf(buf1, "%s", TimeString(whiteTimeRemaining));\r
982         str1 = buf1;\r
983         SayString("White's remaining time is", FALSE);\r
984         SayString(str1, FALSE);\r
985         sprintf(buf2, "%s", TimeString(blackTimeRemaining));\r
986         str2 = buf2;\r
987         SayString("Black's remaining time is", FALSE);\r
988         SayString(str2, FALSE);\r
989         lastWhiteTime = whiteTimeRemaining;\r
990         lastBlackTime = blackTimeRemaining;\r
991 }\r
992 \r
993 VOID\r
994 Toggle(Boolean *b, char *mess)\r
995 {\r
996         *b = !*b;\r
997         SayString(mess, FALSE);\r
998         SayString("is now", FALSE);\r
999         SayString(*b ? "on" : "off", FALSE);\r
1000 }\r
1001 \r
1002 /* handles keyboard moves in a click-click fashion */\r
1003 VOID\r
1004 KeyboardMove(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
1005 {\r
1006         ChessSquare currentpiece;\r
1007         char *piece;\r
1008         \r
1009         static BOOLEAN sameAgain = FALSE;\r
1010         switch (message) {\r
1011         case WM_KEYDOWN:\r
1012                 sameAgain = FALSE;\r
1013                 if(oldFromX == fromX && oldFromY == fromY) {\r
1014                         sameAgain = TRUE;\r
1015                         /* click on same square */\r
1016                         break;\r
1017                 }\r
1018                 else if(oldFromX != -1) {\r
1019                         \r
1020                         ChessSquare pdown, pup;\r
1021       pdown = boards[currentMove][oldFromY][oldFromX];\r
1022       pup = boards[currentMove][fromY][fromX];\r
1023                 \r
1024                 if (gameMode == EditPosition ||\r
1025                         !((WhitePawn <= pdown && pdown <= WhiteKing &&\r
1026                                  WhitePawn <= pup && pup <= WhiteKing) ||\r
1027                                 (BlackPawn <= pdown && pdown <= BlackKing &&\r
1028                                  BlackPawn <= pup && pup <= BlackKing))) {\r
1029                         /* EditPosition, empty square, or different color piece;\r
1030                         click-click move is possible */\r
1031                 \r
1032                         if (IsPromotion(oldFromX, oldFromY, fromX, fromY)) {\r
1033                                 if (appData.alwaysPromoteToQueen) {\r
1034                                         UserMoveEvent(oldFromX, oldFromY, fromX, fromY, 'q');\r
1035                                 }\r
1036                                 else {\r
1037                                         toX = fromX; toY = fromY; fromX = oldFromX; fromY = oldFromY;\r
1038                                         PromotionPopup(hwnd);\r
1039                                         fromX = toX; fromY = toY;\r
1040                                 }       \r
1041                         }\r
1042                         else {\r
1043                                 UserMoveEvent(oldFromX, oldFromY, fromX, fromY, NULLCHAR);\r
1044                         }\r
1045                 oldFromX = oldFromY = -1;\r
1046                 break;\r
1047                 }\r
1048                 \r
1049                 }\r
1050                 /* First downclick, or restart on a square with same color piece */\r
1051                 if (OKToStartUserMove(fromX, fromY)) {\r
1052                 oldFromX = fromX;\r
1053                 oldFromY = fromY;\r
1054                 currentpiece = boards[currentMove][fromY][fromX];       \r
1055                 piece = PieceToName(currentpiece,1);\r
1056                 SayString(piece, FALSE);\r
1057                 SayString("selected", FALSE);\r
1058                 }\r
1059                 else {\r
1060                 oldFromX = oldFromY = -1;\r
1061                 }\r
1062                 break;\r
1063 \r
1064         case WM_KEYUP:\r
1065                 if (oldFromX == fromX && oldFromY == fromY) {\r
1066       /* Upclick on same square */\r
1067       if (sameAgain) {\r
1068         /* Clicked same square twice: abort click-click move */\r
1069                         oldFromX = oldFromY = -1;\r
1070                         currentpiece = boards[currentMove][fromY][fromX];       \r
1071                         piece = PieceToName(currentpiece,0);\r
1072                         SayString(piece, FALSE);\r
1073                         SayString("unselected", FALSE);\r
1074                         }\r
1075                 }\r
1076         }\r
1077 }\r
1078 \r
1079 int\r
1080 NiceTime(int x)\r
1081 {       // return TRUE for times we want to announce\r
1082         if(x<0) return 0;\r
1083         x = (x+50)/100;   // tenth of seconds\r
1084         if(x <= 100) return (x%10 == 0);\r
1085         if(x <= 600) return (x%100 == 0);\r
1086         if(x <= 6000) return (x%600 == 0);\r
1087         return (x%3000 == 0);\r
1088 }\r
1089 \r
1090 #if 0\r
1091             if(isalpha((char)wParam)) {\r
1092                 /* capitals of any ind are intercepted and distinguished by left and right shift */\r
1093                 int mine = GetKeyState(VK_RSHIFT) < 0;\r
1094                 if(mine || GetKeyState(VK_LSHIFT) < 0) {\r
1095 #endif\r
1096 \r
1097 #define JAWS_ALT_INTERCEPT \\r
1098             if(suppressOneKey) {\\r
1099                 suppressOneKey = 0;\\r
1100                 if(GetKeyState(VK_MENU) < 0 && GetKeyState(VK_CONTROL) < 0) break;\\r
1101             }\\r
1102             if ((char)wParam == 022 && gameMode == EditPosition) { /* <Ctl R>. Pop up piece menu */\\r
1103                 POINT pt; int x, y;\\r
1104                 SquareToPos(fromY, fromX, &x, &y);\\r
1105                 pt.x = x; pt.y = y;\\r
1106                 if(gameInfo.variant != VariantShogi)\\r
1107                     MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\\r
1108                 else\\r
1109                     MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\\r
1110                 break;\\r
1111             }\\r
1112 \r
1113 #define JAWS_REPLAY \\r
1114     case '\020': /* ctrl P */\\r
1115       { char buf[MSG_SIZ];\\r
1116         if(GetWindowText(hwnd, buf, MSG_SIZ-1))\\r
1117                 SayString(buf, FALSE);\\r
1118       }\\r
1119       return 0;\\r
1120 \r
1121 #define JAWS_KB_NAVIGATION \\r
1122 \\r
1123         case WM_KEYDOWN:\\r
1124 \\r
1125                 if(GetKeyState(VK_MENU) < 0 && GetKeyState(VK_CONTROL) < 0) {\\r
1126                     /* Control + Alt + letter used for speaking piece positions */\\r
1127                     static int lastTime; static char lastChar;\\r
1128                     int mine = 0, time = GetTickCount(); char c;\\r
1129 \\r
1130                     if((char)wParam == lastChar && time-lastTime < 250) mine = 1;\\r
1131                     lastChar = wParam; lastTime = time;\\r
1132                     c = wParam;\\r
1133 \\r
1134                     if(gameMode == IcsPlayingWhite || gameMode == MachinePlaysBlack) mine = !mine;\\r
1135 \\r
1136                     if(ToLower(c) == 'x') {\\r
1137                         SayPieces(mine ? WhitePlay : BlackPlay);\\r
1138                         suppressOneKey = 1;\\r
1139                         break;\\r
1140                     } else\\r
1141                     if(CharToPiece(c) != EmptySquare) {\\r
1142                         SayPieces(CharToPiece(mine ? ToUpper(c) : ToLower(c)));\\r
1143                         suppressOneKey = 1;\\r
1144                         break;\\r
1145                     }\\r
1146                 }\\r
1147 \\r
1148                 switch (wParam) {\\r
1149                 case VK_LEFT:\\r
1150                 case VK_RIGHT:\\r
1151                 case VK_UP:\\r
1152                 case VK_DOWN:\\r
1153                         KeyboardEvent(hwnd, message, wParam, lParam);\\r
1154                         break;\\r
1155                 case VK_SPACE:\\r
1156                         KeyboardMove(hwnd, message, wParam, lParam);\\r
1157                         break;\\r
1158                 }\\r
1159                 break;\\r
1160         case WM_KEYUP:\\r
1161                 switch (wParam) {\\r
1162                 case VK_SPACE:\\r
1163                         KeyboardMove(hwnd, message, wParam, lParam);\\r
1164                         break;\\r
1165                 }\\r
1166                 break;\\r
1167 \r
1168 #define JAWS_MENU_ITEMS \\r
1169                 case IDM_PossibleAttackMove:  /*What can I possible attack from here */\\r
1170                         PossibleAttackMove();\\r
1171                         break;\\r
1172 \\r
1173                 case IDM_PossibleAttacked:  /*what can possible attack this square*/\\r
1174                         PossibleAttacked();\\r
1175                         break;\\r
1176 \\r
1177                 case IDM_ReadRow:   /* Read the current row of pieces */\\r
1178                         ReadRow();\\r
1179                         break;\\r
1180 \\r
1181                 case IDM_ReadColumn:   /* Read the current column of pieces */\\r
1182                         ReadColumn();\\r
1183                         break;\\r
1184 \\r
1185                 case IDM_SayCurrentPos: /* Say current position including color */\\r
1186                         SayCurrentPos();\\r
1187                         break;\\r
1188 \\r
1189                 case IDM_SayAllBoard:  /* Say the whole board from bottom to top */\\r
1190                         SayAllBoard();\\r
1191                         break;\\r
1192 \\r
1193                 case IDM_SayMachineMove:  /* Say the last move made */\\r
1194                         timeflag = 1;\\r
1195                         SayMachineMove(1);\\r
1196                         break;\\r
1197 \\r
1198                 case IDM_SayUpperDiagnols:  /* Says the diagnol positions above you */\\r
1199                         SayUpperDiagnols();\\r
1200                         break;\\r
1201 \\r
1202                 case IDM_SayLowerDiagnols:  /* Say the diagnol positions below you */\\r
1203                         SayLowerDiagnols();\\r
1204                         break;\\r
1205 \\r
1206                 case IDM_SayBlackPieces: /*Say the opponents pieces */\\r
1207                         SayPieces(BlackPlay);\\r
1208                         break;\\r
1209 \\r
1210                 case IDM_SayWhitePieces: /*Say the opponents pieces */\\r
1211                         SayPieces(WhitePlay);\\r
1212                         break;\\r
1213 \\r
1214                 case IDM_SayClockTime:  /*Say the clock time */\\r
1215                         SayClockTime();\\r
1216                         break;\\r
1217 \\r
1218                 case IDM_SayWhosTurn:   /* Say whos turn it its */\\r
1219                         SayWhosTurn();\\r
1220                         break;\\r
1221 \\r
1222                 case IDM_SayKnightMoves:  /* Say Knights (L-shaped) move */\\r
1223                         SayKnightMoves();\\r
1224                         break;\\r
1225 \\r
1226                 case OPT_PonderNextMove:  /* Toggle option setting */\\r
1227                         Toggle(&appData.ponderNextMove, "ponder");\\r
1228                         break;\\r
1229 \\r
1230                 case OPT_AnimateMoving:  /* Toggle option setting */\\r
1231                         Toggle(&appData.animate, "animate moving");\\r
1232                         break;\\r
1233 \\r
1234                 case OPT_AutoFlag:  /* Toggle option setting */\\r
1235                         Toggle(&appData.autoCallFlag, "auto flag");\\r
1236                         break;\\r
1237 \\r
1238                 case OPT_AlwaysQueen:  /* Toggle option setting */\\r
1239                         Toggle(&appData.alwaysPromoteToQueen, "always promote to queen");\\r
1240                         break;\\r
1241 \\r
1242                 case OPT_TestLegality:  /* Toggle option setting */\\r
1243                         Toggle(&appData.testLegality, "legality testing");\\r
1244                         break;\\r
1245 \\r
1246                 case OPT_HideThinkFromHuman:  /* Toggle option setting */\\r
1247                         Toggle(&appData.hideThinkingFromHuman, "hide thinking");\\r
1248                         ShowThinkingEvent();\\r
1249                         break;\\r
1250 \\r
1251                 case OPT_SaveExtPGN:  /* Toggle option setting */\\r
1252                         Toggle(&appData.saveExtendedInfoInPGN, "extended P G N info");\\r
1253                         break;\\r
1254 \\r
1255                 case OPT_ExtraInfoInMoveHistory:  /* Toggle option setting */\\r
1256                         Toggle(&appData.showEvalInMoveHistory, "extra info in move histoty");\\r
1257                         break;\\r
1258 \\r
1259 \r
1260 \r
1261 #define JAWS_INIT if (!InitJAWS()) return (FALSE);\r
1262 \r
1263 #define JAWS_DELETE(X)\r
1264 \r
1265 #define JAWS_SILENCE if(suppressClocks) return;\r
1266 \r
1267 #define SAY(S) SayString((S), FALSE)\r
1268 \r
1269 #define SAYMACHINEMOVE() SayMachineMove(0)\r
1270 \r
1271 // After inclusion of this file somewhere early in winboard.c, the remaining part of the patch\r
1272 // is scattered over winboard.c for actually calling the routines.\r
1273 //\r
1274 // * move fromX, fromY declaration to front, before incusion of this file. (Can be permanent change in winboard.c.)\r
1275 // * call InitJAWS(), after calling InitIntance(). (Using JAWS_INIT macro)\r
1276 // * add keyboard cases in main switch of WndProc, though JAWS_KB_NAVIGATION above, e.g. before WM_CHAR case.\r
1277 // * change the WM_CHAR case of same switch from "if(appData.icsActive)" to "if(appData.icsActive JAWS_IF_TAB)"\r
1278 // * add new menu cases in WM_COMMAND case of WndProc, e.g. before IDM_Forward. (throug macro defined above)\r
1279 // * add SAYMACHINEMOVE(); at the end of DisplayMessage();\r
1280 // * add SAY("board"); in WM_CHAR case of ConsoleTextSubclass, just before "SetFocus(buttondesc..."\r