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