Make deferral default in Shogi promotions
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\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, 2010 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 \r
77 #if __GNUC__\r
78 #include <errno.h>\r
79 #include <string.h>\r
80 #endif\r
81 \r
82 #include "common.h"\r
83 #include "frontend.h"\r
84 #include "backend.h"\r
85 #include "winboard.h"\r
86 #include "moves.h"\r
87 #include "wclipbrd.h"\r
88 #include "woptions.h"\r
89 #include "wsockerr.h"\r
90 #include "defaults.h"\r
91 #include "help.h"\r
92 #include "wsnap.h"\r
93 \r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
95 \r
96   int myrandom(void);\r
97   void mysrandom(unsigned int seed);\r
98 \r
99 extern int whiteFlag, blackFlag;\r
100 Boolean flipClock = FALSE;\r
101 extern HANDLE chatHandle[];\r
102 extern int ics_type;\r
103 \r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
105 VOID NewVariantPopup(HWND hwnd);\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
107                    /*char*/int promoChar));\r
108 void DisplayMove P((int moveNumber));\r
109 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
110 void ChatPopUp P((char *s));\r
111 typedef struct {\r
112   ChessSquare piece;  \r
113   POINT pos;      /* window coordinates of current pos */\r
114   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
115   POINT from;     /* board coordinates of the piece's orig pos */\r
116   POINT to;       /* board coordinates of the piece's new pos */\r
117 } AnimInfo;\r
118 \r
119 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
120 \r
121 typedef struct {\r
122   POINT start;    /* window coordinates of start pos */\r
123   POINT pos;      /* window coordinates of current pos */\r
124   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
125   POINT from;     /* board coordinates of the piece's orig pos */\r
126 } DragInfo;\r
127 \r
128 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
129 \r
130 typedef struct {\r
131   POINT sq[2];    /* board coordinates of from, to squares */\r
132 } HighlightInfo;\r
133 \r
134 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
135 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
136 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
137 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
138 \r
139 typedef struct { // [HGM] atomic\r
140   int fromX, fromY, toX, toY, radius;\r
141 } ExplodeInfo;\r
142 \r
143 static ExplodeInfo explodeInfo;\r
144 \r
145 /* Window class names */\r
146 char szAppName[] = "WinBoard";\r
147 char szConsoleName[] = "WBConsole";\r
148 \r
149 /* Title bar text */\r
150 char szTitle[] = "WinBoard";\r
151 char szConsoleTitle[] = "I C S Interaction";\r
152 \r
153 char *programName;\r
154 char *settingsFileName;\r
155 Boolean saveSettingsOnExit;\r
156 char installDir[MSG_SIZ];\r
157 int errorExitStatus;\r
158 \r
159 BoardSize boardSize;\r
160 Boolean chessProgram;\r
161 //static int boardX, boardY;\r
162 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
163 int squareSize, lineGap, minorSize;\r
164 static int winW, winH;\r
165 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
166 static int logoHeight = 0;\r
167 static char messageText[MESSAGE_TEXT_MAX];\r
168 static int clockTimerEvent = 0;\r
169 static int loadGameTimerEvent = 0;\r
170 static int analysisTimerEvent = 0;\r
171 static DelayedEventCallback delayedTimerCallback;\r
172 static int delayedTimerEvent = 0;\r
173 static int buttonCount = 2;\r
174 char *icsTextMenuString;\r
175 char *icsNames;\r
176 char *firstChessProgramNames;\r
177 char *secondChessProgramNames;\r
178 \r
179 #define PALETTESIZE 256\r
180 \r
181 HINSTANCE hInst;          /* current instance */\r
182 Boolean alwaysOnTop = FALSE;\r
183 RECT boardRect;\r
184 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
185   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
186 HPALETTE hPal;\r
187 ColorClass currentColorClass;\r
188 \r
189 HWND hCommPort = NULL;    /* currently open comm port */\r
190 static HWND hwndPause;    /* pause button */\r
191 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
192 static HBRUSH lightSquareBrush, darkSquareBrush,\r
193   blackSquareBrush, /* [HGM] for band between board and holdings */\r
194   explodeBrush,     /* [HGM] atomic */\r
195   markerBrush,      /* [HGM] markers */\r
196   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
197 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
198 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
199 static HPEN gridPen = NULL;\r
200 static HPEN highlightPen = NULL;\r
201 static HPEN premovePen = NULL;\r
202 static NPLOGPALETTE pLogPal;\r
203 static BOOL paletteChanged = FALSE;\r
204 static HICON iconWhite, iconBlack, iconCurrent;\r
205 static int doingSizing = FALSE;\r
206 static int lastSizing = 0;\r
207 static int prevStderrPort;\r
208 static HBITMAP userLogo;\r
209 \r
210 static HBITMAP liteBackTexture = NULL;\r
211 static HBITMAP darkBackTexture = NULL;\r
212 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
213 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
214 static int backTextureSquareSize = 0;\r
215 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
216 \r
217 #if __GNUC__ && !defined(_winmajor)\r
218 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
219 #else\r
220 #if defined(_winmajor)\r
221 #define oldDialog (_winmajor < 4)\r
222 #else\r
223 #define oldDialog 0\r
224 #endif\r
225 #endif\r
226 \r
227 #define INTERNATIONAL\r
228 \r
229 #ifdef INTERNATIONAL\r
230 #  define _(s) T_(s)\r
231 #  define N_(s) s\r
232 #else\r
233 #  define _(s) s\r
234 #  define N_(s) s\r
235 #  define T_(s) s\r
236 #  define Translate(x, y)\r
237 #  define LoadLanguageFile(s)\r
238 #endif\r
239 \r
240 #ifdef INTERNATIONAL\r
241 \r
242 Boolean barbaric; // flag indicating if translation is needed\r
243 \r
244 // list of item numbers used in each dialog (used to alter language at run time)\r
245 \r
246 #define ABOUTBOX -1  /* not sure why these are needed */\r
247 #define ABOUTBOX2 -1\r
248 \r
249 int dialogItems[][40] = {\r
250 { ABOUTBOX, IDOK, 400 }, \r
251 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
252   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
253 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL }, \r
254 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
255   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
256 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
257 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
258   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
259 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
260 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
261   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
262 { ABOUTBOX2, IDC_ChessBoard }, \r
263 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
264   OPT_GameListClose, IDC_GameListDoFilter }, \r
265 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
266 { DLG_Error, IDOK }, \r
267 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
268   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
269 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
270 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
271   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
272   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
273 { DLG_IndexNumber, IDC_Index }, \r
274 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
275 { DLG_TypeInName, IDOK, IDCANCEL }, \r
276 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
277   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
278 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
279   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
280   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
281   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
282   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
283   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
284   OPT_HighlightMoveArrow, OPT_AutoLogo }, \r
285 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
286   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
287   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
288   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
289   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
290   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
291   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
292   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
293   GPB_General, GPB_Alarm }, \r
294 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
295   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
296   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
297   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
298   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
299   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
300   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
301   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size }, \r
302 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
303   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
304   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
305   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
306   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
307   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
308   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat,\r
309   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
310   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
311 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
312   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
313   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,\r
314   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
315 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
316 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
317   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
318 { DLG_MoveHistory }, \r
319 { DLG_EvalGraph }, \r
320 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
321 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
322 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
323   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
324   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
325   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
326 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
327   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
328   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
329 { 0 }\r
330 };\r
331 \r
332 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
333 static int lastChecked;\r
334 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
335 extern int tinyLayout;\r
336 extern char * menuBarText[][8];\r
337 \r
338 void\r
339 LoadLanguageFile(char *name)\r
340 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
341     FILE *f;\r
342     int i=0, j=0, n=0, k;\r
343     char buf[MSG_SIZ];\r
344 \r
345     if(!name || name[0] == NULLCHAR) return;\r
346       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
347     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
348     if((f = fopen(buf, "r")) == NULL) return;\r
349     while((k = fgetc(f)) != EOF) {\r
350         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
351         languageBuf[i] = k;\r
352         if(k == '\n') {\r
353             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
354                 char *p;\r
355                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
356                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
357                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
358                         english[j] = languageBuf + n + 1; *p = 0;\r
359                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
360 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
361                     }\r
362                 }\r
363             }\r
364             n = i + 1;\r
365         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
366             switch(k) {\r
367               case 'n': k = '\n'; break;\r
368               case 'r': k = '\r'; break;\r
369               case 't': k = '\t'; break;\r
370             }\r
371             languageBuf[--i] = k;\r
372         }\r
373         i++;\r
374     }\r
375     fclose(f);\r
376     barbaric = (j != 0);\r
377     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
378 }\r
379 \r
380 char *\r
381 T_(char *s)\r
382 {   // return the translation of the given string\r
383     // efficiency can be improved a lot...\r
384     int i=0;\r
385 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
386     if(!barbaric) return s;\r
387     if(!s) return ""; // sanity\r
388     while(english[i]) {\r
389         if(!strcmp(s, english[i])) return foreign[i];\r
390         i++;\r
391     }\r
392     return s;\r
393 }\r
394 \r
395 void\r
396 Translate(HWND hDlg, int dialogID)\r
397 {   // translate all text items in the given dialog\r
398     int i=0, j, k;\r
399     char buf[MSG_SIZ], *s;\r
400 //if(appData.debugMode) fprintf(debugFP, "Translate(%d)\n", dialogID);\r
401     if(!barbaric) return;\r
402     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
403     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
404     GetWindowText( hDlg, buf, MSG_SIZ );\r
405     s = T_(buf);\r
406 //if(appData.debugMode) fprintf(debugFP, "WindowText '%s' -> '%s'\n", buf, s);\r
407     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
408     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
409         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
410         if(strlen(buf) == 0) continue;\r
411         s = T_(buf);\r
412         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
413     }\r
414 }\r
415 \r
416 void\r
417 TranslateMenus(int addLanguage)\r
418 {\r
419     int i, j;\r
420     WIN32_FIND_DATA fileData;\r
421     HANDLE hFind;\r
422 #define IDM_English 1895\r
423     if(1) {\r
424         HMENU mainMenu = GetMenu(hwndMain);\r
425         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
426           HMENU subMenu = GetSubMenu(mainMenu, i);\r
427           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
428                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
429           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
430             char buf[MSG_SIZ];\r
431             UINT k = GetMenuItemID(subMenu, j);\r
432               if(menuText[i][j])
433                 safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) ); else {\r
434                 GetMenuString(subMenu, j, buf, MSG_SIZ, MF_BYPOSITION);\r
435                 menuText[i][j] = strdup(buf); // remember original on first change\r
436             }\r
437             if(buf[0] == NULLCHAR) continue;\r
438 //fprintf(debugFP, "menu(%d,%d) = %s (%08x, %08x) %d\n", i, j, buf, mainMenu, subMenu, k);\r
439             ModifyMenu(subMenu, j, MF_STRING|MF_BYPOSITION\r
440                                    |CheckMenuItem(subMenu, j, MF_BYPOSITION)\r
441                                    |EnableMenuItem(subMenu, j, MF_BYPOSITION), k, T_(buf));\r
442           }\r
443         }\r
444         DrawMenuBar(hwndMain);\r
445     }\r
446 \r
447     if(!addLanguage) return;\r
448     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
449         HMENU mainMenu = GetMenu(hwndMain);\r
450         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
451         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
452         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
453         i = 0; lastChecked = IDM_English;\r
454         do {\r
455             char *p, *q = fileData.cFileName;\r
456             int checkFlag = MF_UNCHECKED;\r
457             languageFile[i] = strdup(q);\r
458             if(barbaric && !strcmp(oldLanguage, q)) {\r
459                 checkFlag = MF_CHECKED;\r
460                 lastChecked = IDM_English + i + 1;\r
461                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
462             }\r
463             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
464             p = strstr(fileData.cFileName, ".lng");\r
465             if(p) *p = 0;\r
466             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
467         } while(FindNextFile(hFind, &fileData));\r
468         FindClose(hFind);\r
469     }\r
470 }\r
471 \r
472 #endif\r
473 \r
474 typedef struct {\r
475   char *name;\r
476   int squareSize;\r
477   int lineGap;\r
478   int smallLayout;\r
479   int tinyLayout;\r
480   int cliWidth, cliHeight;\r
481 } SizeInfo;\r
482 \r
483 SizeInfo sizeInfo[] = \r
484 {\r
485   { "tiny",     21, 0, 1, 1, 0, 0 },\r
486   { "teeny",    25, 1, 1, 1, 0, 0 },\r
487   { "dinky",    29, 1, 1, 1, 0, 0 },\r
488   { "petite",   33, 1, 1, 1, 0, 0 },\r
489   { "slim",     37, 2, 1, 0, 0, 0 },\r
490   { "small",    40, 2, 1, 0, 0, 0 },\r
491   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
492   { "middling", 49, 2, 0, 0, 0, 0 },\r
493   { "average",  54, 2, 0, 0, 0, 0 },\r
494   { "moderate", 58, 3, 0, 0, 0, 0 },\r
495   { "medium",   64, 3, 0, 0, 0, 0 },\r
496   { "bulky",    72, 3, 0, 0, 0, 0 },\r
497   { "large",    80, 3, 0, 0, 0, 0 },\r
498   { "big",      87, 3, 0, 0, 0, 0 },\r
499   { "huge",     95, 3, 0, 0, 0, 0 },\r
500   { "giant",    108, 3, 0, 0, 0, 0 },\r
501   { "colossal", 116, 4, 0, 0, 0, 0 },\r
502   { "titanic",  129, 4, 0, 0, 0, 0 },\r
503   { NULL, 0, 0, 0, 0, 0, 0 }\r
504 };\r
505 \r
506 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
507 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
508 {\r
509   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },\r
510   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },\r
511   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },\r
512   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },\r
513   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },\r
514   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },\r
515   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },\r
516   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },\r
517   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },\r
518   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },\r
519   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },\r
520   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },\r
521   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },\r
522   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },\r
523   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },\r
524   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },\r
525   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },\r
526   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },\r
527 };\r
528 \r
529 MyFont *font[NUM_SIZES][NUM_FONTS];\r
530 \r
531 typedef struct {\r
532   char *label;\r
533   int id;\r
534   HWND hwnd;\r
535   WNDPROC wndproc;\r
536 } MyButtonDesc;\r
537 \r
538 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
539 #define N_BUTTONS 5\r
540 \r
541 MyButtonDesc buttonDesc[N_BUTTONS] =\r
542 {\r
543   {"<<", IDM_ToStart, NULL, NULL},\r
544   {"<", IDM_Backward, NULL, NULL},\r
545   {"P", IDM_Pause, NULL, NULL},\r
546   {">", IDM_Forward, NULL, NULL},\r
547   {">>", IDM_ToEnd, NULL, NULL},\r
548 };\r
549 \r
550 int tinyLayout = 0, smallLayout = 0;\r
551 #define MENU_BAR_ITEMS 7\r
552 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
553   { N_("&File"), N_("&Mode"), N_("&Action"), N_("&Step"), N_("&Options"), N_("&Help"), NULL },\r
554   { N_("&F"), N_("&M"), N_("&A"), N_("&S"), N_("&O"), N_("&H"), NULL },\r
555 };\r
556 \r
557 \r
558 MySound sounds[(int)NSoundClasses];\r
559 MyTextAttribs textAttribs[(int)NColorClasses];\r
560 \r
561 MyColorizeAttribs colorizeAttribs[] = {\r
562   { (COLORREF)0, 0, N_("Shout Text") },\r
563   { (COLORREF)0, 0, N_("SShout/CShout") },\r
564   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
565   { (COLORREF)0, 0, N_("Channel Text") },\r
566   { (COLORREF)0, 0, N_("Kibitz Text") },\r
567   { (COLORREF)0, 0, N_("Tell Text") },\r
568   { (COLORREF)0, 0, N_("Challenge Text") },\r
569   { (COLORREF)0, 0, N_("Request Text") },\r
570   { (COLORREF)0, 0, N_("Seek Text") },\r
571   { (COLORREF)0, 0, N_("Normal Text") },\r
572   { (COLORREF)0, 0, N_("None") }\r
573 };\r
574 \r
575 \r
576 \r
577 static char *commentTitle;\r
578 static char *commentText;\r
579 static int commentIndex;\r
580 static Boolean editComment = FALSE;\r
581 \r
582 \r
583 char errorTitle[MSG_SIZ];\r
584 char errorMessage[2*MSG_SIZ];\r
585 HWND errorDialog = NULL;\r
586 BOOLEAN moveErrorMessageUp = FALSE;\r
587 BOOLEAN consoleEcho = TRUE;\r
588 CHARFORMAT consoleCF;\r
589 COLORREF consoleBackgroundColor;\r
590 \r
591 char *programVersion;\r
592 \r
593 #define CPReal 1\r
594 #define CPComm 2\r
595 #define CPSock 3\r
596 #define CPRcmd 4\r
597 typedef int CPKind;\r
598 \r
599 typedef struct {\r
600   CPKind kind;\r
601   HANDLE hProcess;\r
602   DWORD pid;\r
603   HANDLE hTo;\r
604   HANDLE hFrom;\r
605   SOCKET sock;\r
606   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
607 } ChildProc;\r
608 \r
609 #define INPUT_SOURCE_BUF_SIZE 4096\r
610 \r
611 typedef struct _InputSource {\r
612   CPKind kind;\r
613   HANDLE hFile;\r
614   SOCKET sock;\r
615   int lineByLine;\r
616   HANDLE hThread;\r
617   DWORD id;\r
618   char buf[INPUT_SOURCE_BUF_SIZE];\r
619   char *next;\r
620   DWORD count;\r
621   int error;\r
622   InputCallback func;\r
623   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
624   VOIDSTAR closure;\r
625 } InputSource;\r
626 \r
627 InputSource *consoleInputSource;\r
628 \r
629 DCB dcb;\r
630 \r
631 /* forward */\r
632 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
633 VOID ConsoleCreate();\r
634 LRESULT CALLBACK\r
635   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
636 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
637 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
638 VOID ParseCommSettings(char *arg, DCB *dcb);\r
639 LRESULT CALLBACK\r
640   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
641 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
642 void ParseIcsTextMenu(char *icsTextMenuString);\r
643 VOID PopUpMoveDialog(char firstchar);\r
644 VOID PopUpNameDialog(char firstchar);\r
645 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
646 \r
647 /* [AS] */\r
648 int NewGameFRC();\r
649 int GameListOptions();\r
650 \r
651 int dummy; // [HGM] for obsolete args\r
652 \r
653 HWND hwndMain = NULL;        /* root window*/\r
654 HWND hwndConsole = NULL;\r
655 HWND commentDialog = NULL;\r
656 HWND moveHistoryDialog = NULL;\r
657 HWND evalGraphDialog = NULL;\r
658 HWND engineOutputDialog = NULL;\r
659 HWND gameListDialog = NULL;\r
660 HWND editTagsDialog = NULL;\r
661 \r
662 int commentUp = FALSE;\r
663 \r
664 WindowPlacement wpMain;\r
665 WindowPlacement wpConsole;\r
666 WindowPlacement wpComment;\r
667 WindowPlacement wpMoveHistory;\r
668 WindowPlacement wpEvalGraph;\r
669 WindowPlacement wpEngineOutput;\r
670 WindowPlacement wpGameList;\r
671 WindowPlacement wpTags;\r
672 \r
673 VOID EngineOptionsPopup(); // [HGM] settings\r
674 \r
675 VOID GothicPopUp(char *title, VariantClass variant);\r
676 /*\r
677  * Setting "frozen" should disable all user input other than deleting\r
678  * the window.  We do this while engines are initializing themselves.\r
679  */\r
680 static int frozen = 0;\r
681 static int oldMenuItemState[MENU_BAR_ITEMS];\r
682 void FreezeUI()\r
683 {\r
684   HMENU hmenu;\r
685   int i;\r
686 \r
687   if (frozen) return;\r
688   frozen = 1;\r
689   hmenu = GetMenu(hwndMain);\r
690   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
691     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
692   }\r
693   DrawMenuBar(hwndMain);\r
694 }\r
695 \r
696 /* Undo a FreezeUI */\r
697 void ThawUI()\r
698 {\r
699   HMENU hmenu;\r
700   int i;\r
701 \r
702   if (!frozen) return;\r
703   frozen = 0;\r
704   hmenu = GetMenu(hwndMain);\r
705   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
706     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
707   }\r
708   DrawMenuBar(hwndMain);\r
709 }\r
710 \r
711 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
712 \r
713 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
714 #ifdef JAWS\r
715 #include "jaws.c"\r
716 #else\r
717 #define JAWS_INIT\r
718 #define JAWS_ARGS\r
719 #define JAWS_ALT_INTERCEPT\r
720 #define JAWS_KB_NAVIGATION\r
721 #define JAWS_MENU_ITEMS\r
722 #define JAWS_SILENCE\r
723 #define JAWS_REPLAY\r
724 #define JAWS_ACCEL\r
725 #define JAWS_COPYRIGHT\r
726 #define JAWS_DELETE(X) X\r
727 #define SAYMACHINEMOVE()\r
728 #define SAY(X)\r
729 #endif\r
730 \r
731 /*---------------------------------------------------------------------------*\\r
732  *\r
733  * WinMain\r
734  *\r
735 \*---------------------------------------------------------------------------*/\r
736 \r
737 int APIENTRY\r
738 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
739         LPSTR lpCmdLine, int nCmdShow)\r
740 {\r
741   MSG msg;\r
742   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
743 //  INITCOMMONCONTROLSEX ex;\r
744 \r
745   debugFP = stderr;\r
746 \r
747   LoadLibrary("RICHED32.DLL");\r
748   consoleCF.cbSize = sizeof(CHARFORMAT);\r
749 \r
750   if (!InitApplication(hInstance)) {\r
751     return (FALSE);\r
752   }\r
753   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
754     return (FALSE);\r
755   }\r
756 \r
757   JAWS_INIT\r
758 \r
759 //  InitCommonControlsEx(&ex);\r
760   InitCommonControls();\r
761 \r
762   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
763   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
764   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
765 \r
766   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
767 \r
768   while (GetMessage(&msg, /* message structure */\r
769                     NULL, /* handle of window receiving the message */\r
770                     0,    /* lowest message to examine */\r
771                     0))   /* highest message to examine */\r
772     {\r
773 \r
774       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
775         // [HGM] navigate: switch between all windows with tab\r
776         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
777         int i, currentElement = 0;\r
778 \r
779         // first determine what element of the chain we come from (if any)\r
780         if(appData.icsActive) {\r
781             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
782             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
783         }\r
784         if(engineOutputDialog && EngineOutputIsUp()) {\r
785             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
786             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
787         }\r
788         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
789             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
790         }\r
791         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
792         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
793         if(msg.hwnd == e1)                 currentElement = 2; else\r
794         if(msg.hwnd == e2)                 currentElement = 3; else\r
795         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
796         if(msg.hwnd == mh)                currentElement = 4; else\r
797         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
798         if(msg.hwnd == hText)  currentElement = 5; else\r
799         if(msg.hwnd == hInput) currentElement = 6; else\r
800         for (i = 0; i < N_BUTTONS; i++) {\r
801             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
802         }\r
803 \r
804         // determine where to go to\r
805         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
806           do {\r
807             currentElement = (currentElement + direction) % 7;\r
808             switch(currentElement) {\r
809                 case 0:\r
810                   h = hwndMain; break; // passing this case always makes the loop exit\r
811                 case 1:\r
812                   h = buttonDesc[0].hwnd; break; // could be NULL\r
813                 case 2:\r
814                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
815                   h = e1; break;\r
816                 case 3:\r
817                   if(!EngineOutputIsUp()) continue;\r
818                   h = e2; break;\r
819                 case 4:\r
820                   if(!MoveHistoryIsUp()) continue;\r
821                   h = mh; break;\r
822 //              case 6: // input to eval graph does not seem to get here!\r
823 //                if(!EvalGraphIsUp()) continue;\r
824 //                h = evalGraphDialog; break;\r
825                 case 5:\r
826                   if(!appData.icsActive) continue;\r
827                   SAY("display");\r
828                   h = hText; break;\r
829                 case 6:\r
830                   if(!appData.icsActive) continue;\r
831                   SAY("input");\r
832                   h = hInput; break;\r
833             }\r
834           } while(h == 0);\r
835 \r
836           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
837           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
838           SetFocus(h);\r
839 \r
840           continue; // this message now has been processed\r
841         }\r
842       }\r
843 \r
844       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
845           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
846           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
847           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
848           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
849           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
850           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
851           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
852           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
853           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
854         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
855         for(i=0; i<MAX_CHAT; i++) \r
856             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
857                 done = 1; break;\r
858         }\r
859         if(done) continue; // [HGM] chat: end patch\r
860         TranslateMessage(&msg); /* Translates virtual key codes */\r
861         DispatchMessage(&msg);  /* Dispatches message to window */\r
862       }\r
863     }\r
864 \r
865 \r
866   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
867 }\r
868 \r
869 /*---------------------------------------------------------------------------*\\r
870  *\r
871  * Initialization functions\r
872  *\r
873 \*---------------------------------------------------------------------------*/\r
874 \r
875 void\r
876 SetUserLogo()\r
877 {   // update user logo if necessary\r
878     static char oldUserName[MSG_SIZ], *curName;\r
879 \r
880     if(appData.autoLogo) {\r
881           curName = UserName();\r
882           if(strcmp(curName, oldUserName)) {\r
883             snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
884                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
885                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
886           }\r
887     }\r
888 }\r
889 \r
890 BOOL\r
891 InitApplication(HINSTANCE hInstance)\r
892 {\r
893   WNDCLASS wc;\r
894 \r
895   /* Fill in window class structure with parameters that describe the */\r
896   /* main window. */\r
897 \r
898   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
899   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
900   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
901   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
902   wc.hInstance     = hInstance;         /* Owner of this class */\r
903   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
904   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
905   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
906   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
907   wc.lpszClassName = szAppName;                 /* Name to register as */\r
908 \r
909   /* Register the window class and return success/failure code. */\r
910   if (!RegisterClass(&wc)) return FALSE;\r
911 \r
912   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
913   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
914   wc.cbClsExtra    = 0;\r
915   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
916   wc.hInstance     = hInstance;\r
917   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
918   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
919   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
920   wc.lpszMenuName  = NULL;\r
921   wc.lpszClassName = szConsoleName;\r
922 \r
923   if (!RegisterClass(&wc)) return FALSE;\r
924   return TRUE;\r
925 }\r
926 \r
927 \r
928 /* Set by InitInstance, used by EnsureOnScreen */\r
929 int screenHeight, screenWidth;\r
930 \r
931 void\r
932 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
933 {\r
934 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
935   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
936   if (*x > screenWidth - 32) *x = 0;\r
937   if (*y > screenHeight - 32) *y = 0;\r
938   if (*x < minX) *x = minX;\r
939   if (*y < minY) *y = minY;\r
940 }\r
941 \r
942 BOOL\r
943 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
944 {\r
945   HWND hwnd; /* Main window handle. */\r
946   int ibs;\r
947   WINDOWPLACEMENT wp;\r
948   char *filepart;\r
949 \r
950   hInst = hInstance;    /* Store instance handle in our global variable */\r
951   programName = szAppName;\r
952 \r
953   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
954     *filepart = NULLCHAR;\r
955   } else {\r
956     GetCurrentDirectory(MSG_SIZ, installDir);\r
957   }\r
958   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
959   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
960   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
961   /* xboard, and older WinBoards, controlled the move sound with the\r
962      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
963      always turn the option on (so that the backend will call us),\r
964      then let the user turn the sound off by setting it to silence if\r
965      desired.  To accommodate old winboard.ini files saved by old\r
966      versions of WinBoard, we also turn off the sound if the option\r
967      was initially set to false. [HGM] taken out of InitAppData */\r
968   if (!appData.ringBellAfterMoves) {\r
969     sounds[(int)SoundMove].name = strdup("");\r
970     appData.ringBellAfterMoves = TRUE;\r
971   }\r
972   if (appData.debugMode) {\r
973     debugFP = fopen(appData.nameOfDebugFile, "w");\r
974     setbuf(debugFP, NULL);\r
975   }\r
976 \r
977   LoadLanguageFile(appData.language);\r
978 \r
979   InitBackEnd1();\r
980 \r
981 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
982 //  InitEngineUCI( installDir, &second );\r
983 \r
984   /* Create a main window for this application instance. */\r
985   hwnd = CreateWindow(szAppName, szTitle,\r
986                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
987                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
988                       NULL, NULL, hInstance, NULL);\r
989   hwndMain = hwnd;\r
990 \r
991   /* If window could not be created, return "failure" */\r
992   if (!hwnd) {\r
993     return (FALSE);\r
994   }\r
995 \r
996   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
997   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
998       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
999 \r
1000       if (first.programLogo == NULL && appData.debugMode) {\r
1001           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
1002       }\r
1003   } else if(appData.autoLogo) {\r
1004       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
1005         char buf[MSG_SIZ];\r
1006           snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.firstDirectory);\r
1007         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
1008       }\r
1009   }\r
1010 \r
1011   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
1012       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1013 \r
1014       if (second.programLogo == NULL && appData.debugMode) {\r
1015           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
1016       }\r
1017   } else if(appData.autoLogo) {\r
1018       char buf[MSG_SIZ];\r
1019       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
1020         snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);\r
1021         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1022       } else\r
1023       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
1024         snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);\r
1025         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
1026       }\r
1027   }\r
1028 \r
1029   SetUserLogo();\r
1030 \r
1031   iconWhite = LoadIcon(hInstance, "icon_white");\r
1032   iconBlack = LoadIcon(hInstance, "icon_black");\r
1033   iconCurrent = iconWhite;\r
1034   InitDrawingColors();\r
1035   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1036   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1037   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1038     /* Compute window size for each board size, and use the largest\r
1039        size that fits on this screen as the default. */\r
1040     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1041     if (boardSize == (BoardSize)-1 &&\r
1042         winH <= screenHeight\r
1043            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1044         && winW <= screenWidth) {\r
1045       boardSize = (BoardSize)ibs;\r
1046     }\r
1047   }\r
1048 \r
1049   InitDrawingSizes(boardSize, 0);\r
1050   TranslateMenus(1);\r
1051   InitMenuChecks();\r
1052   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1053 \r
1054   /* [AS] Load textures if specified */\r
1055   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1056   \r
1057   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1058       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1059       liteBackTextureMode = appData.liteBackTextureMode;\r
1060 \r
1061       if (liteBackTexture == NULL && appData.debugMode) {\r
1062           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1063       }\r
1064   }\r
1065   \r
1066   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1067       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1068       darkBackTextureMode = appData.darkBackTextureMode;\r
1069 \r
1070       if (darkBackTexture == NULL && appData.debugMode) {\r
1071           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1072       }\r
1073   }\r
1074 \r
1075   mysrandom( (unsigned) time(NULL) );\r
1076 \r
1077   /* [AS] Restore layout */\r
1078   if( wpMoveHistory.visible ) {\r
1079       MoveHistoryPopUp();\r
1080   }\r
1081 \r
1082   if( wpEvalGraph.visible ) {\r
1083       EvalGraphPopUp();\r
1084   }\r
1085 \r
1086   if( wpEngineOutput.visible ) {\r
1087       EngineOutputPopUp();\r
1088   }\r
1089 \r
1090   /* Make the window visible; update its client area; and return "success" */\r
1091   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1092   wp.length = sizeof(WINDOWPLACEMENT);\r
1093   wp.flags = 0;\r
1094   wp.showCmd = nCmdShow;\r
1095   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1096   wp.rcNormalPosition.left = wpMain.x;\r
1097   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1098   wp.rcNormalPosition.top = wpMain.y;\r
1099   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1100   SetWindowPlacement(hwndMain, &wp);\r
1101 \r
1102   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1103 \r
1104   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1105                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1106 \r
1107   if (hwndConsole) {\r
1108 #if AOT_CONSOLE\r
1109     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1110                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1111 #endif\r
1112     ShowWindow(hwndConsole, nCmdShow);\r
1113     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
1114       char buf[MSG_SIZ], *p = buf, *q;\r
1115         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
1116       do {\r
1117         q = strchr(p, ';');\r
1118         if(q) *q++ = 0;\r
1119         if(*p) ChatPopUp(p);\r
1120       } while(p=q);\r
1121     }\r
1122     SetActiveWindow(hwndConsole);\r
1123   }\r
1124   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1125   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1126 \r
1127   return TRUE;\r
1128 \r
1129 }\r
1130 \r
1131 VOID\r
1132 InitMenuChecks()\r
1133 {\r
1134   HMENU hmenu = GetMenu(hwndMain);\r
1135 \r
1136   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1137                         MF_BYCOMMAND|((appData.icsActive &&\r
1138                                        *appData.icsCommPort != NULLCHAR) ?\r
1139                                       MF_ENABLED : MF_GRAYED));\r
1140   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1141                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1142                                      MF_CHECKED : MF_UNCHECKED));\r
1143 }\r
1144 \r
1145 //---------------------------------------------------------------------------------------------------------\r
1146 \r
1147 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1148 #define XBOARD FALSE\r
1149 \r
1150 #define OPTCHAR "/"\r
1151 #define SEPCHAR "="\r
1152 \r
1153 #include "args.h"\r
1154 \r
1155 // front-end part of option handling\r
1156 \r
1157 VOID\r
1158 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1159 {\r
1160   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1161   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1162   DeleteDC(hdc);\r
1163   lf->lfWidth = 0;\r
1164   lf->lfEscapement = 0;\r
1165   lf->lfOrientation = 0;\r
1166   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1167   lf->lfItalic = mfp->italic;\r
1168   lf->lfUnderline = mfp->underline;\r
1169   lf->lfStrikeOut = mfp->strikeout;\r
1170   lf->lfCharSet = mfp->charset;\r
1171   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1172   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1173   lf->lfQuality = DEFAULT_QUALITY;\r
1174   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1175     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1176 }\r
1177 \r
1178 void\r
1179 CreateFontInMF(MyFont *mf)\r
1180\r
1181   LFfromMFP(&mf->lf, &mf->mfp);\r
1182   if (mf->hf) DeleteObject(mf->hf);\r
1183   mf->hf = CreateFontIndirect(&mf->lf);\r
1184 }\r
1185 \r
1186 // [HGM] This platform-dependent table provides the location for storing the color info\r
1187 void *\r
1188 colorVariable[] = {\r
1189   &whitePieceColor, \r
1190   &blackPieceColor, \r
1191   &lightSquareColor,\r
1192   &darkSquareColor, \r
1193   &highlightSquareColor,\r
1194   &premoveHighlightColor,\r
1195   NULL,\r
1196   &consoleBackgroundColor,\r
1197   &appData.fontForeColorWhite,\r
1198   &appData.fontBackColorWhite,\r
1199   &appData.fontForeColorBlack,\r
1200   &appData.fontBackColorBlack,\r
1201   &appData.evalHistColorWhite,\r
1202   &appData.evalHistColorBlack,\r
1203   &appData.highlightArrowColor,\r
1204 };\r
1205 \r
1206 /* Command line font name parser.  NULL name means do nothing.\r
1207    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1208    For backward compatibility, syntax without the colon is also\r
1209    accepted, but font names with digits in them won't work in that case.\r
1210 */\r
1211 VOID\r
1212 ParseFontName(char *name, MyFontParams *mfp)\r
1213 {\r
1214   char *p, *q;\r
1215   if (name == NULL) return;\r
1216   p = name;\r
1217   q = strchr(p, ':');\r
1218   if (q) {\r
1219     if (q - p >= sizeof(mfp->faceName))\r
1220       ExitArgError(_("Font name too long:"), name);\r
1221     memcpy(mfp->faceName, p, q - p);\r
1222     mfp->faceName[q - p] = NULLCHAR;\r
1223     p = q + 1;\r
1224   } else {\r
1225     q = mfp->faceName;\r
1226     while (*p && !isdigit(*p)) {\r
1227       *q++ = *p++;\r
1228       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1229         ExitArgError(_("Font name too long:"), name);\r
1230     }\r
1231     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1232     *q = NULLCHAR;\r
1233   }\r
1234   if (!*p) ExitArgError(_("Font point size missing:"), name);\r
1235   mfp->pointSize = (float) atof(p);\r
1236   mfp->bold = (strchr(p, 'b') != NULL);\r
1237   mfp->italic = (strchr(p, 'i') != NULL);\r
1238   mfp->underline = (strchr(p, 'u') != NULL);\r
1239   mfp->strikeout = (strchr(p, 's') != NULL);\r
1240   mfp->charset = DEFAULT_CHARSET;\r
1241   q = strchr(p, 'c');\r
1242   if (q)\r
1243     mfp->charset = (BYTE) atoi(q+1);\r
1244 }\r
1245 \r
1246 void\r
1247 ParseFont(char *name, int number)\r
1248 { // wrapper to shield back-end from 'font'\r
1249   ParseFontName(name, &font[boardSize][number]->mfp);\r
1250 }\r
1251 \r
1252 void\r
1253 SetFontDefaults()\r
1254 { // in WB  we have a 2D array of fonts; this initializes their description\r
1255   int i, j;\r
1256   /* Point font array elements to structures and\r
1257      parse default font names */\r
1258   for (i=0; i<NUM_FONTS; i++) {\r
1259     for (j=0; j<NUM_SIZES; j++) {\r
1260       font[j][i] = &fontRec[j][i];\r
1261       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1262     }\r
1263   }\r
1264 }\r
1265 \r
1266 void\r
1267 CreateFonts()\r
1268 { // here we create the actual fonts from the selected descriptions\r
1269   int i, j;\r
1270   for (i=0; i<NUM_FONTS; i++) {\r
1271     for (j=0; j<NUM_SIZES; j++) {\r
1272       CreateFontInMF(font[j][i]);\r
1273     }\r
1274   }\r
1275 }\r
1276 /* Color name parser.\r
1277    X version accepts X color names, but this one\r
1278    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1279 COLORREF\r
1280 ParseColorName(char *name)\r
1281 {\r
1282   int red, green, blue, count;\r
1283   char buf[MSG_SIZ];\r
1284 \r
1285   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1286   if (count != 3) {\r
1287     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1288       &red, &green, &blue);\r
1289   }\r
1290   if (count != 3) {\r
1291     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1292     DisplayError(buf, 0);\r
1293     return RGB(0, 0, 0);\r
1294   }\r
1295   return PALETTERGB(red, green, blue);\r
1296 }\r
1297 \r
1298 void\r
1299 ParseColor(int n, char *name)\r
1300 { // for WinBoard the color is an int, which needs to be derived from the string\r
1301   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1302 }\r
1303 \r
1304 void\r
1305 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1306 {\r
1307   char *e = argValue;\r
1308   int eff = 0;\r
1309 \r
1310   while (*e) {\r
1311     if (*e == 'b')      eff |= CFE_BOLD;\r
1312     else if (*e == 'i') eff |= CFE_ITALIC;\r
1313     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1314     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1315     else if (*e == '#' || isdigit(*e)) break;\r
1316     e++;\r
1317   }\r
1318   *effects = eff;\r
1319   *color   = ParseColorName(e);\r
1320 }\r
1321 \r
1322 void\r
1323 ParseTextAttribs(ColorClass cc, char *s)\r
1324 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1325     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1326     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1327 }\r
1328 \r
1329 void\r
1330 ParseBoardSize(void *addr, char *name)\r
1331 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1332   BoardSize bs = SizeTiny;\r
1333   while (sizeInfo[bs].name != NULL) {\r
1334     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1335         *(BoardSize *)addr = bs;\r
1336         return;\r
1337     }\r
1338     bs++;\r
1339   }\r
1340   ExitArgError(_("Unrecognized board size value"), name);\r
1341 }\r
1342 \r
1343 void\r
1344 LoadAllSounds()\r
1345 { // [HGM] import name from appData first\r
1346   ColorClass cc;\r
1347   SoundClass sc;\r
1348   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1349     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1350     textAttribs[cc].sound.data = NULL;\r
1351     MyLoadSound(&textAttribs[cc].sound);\r
1352   }\r
1353   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1354     textAttribs[cc].sound.name = strdup("");\r
1355     textAttribs[cc].sound.data = NULL;\r
1356   }\r
1357   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1358     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1359     sounds[sc].data = NULL;\r
1360     MyLoadSound(&sounds[sc]);\r
1361   }\r
1362 }\r
1363 \r
1364 void\r
1365 SetCommPortDefaults()\r
1366 {\r
1367    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1368   dcb.DCBlength = sizeof(DCB);\r
1369   dcb.BaudRate = 9600;\r
1370   dcb.fBinary = TRUE;\r
1371   dcb.fParity = FALSE;\r
1372   dcb.fOutxCtsFlow = FALSE;\r
1373   dcb.fOutxDsrFlow = FALSE;\r
1374   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1375   dcb.fDsrSensitivity = FALSE;\r
1376   dcb.fTXContinueOnXoff = TRUE;\r
1377   dcb.fOutX = FALSE;\r
1378   dcb.fInX = FALSE;\r
1379   dcb.fNull = FALSE;\r
1380   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1381   dcb.fAbortOnError = FALSE;\r
1382   dcb.ByteSize = 7;\r
1383   dcb.Parity = SPACEPARITY;\r
1384   dcb.StopBits = ONESTOPBIT;\r
1385 }\r
1386 \r
1387 // [HGM] args: these three cases taken out to stay in front-end\r
1388 void\r
1389 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1390 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1391         // while the curent board size determines the element. This system should be ported to XBoard.\r
1392         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1393         int bs;\r
1394         for (bs=0; bs<NUM_SIZES; bs++) {\r
1395           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1396           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1397           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1398             ad->argName, mfp->faceName, mfp->pointSize,\r
1399             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1400             mfp->bold ? "b" : "",\r
1401             mfp->italic ? "i" : "",\r
1402             mfp->underline ? "u" : "",\r
1403             mfp->strikeout ? "s" : "",\r
1404             (int)mfp->charset);\r
1405         }\r
1406       }\r
1407 \r
1408 void\r
1409 ExportSounds()\r
1410 { // [HGM] copy the names from the internal WB variables to appData\r
1411   ColorClass cc;\r
1412   SoundClass sc;\r
1413   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1414     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1415   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1416     (&appData.soundMove)[sc] = sounds[sc].name;\r
1417 }\r
1418 \r
1419 void\r
1420 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1421 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1422         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1423         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1424           (ta->effects & CFE_BOLD) ? "b" : "",\r
1425           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1426           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1427           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1428           (ta->effects) ? " " : "",\r
1429           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1430       }\r
1431 \r
1432 void\r
1433 SaveColor(FILE *f, ArgDescriptor *ad)\r
1434 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1435         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1436         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1437           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1438 }\r
1439 \r
1440 void\r
1441 SaveBoardSize(FILE *f, char *name, void *addr)\r
1442 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1443   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1444 }\r
1445 \r
1446 void\r
1447 ParseCommPortSettings(char *s)\r
1448 { // wrapper to keep dcb from back-end\r
1449   ParseCommSettings(s, &dcb);\r
1450 }\r
1451 \r
1452 void\r
1453 GetWindowCoords()\r
1454 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1455   GetActualPlacement(hwndMain, &wpMain);\r
1456   GetActualPlacement(hwndConsole, &wpConsole);\r
1457   GetActualPlacement(commentDialog, &wpComment);\r
1458   GetActualPlacement(editTagsDialog, &wpTags);\r
1459   GetActualPlacement(gameListDialog, &wpGameList);\r
1460   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1461   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1462   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1463 }\r
1464 \r
1465 void\r
1466 PrintCommPortSettings(FILE *f, char *name)\r
1467 { // wrapper to shield back-end from DCB\r
1468       PrintCommSettings(f, name, &dcb);\r
1469 }\r
1470 \r
1471 int\r
1472 MySearchPath(char *installDir, char *name, char *fullname)\r
1473 {\r
1474   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1475   if(name[0]== '%') {\r
1476     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1477     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1478       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1479       *strchr(buf, '%') = 0;\r
1480       strcat(fullname, getenv(buf));\r
1481       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1482     }\r
1483     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1484     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1485     return (int) strlen(fullname);\r
1486   }\r
1487   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1488 }\r
1489 \r
1490 int\r
1491 MyGetFullPathName(char *name, char *fullname)\r
1492 {\r
1493   char *dummy;\r
1494   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1495 }\r
1496 \r
1497 int\r
1498 MainWindowUp()\r
1499 { // [HGM] args: allows testing if main window is realized from back-end\r
1500   return hwndMain != NULL;\r
1501 }\r
1502 \r
1503 void\r
1504 PopUpStartupDialog()\r
1505 {\r
1506     FARPROC lpProc;\r
1507     \r
1508     LoadLanguageFile(appData.language);\r
1509     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1510     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1511     FreeProcInstance(lpProc);\r
1512 }\r
1513 \r
1514 /*---------------------------------------------------------------------------*\\r
1515  *\r
1516  * GDI board drawing routines\r
1517  *\r
1518 \*---------------------------------------------------------------------------*/\r
1519 \r
1520 /* [AS] Draw square using background texture */\r
1521 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1522 {\r
1523     XFORM   x;\r
1524 \r
1525     if( mode == 0 ) {\r
1526         return; /* Should never happen! */\r
1527     }\r
1528 \r
1529     SetGraphicsMode( dst, GM_ADVANCED );\r
1530 \r
1531     switch( mode ) {\r
1532     case 1:\r
1533         /* Identity */\r
1534         break;\r
1535     case 2:\r
1536         /* X reflection */\r
1537         x.eM11 = -1.0;\r
1538         x.eM12 = 0;\r
1539         x.eM21 = 0;\r
1540         x.eM22 = 1.0;\r
1541         x.eDx = (FLOAT) dw + dx - 1;\r
1542         x.eDy = 0;\r
1543         dx = 0;\r
1544         SetWorldTransform( dst, &x );\r
1545         break;\r
1546     case 3:\r
1547         /* Y reflection */\r
1548         x.eM11 = 1.0;\r
1549         x.eM12 = 0;\r
1550         x.eM21 = 0;\r
1551         x.eM22 = -1.0;\r
1552         x.eDx = 0;\r
1553         x.eDy = (FLOAT) dh + dy - 1;\r
1554         dy = 0;\r
1555         SetWorldTransform( dst, &x );\r
1556         break;\r
1557     case 4:\r
1558         /* X/Y flip */\r
1559         x.eM11 = 0;\r
1560         x.eM12 = 1.0;\r
1561         x.eM21 = 1.0;\r
1562         x.eM22 = 0;\r
1563         x.eDx = (FLOAT) dx;\r
1564         x.eDy = (FLOAT) dy;\r
1565         dx = 0;\r
1566         dy = 0;\r
1567         SetWorldTransform( dst, &x );\r
1568         break;\r
1569     }\r
1570 \r
1571     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1572 \r
1573     x.eM11 = 1.0;\r
1574     x.eM12 = 0;\r
1575     x.eM21 = 0;\r
1576     x.eM22 = 1.0;\r
1577     x.eDx = 0;\r
1578     x.eDy = 0;\r
1579     SetWorldTransform( dst, &x );\r
1580 \r
1581     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1582 }\r
1583 \r
1584 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1585 enum {\r
1586     PM_WP = (int) WhitePawn, \r
1587     PM_WN = (int) WhiteKnight, \r
1588     PM_WB = (int) WhiteBishop, \r
1589     PM_WR = (int) WhiteRook, \r
1590     PM_WQ = (int) WhiteQueen, \r
1591     PM_WF = (int) WhiteFerz, \r
1592     PM_WW = (int) WhiteWazir, \r
1593     PM_WE = (int) WhiteAlfil, \r
1594     PM_WM = (int) WhiteMan, \r
1595     PM_WO = (int) WhiteCannon, \r
1596     PM_WU = (int) WhiteUnicorn, \r
1597     PM_WH = (int) WhiteNightrider, \r
1598     PM_WA = (int) WhiteAngel, \r
1599     PM_WC = (int) WhiteMarshall, \r
1600     PM_WAB = (int) WhiteCardinal, \r
1601     PM_WD = (int) WhiteDragon, \r
1602     PM_WL = (int) WhiteLance, \r
1603     PM_WS = (int) WhiteCobra, \r
1604     PM_WV = (int) WhiteFalcon, \r
1605     PM_WSG = (int) WhiteSilver, \r
1606     PM_WG = (int) WhiteGrasshopper, \r
1607     PM_WK = (int) WhiteKing,\r
1608     PM_BP = (int) BlackPawn, \r
1609     PM_BN = (int) BlackKnight, \r
1610     PM_BB = (int) BlackBishop, \r
1611     PM_BR = (int) BlackRook, \r
1612     PM_BQ = (int) BlackQueen, \r
1613     PM_BF = (int) BlackFerz, \r
1614     PM_BW = (int) BlackWazir, \r
1615     PM_BE = (int) BlackAlfil, \r
1616     PM_BM = (int) BlackMan,\r
1617     PM_BO = (int) BlackCannon, \r
1618     PM_BU = (int) BlackUnicorn, \r
1619     PM_BH = (int) BlackNightrider, \r
1620     PM_BA = (int) BlackAngel, \r
1621     PM_BC = (int) BlackMarshall, \r
1622     PM_BG = (int) BlackGrasshopper, \r
1623     PM_BAB = (int) BlackCardinal,\r
1624     PM_BD = (int) BlackDragon,\r
1625     PM_BL = (int) BlackLance,\r
1626     PM_BS = (int) BlackCobra,\r
1627     PM_BV = (int) BlackFalcon,\r
1628     PM_BSG = (int) BlackSilver,\r
1629     PM_BK = (int) BlackKing\r
1630 };\r
1631 \r
1632 static HFONT hPieceFont = NULL;\r
1633 static HBITMAP hPieceMask[(int) EmptySquare];\r
1634 static HBITMAP hPieceFace[(int) EmptySquare];\r
1635 static int fontBitmapSquareSize = 0;\r
1636 static char pieceToFontChar[(int) EmptySquare] =\r
1637                               { 'p', 'n', 'b', 'r', 'q', \r
1638                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1639                       'k', 'o', 'm', 'v', 't', 'w', \r
1640                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1641                                                               'l' };\r
1642 \r
1643 extern BOOL SetCharTable( char *table, const char * map );\r
1644 /* [HGM] moved to backend.c */\r
1645 \r
1646 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1647 {\r
1648     HBRUSH hbrush;\r
1649     BYTE r1 = GetRValue( color );\r
1650     BYTE g1 = GetGValue( color );\r
1651     BYTE b1 = GetBValue( color );\r
1652     BYTE r2 = r1 / 2;\r
1653     BYTE g2 = g1 / 2;\r
1654     BYTE b2 = b1 / 2;\r
1655     RECT rc;\r
1656 \r
1657     /* Create a uniform background first */\r
1658     hbrush = CreateSolidBrush( color );\r
1659     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1660     FillRect( hdc, &rc, hbrush );\r
1661     DeleteObject( hbrush );\r
1662     \r
1663     if( mode == 1 ) {\r
1664         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1665         int steps = squareSize / 2;\r
1666         int i;\r
1667 \r
1668         for( i=0; i<steps; i++ ) {\r
1669             BYTE r = r1 - (r1-r2) * i / steps;\r
1670             BYTE g = g1 - (g1-g2) * i / steps;\r
1671             BYTE b = b1 - (b1-b2) * i / steps;\r
1672 \r
1673             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1674             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1675             FillRect( hdc, &rc, hbrush );\r
1676             DeleteObject(hbrush);\r
1677         }\r
1678     }\r
1679     else if( mode == 2 ) {\r
1680         /* Diagonal gradient, good more or less for every piece */\r
1681         POINT triangle[3];\r
1682         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1683         HBRUSH hbrush_old;\r
1684         int steps = squareSize;\r
1685         int i;\r
1686 \r
1687         triangle[0].x = squareSize - steps;\r
1688         triangle[0].y = squareSize;\r
1689         triangle[1].x = squareSize;\r
1690         triangle[1].y = squareSize;\r
1691         triangle[2].x = squareSize;\r
1692         triangle[2].y = squareSize - steps;\r
1693 \r
1694         for( i=0; i<steps; i++ ) {\r
1695             BYTE r = r1 - (r1-r2) * i / steps;\r
1696             BYTE g = g1 - (g1-g2) * i / steps;\r
1697             BYTE b = b1 - (b1-b2) * i / steps;\r
1698 \r
1699             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1700             hbrush_old = SelectObject( hdc, hbrush );\r
1701             Polygon( hdc, triangle, 3 );\r
1702             SelectObject( hdc, hbrush_old );\r
1703             DeleteObject(hbrush);\r
1704             triangle[0].x++;\r
1705             triangle[2].y++;\r
1706         }\r
1707 \r
1708         SelectObject( hdc, hpen );\r
1709     }\r
1710 }\r
1711 \r
1712 /*\r
1713     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1714     seems to work ok. The main problem here is to find the "inside" of a chess\r
1715     piece: follow the steps as explained below.\r
1716 */\r
1717 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1718 {\r
1719     HBITMAP hbm;\r
1720     HBITMAP hbm_old;\r
1721     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1722     RECT rc;\r
1723     SIZE sz;\r
1724     POINT pt;\r
1725     int backColor = whitePieceColor; \r
1726     int foreColor = blackPieceColor;\r
1727     \r
1728     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1729         backColor = appData.fontBackColorWhite;\r
1730         foreColor = appData.fontForeColorWhite;\r
1731     }\r
1732     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1733         backColor = appData.fontBackColorBlack;\r
1734         foreColor = appData.fontForeColorBlack;\r
1735     }\r
1736 \r
1737     /* Mask */\r
1738     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1739 \r
1740     hbm_old = SelectObject( hdc, hbm );\r
1741 \r
1742     rc.left = 0;\r
1743     rc.top = 0;\r
1744     rc.right = squareSize;\r
1745     rc.bottom = squareSize;\r
1746 \r
1747     /* Step 1: background is now black */\r
1748     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1749 \r
1750     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1751 \r
1752     pt.x = (squareSize - sz.cx) / 2;\r
1753     pt.y = (squareSize - sz.cy) / 2;\r
1754 \r
1755     SetBkMode( hdc, TRANSPARENT );\r
1756     SetTextColor( hdc, chroma );\r
1757     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1758     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1759 \r
1760     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1761     /* Step 3: the area outside the piece is filled with white */\r
1762 //    FloodFill( hdc, 0, 0, chroma );\r
1763     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1764     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1765     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1766     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1767     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1768     /* \r
1769         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1770         but if the start point is not inside the piece we're lost!\r
1771         There should be a better way to do this... if we could create a region or path\r
1772         from the fill operation we would be fine for example.\r
1773     */\r
1774 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1775     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1776 \r
1777     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1778         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1779         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1780 \r
1781         SelectObject( dc2, bm2 );\r
1782         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1783         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1784         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1785         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1786         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1787 \r
1788         DeleteDC( dc2 );\r
1789         DeleteObject( bm2 );\r
1790     }\r
1791 \r
1792     SetTextColor( hdc, 0 );\r
1793     /* \r
1794         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1795         draw the piece again in black for safety.\r
1796     */\r
1797     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1798 \r
1799     SelectObject( hdc, hbm_old );\r
1800 \r
1801     if( hPieceMask[index] != NULL ) {\r
1802         DeleteObject( hPieceMask[index] );\r
1803     }\r
1804 \r
1805     hPieceMask[index] = hbm;\r
1806 \r
1807     /* Face */\r
1808     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1809 \r
1810     SelectObject( hdc, hbm );\r
1811 \r
1812     {\r
1813         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1814         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1815         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1816 \r
1817         SelectObject( dc1, hPieceMask[index] );\r
1818         SelectObject( dc2, bm2 );\r
1819         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1820         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1821         \r
1822         /* \r
1823             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1824             the piece background and deletes (makes transparent) the rest.\r
1825             Thanks to that mask, we are free to paint the background with the greates\r
1826             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1827             We use this, to make gradients and give the pieces a "roundish" look.\r
1828         */\r
1829         SetPieceBackground( hdc, backColor, 2 );\r
1830         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1831 \r
1832         DeleteDC( dc2 );\r
1833         DeleteDC( dc1 );\r
1834         DeleteObject( bm2 );\r
1835     }\r
1836 \r
1837     SetTextColor( hdc, foreColor );\r
1838     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1839 \r
1840     SelectObject( hdc, hbm_old );\r
1841 \r
1842     if( hPieceFace[index] != NULL ) {\r
1843         DeleteObject( hPieceFace[index] );\r
1844     }\r
1845 \r
1846     hPieceFace[index] = hbm;\r
1847 }\r
1848 \r
1849 static int TranslatePieceToFontPiece( int piece )\r
1850 {\r
1851     switch( piece ) {\r
1852     case BlackPawn:\r
1853         return PM_BP;\r
1854     case BlackKnight:\r
1855         return PM_BN;\r
1856     case BlackBishop:\r
1857         return PM_BB;\r
1858     case BlackRook:\r
1859         return PM_BR;\r
1860     case BlackQueen:\r
1861         return PM_BQ;\r
1862     case BlackKing:\r
1863         return PM_BK;\r
1864     case WhitePawn:\r
1865         return PM_WP;\r
1866     case WhiteKnight:\r
1867         return PM_WN;\r
1868     case WhiteBishop:\r
1869         return PM_WB;\r
1870     case WhiteRook:\r
1871         return PM_WR;\r
1872     case WhiteQueen:\r
1873         return PM_WQ;\r
1874     case WhiteKing:\r
1875         return PM_WK;\r
1876 \r
1877     case BlackAngel:\r
1878         return PM_BA;\r
1879     case BlackMarshall:\r
1880         return PM_BC;\r
1881     case BlackFerz:\r
1882         return PM_BF;\r
1883     case BlackNightrider:\r
1884         return PM_BH;\r
1885     case BlackAlfil:\r
1886         return PM_BE;\r
1887     case BlackWazir:\r
1888         return PM_BW;\r
1889     case BlackUnicorn:\r
1890         return PM_BU;\r
1891     case BlackCannon:\r
1892         return PM_BO;\r
1893     case BlackGrasshopper:\r
1894         return PM_BG;\r
1895     case BlackMan:\r
1896         return PM_BM;\r
1897     case BlackSilver:\r
1898         return PM_BSG;\r
1899     case BlackLance:\r
1900         return PM_BL;\r
1901     case BlackFalcon:\r
1902         return PM_BV;\r
1903     case BlackCobra:\r
1904         return PM_BS;\r
1905     case BlackCardinal:\r
1906         return PM_BAB;\r
1907     case BlackDragon:\r
1908         return PM_BD;\r
1909 \r
1910     case WhiteAngel:\r
1911         return PM_WA;\r
1912     case WhiteMarshall:\r
1913         return PM_WC;\r
1914     case WhiteFerz:\r
1915         return PM_WF;\r
1916     case WhiteNightrider:\r
1917         return PM_WH;\r
1918     case WhiteAlfil:\r
1919         return PM_WE;\r
1920     case WhiteWazir:\r
1921         return PM_WW;\r
1922     case WhiteUnicorn:\r
1923         return PM_WU;\r
1924     case WhiteCannon:\r
1925         return PM_WO;\r
1926     case WhiteGrasshopper:\r
1927         return PM_WG;\r
1928     case WhiteMan:\r
1929         return PM_WM;\r
1930     case WhiteSilver:\r
1931         return PM_WSG;\r
1932     case WhiteLance:\r
1933         return PM_WL;\r
1934     case WhiteFalcon:\r
1935         return PM_WV;\r
1936     case WhiteCobra:\r
1937         return PM_WS;\r
1938     case WhiteCardinal:\r
1939         return PM_WAB;\r
1940     case WhiteDragon:\r
1941         return PM_WD;\r
1942     }\r
1943 \r
1944     return 0;\r
1945 }\r
1946 \r
1947 void CreatePiecesFromFont()\r
1948 {\r
1949     LOGFONT lf;\r
1950     HDC hdc_window = NULL;\r
1951     HDC hdc = NULL;\r
1952     HFONT hfont_old;\r
1953     int fontHeight;\r
1954     int i;\r
1955 \r
1956     if( fontBitmapSquareSize < 0 ) {\r
1957         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1958         return;\r
1959     }\r
1960 \r
1961     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1962         fontBitmapSquareSize = -1;\r
1963         return;\r
1964     }\r
1965 \r
1966     if( fontBitmapSquareSize != squareSize ) {\r
1967         hdc_window = GetDC( hwndMain );\r
1968         hdc = CreateCompatibleDC( hdc_window );\r
1969 \r
1970         if( hPieceFont != NULL ) {\r
1971             DeleteObject( hPieceFont );\r
1972         }\r
1973         else {\r
1974             for( i=0; i<=(int)BlackKing; i++ ) {\r
1975                 hPieceMask[i] = NULL;\r
1976                 hPieceFace[i] = NULL;\r
1977             }\r
1978         }\r
1979 \r
1980         fontHeight = 75;\r
1981 \r
1982         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
1983             fontHeight = appData.fontPieceSize;\r
1984         }\r
1985 \r
1986         fontHeight = (fontHeight * squareSize) / 100;\r
1987 \r
1988         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
1989         lf.lfWidth = 0;\r
1990         lf.lfEscapement = 0;\r
1991         lf.lfOrientation = 0;\r
1992         lf.lfWeight = FW_NORMAL;\r
1993         lf.lfItalic = 0;\r
1994         lf.lfUnderline = 0;\r
1995         lf.lfStrikeOut = 0;\r
1996         lf.lfCharSet = DEFAULT_CHARSET;\r
1997         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1998         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1999         lf.lfQuality = PROOF_QUALITY;\r
2000         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2001         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2002         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2003 \r
2004         hPieceFont = CreateFontIndirect( &lf );\r
2005 \r
2006         if( hPieceFont == NULL ) {\r
2007             fontBitmapSquareSize = -2;\r
2008         }\r
2009         else {\r
2010             /* Setup font-to-piece character table */\r
2011             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2012                 /* No (or wrong) global settings, try to detect the font */\r
2013                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2014                     /* Alpha */\r
2015                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2016                 }\r
2017                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2018                     /* DiagramTT* family */\r
2019                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2020                 }\r
2021                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2022                     /* Fairy symbols */\r
2023                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2024                 }\r
2025                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2026                     /* Good Companion (Some characters get warped as literal :-( */\r
2027                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2028                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2029                     SetCharTable(pieceToFontChar, s);\r
2030                 }\r
2031                 else {\r
2032                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2033                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2034                 }\r
2035             }\r
2036 \r
2037             /* Create bitmaps */\r
2038             hfont_old = SelectObject( hdc, hPieceFont );\r
2039             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2040                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2041                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2042 \r
2043             SelectObject( hdc, hfont_old );\r
2044 \r
2045             fontBitmapSquareSize = squareSize;\r
2046         }\r
2047     }\r
2048 \r
2049     if( hdc != NULL ) {\r
2050         DeleteDC( hdc );\r
2051     }\r
2052 \r
2053     if( hdc_window != NULL ) {\r
2054         ReleaseDC( hwndMain, hdc_window );\r
2055     }\r
2056 }\r
2057 \r
2058 HBITMAP\r
2059 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2060 {\r
2061   char name[128];\r
2062 \r
2063     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2064   if (gameInfo.event &&\r
2065       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2066       strcmp(name, "k80s") == 0) {\r
2067     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2068   }\r
2069   return LoadBitmap(hinst, name);\r
2070 }\r
2071 \r
2072 \r
2073 /* Insert a color into the program's logical palette\r
2074    structure.  This code assumes the given color is\r
2075    the result of the RGB or PALETTERGB macro, and it\r
2076    knows how those macros work (which is documented).\r
2077 */\r
2078 VOID\r
2079 InsertInPalette(COLORREF color)\r
2080 {\r
2081   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2082 \r
2083   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2084     DisplayFatalError(_("Too many colors"), 0, 1);\r
2085     pLogPal->palNumEntries--;\r
2086     return;\r
2087   }\r
2088 \r
2089   pe->peFlags = (char) 0;\r
2090   pe->peRed = (char) (0xFF & color);\r
2091   pe->peGreen = (char) (0xFF & (color >> 8));\r
2092   pe->peBlue = (char) (0xFF & (color >> 16));\r
2093   return;\r
2094 }\r
2095 \r
2096 \r
2097 VOID\r
2098 InitDrawingColors()\r
2099 {\r
2100   if (pLogPal == NULL) {\r
2101     /* Allocate enough memory for a logical palette with\r
2102      * PALETTESIZE entries and set the size and version fields\r
2103      * of the logical palette structure.\r
2104      */\r
2105     pLogPal = (NPLOGPALETTE)\r
2106       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2107                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2108     pLogPal->palVersion    = 0x300;\r
2109   }\r
2110   pLogPal->palNumEntries = 0;\r
2111 \r
2112   InsertInPalette(lightSquareColor);\r
2113   InsertInPalette(darkSquareColor);\r
2114   InsertInPalette(whitePieceColor);\r
2115   InsertInPalette(blackPieceColor);\r
2116   InsertInPalette(highlightSquareColor);\r
2117   InsertInPalette(premoveHighlightColor);\r
2118 \r
2119   /*  create a logical color palette according the information\r
2120    *  in the LOGPALETTE structure.\r
2121    */\r
2122   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2123 \r
2124   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2125   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2126   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2127   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2128   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2129   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2130   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2131   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2132   /* [AS] Force rendering of the font-based pieces */\r
2133   if( fontBitmapSquareSize > 0 ) {\r
2134     fontBitmapSquareSize = 0;\r
2135   }\r
2136 }\r
2137 \r
2138 \r
2139 int\r
2140 BoardWidth(int boardSize, int n)\r
2141 { /* [HGM] argument n added to allow different width and height */\r
2142   int lineGap = sizeInfo[boardSize].lineGap;\r
2143 \r
2144   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2145       lineGap = appData.overrideLineGap;\r
2146   }\r
2147 \r
2148   return (n + 1) * lineGap +\r
2149           n * sizeInfo[boardSize].squareSize;\r
2150 }\r
2151 \r
2152 /* Respond to board resize by dragging edge */\r
2153 VOID\r
2154 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2155 {\r
2156   BoardSize newSize = NUM_SIZES - 1;\r
2157   static int recurse = 0;\r
2158   if (IsIconic(hwndMain)) return;\r
2159   if (recurse > 0) return;\r
2160   recurse++;\r
2161   while (newSize > 0) {\r
2162         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2163         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2164            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2165     newSize--;\r
2166   } \r
2167   boardSize = newSize;\r
2168   InitDrawingSizes(boardSize, flags);\r
2169   recurse--;\r
2170 }\r
2171 \r
2172 \r
2173 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2174 \r
2175 VOID\r
2176 InitDrawingSizes(BoardSize boardSize, int flags)\r
2177 {\r
2178   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2179   ChessSquare piece;\r
2180   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2181   HDC hdc;\r
2182   SIZE clockSize, messageSize;\r
2183   HFONT oldFont;\r
2184   char buf[MSG_SIZ];\r
2185   char *str;\r
2186   HMENU hmenu = GetMenu(hwndMain);\r
2187   RECT crect, wrect, oldRect;\r
2188   int offby;\r
2189   LOGBRUSH logbrush;\r
2190 \r
2191   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2192   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2193 \r
2194   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2195   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2196 \r
2197   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2198   oldRect.top = wpMain.y;\r
2199   oldRect.right = wpMain.x + wpMain.width;\r
2200   oldRect.bottom = wpMain.y + wpMain.height;\r
2201 \r
2202   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2203   smallLayout = sizeInfo[boardSize].smallLayout;\r
2204   squareSize = sizeInfo[boardSize].squareSize;\r
2205   lineGap = sizeInfo[boardSize].lineGap;\r
2206   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2207 \r
2208   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2209       lineGap = appData.overrideLineGap;\r
2210   }\r
2211 \r
2212   if (tinyLayout != oldTinyLayout) {\r
2213     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
2214     if (tinyLayout) {\r
2215       style &= ~WS_SYSMENU;\r
2216       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2217                  "&Minimize\tCtrl+F4");\r
2218     } else {\r
2219       style |= WS_SYSMENU;\r
2220       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2221     }\r
2222     SetWindowLong(hwndMain, GWL_STYLE, style);\r
2223 \r
2224     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2225       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2226         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2227     }\r
2228     DrawMenuBar(hwndMain);\r
2229   }\r
2230 \r
2231   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2232   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2233 \r
2234   /* Get text area sizes */\r
2235   hdc = GetDC(hwndMain);\r
2236   if (appData.clockMode) {\r
2237     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2238   } else {\r
2239     snprintf(buf, MSG_SIZ, _("White"));\r
2240   }\r
2241   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2242   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2243   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2244   str = _("We only care about the height here");\r
2245   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2246   SelectObject(hdc, oldFont);\r
2247   ReleaseDC(hwndMain, hdc);\r
2248 \r
2249   /* Compute where everything goes */\r
2250   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2251         /* [HGM] logo: if either logo is on, reserve space for it */\r
2252         logoHeight =  2*clockSize.cy;\r
2253         leftLogoRect.left   = OUTER_MARGIN;\r
2254         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2255         leftLogoRect.top    = OUTER_MARGIN;\r
2256         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2257 \r
2258         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2259         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2260         rightLogoRect.top    = OUTER_MARGIN;\r
2261         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2262 \r
2263 \r
2264     whiteRect.left = leftLogoRect.right;\r
2265     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2266     whiteRect.top = OUTER_MARGIN;\r
2267     whiteRect.bottom = whiteRect.top + logoHeight;\r
2268 \r
2269     blackRect.right = rightLogoRect.left;\r
2270     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2271     blackRect.top = whiteRect.top;\r
2272     blackRect.bottom = whiteRect.bottom;\r
2273   } else {\r
2274     whiteRect.left = OUTER_MARGIN;\r
2275     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2276     whiteRect.top = OUTER_MARGIN;\r
2277     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2278 \r
2279     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2280     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2281     blackRect.top = whiteRect.top;\r
2282     blackRect.bottom = whiteRect.bottom;\r
2283 \r
2284     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2285   }\r
2286 \r
2287   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2288   if (appData.showButtonBar) {\r
2289     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2290       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2291   } else {\r
2292     messageRect.right = OUTER_MARGIN + boardWidth;\r
2293   }\r
2294   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2295   messageRect.bottom = messageRect.top + messageSize.cy;\r
2296 \r
2297   boardRect.left = OUTER_MARGIN;\r
2298   boardRect.right = boardRect.left + boardWidth;\r
2299   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2300   boardRect.bottom = boardRect.top + boardHeight;\r
2301 \r
2302   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2303   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2304   oldBoardSize = boardSize;\r
2305   oldTinyLayout = tinyLayout;\r
2306   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2307   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2308     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2309   winW *= 1 + twoBoards;\r
2310   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2311   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2312   wpMain.height = winH; //       without disturbing window attachments\r
2313   GetWindowRect(hwndMain, &wrect);\r
2314   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2315                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2316 \r
2317   // [HGM] placement: let attached windows follow size change.\r
2318   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2319   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2320   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2321   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2322   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2323 \r
2324   /* compensate if menu bar wrapped */\r
2325   GetClientRect(hwndMain, &crect);\r
2326   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2327   wpMain.height += offby;\r
2328   switch (flags) {\r
2329   case WMSZ_TOPLEFT:\r
2330     SetWindowPos(hwndMain, NULL, \r
2331                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2332                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2333     break;\r
2334 \r
2335   case WMSZ_TOPRIGHT:\r
2336   case WMSZ_TOP:\r
2337     SetWindowPos(hwndMain, NULL, \r
2338                  wrect.left, wrect.bottom - wpMain.height, \r
2339                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2340     break;\r
2341 \r
2342   case WMSZ_BOTTOMLEFT:\r
2343   case WMSZ_LEFT:\r
2344     SetWindowPos(hwndMain, NULL, \r
2345                  wrect.right - wpMain.width, wrect.top, \r
2346                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2347     break;\r
2348 \r
2349   case WMSZ_BOTTOMRIGHT:\r
2350   case WMSZ_BOTTOM:\r
2351   case WMSZ_RIGHT:\r
2352   default:\r
2353     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2354                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2355     break;\r
2356   }\r
2357 \r
2358   hwndPause = NULL;\r
2359   for (i = 0; i < N_BUTTONS; i++) {\r
2360     if (buttonDesc[i].hwnd != NULL) {\r
2361       DestroyWindow(buttonDesc[i].hwnd);\r
2362       buttonDesc[i].hwnd = NULL;\r
2363     }\r
2364     if (appData.showButtonBar) {\r
2365       buttonDesc[i].hwnd =\r
2366         CreateWindow("BUTTON", buttonDesc[i].label,\r
2367                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2368                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2369                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2370                      (HMENU) buttonDesc[i].id,\r
2371                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
2372       if (tinyLayout) {\r
2373         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2374                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2375                     MAKELPARAM(FALSE, 0));\r
2376       }\r
2377       if (buttonDesc[i].id == IDM_Pause)\r
2378         hwndPause = buttonDesc[i].hwnd;\r
2379       buttonDesc[i].wndproc = (WNDPROC)\r
2380         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
2381     }\r
2382   }\r
2383   if (gridPen != NULL) DeleteObject(gridPen);\r
2384   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2385   if (premovePen != NULL) DeleteObject(premovePen);\r
2386   if (lineGap != 0) {\r
2387     logbrush.lbStyle = BS_SOLID;\r
2388     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2389     gridPen =\r
2390       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2391                    lineGap, &logbrush, 0, NULL);\r
2392     logbrush.lbColor = highlightSquareColor;\r
2393     highlightPen =\r
2394       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2395                    lineGap, &logbrush, 0, NULL);\r
2396 \r
2397     logbrush.lbColor = premoveHighlightColor; \r
2398     premovePen =\r
2399       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2400                    lineGap, &logbrush, 0, NULL);\r
2401 \r
2402     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2403     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2404       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2405       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2406         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2407       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2408         BOARD_WIDTH * (squareSize + lineGap);\r
2409       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2410     }\r
2411     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2412       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2413       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2414         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2415         lineGap / 2 + (i * (squareSize + lineGap));\r
2416       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2417         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2418       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2419     }\r
2420   }\r
2421 \r
2422   /* [HGM] Licensing requirement */\r
2423 #ifdef GOTHIC\r
2424   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2425 #endif\r
2426 #ifdef FALCON\r
2427   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2428 #endif\r
2429   GothicPopUp( "", VariantNormal);\r
2430 \r
2431 \r
2432 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2433 \r
2434   /* Load piece bitmaps for this board size */\r
2435   for (i=0; i<=2; i++) {\r
2436     for (piece = WhitePawn;\r
2437          (int) piece < (int) BlackPawn;\r
2438          piece = (ChessSquare) ((int) piece + 1)) {\r
2439       if (pieceBitmap[i][piece] != NULL)\r
2440         DeleteObject(pieceBitmap[i][piece]);\r
2441     }\r
2442   }\r
2443 \r
2444   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2445   // Orthodox Chess pieces\r
2446   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2447   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2448   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2449   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2450   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2451   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2452   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2453   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2454   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2455   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2456   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2457   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2458   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2459   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2460   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2461   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2462     // in Shogi, Hijack the unused Queen for Lance\r
2463     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2464     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2465     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2466   } else {\r
2467     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2468     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2469     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2470   }\r
2471 \r
2472   if(squareSize <= 72 && squareSize >= 33) { \r
2473     /* A & C are available in most sizes now */\r
2474     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2475       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2476       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2477       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2478       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2479       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2480       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2481       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2482       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2483       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2484       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2485       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2486       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2487     } else { // Smirf-like\r
2488       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2489       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2490       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2491     }\r
2492     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2493       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2494       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2495       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2496     } else { // WinBoard standard\r
2497       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2498       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2499       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2500     }\r
2501   }\r
2502 \r
2503 \r
2504   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2505     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2506     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2507     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2508     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2509     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2510     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2511     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2512     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2513     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2514     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2515     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2516     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2517     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2518     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2519     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2520     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2521     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2522     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2523     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2524     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2525     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2526     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2527     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2528     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2529     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2530     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2531     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2532     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2533     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2534     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2535 \r
2536     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2537       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2538       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2539       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2540       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2541       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2542       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2543       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2544       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2545       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2546       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2547       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2548       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2549     } else {\r
2550       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2551       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2552       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2553       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2554       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2555       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2556       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2557       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2558       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2559       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2560       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2561       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2562     }\r
2563 \r
2564   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2565     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2566     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2567     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2568     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2569     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2570     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2571     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2572     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2573     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2574     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2575     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2576     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2577     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2578     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2579   }\r
2580 \r
2581 \r
2582   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2583   /* special Shogi support in this size */\r
2584   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2585       for (piece = WhitePawn;\r
2586            (int) piece < (int) BlackPawn;\r
2587            piece = (ChessSquare) ((int) piece + 1)) {\r
2588         if (pieceBitmap[i][piece] != NULL)\r
2589           DeleteObject(pieceBitmap[i][piece]);\r
2590       }\r
2591     }\r
2592   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2593   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2594   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2595   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2596   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2597   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2598   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2599   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2600   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2601   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2602   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2603   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2604   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2605   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2606   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2607   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2608   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2609   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2610   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2611   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2612   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2613   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2614   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2615   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2616   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2617   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2618   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2619   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2620   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2621   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2622   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2623   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2624   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2625   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2626   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2627   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2628   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2629   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2630   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2631   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2632   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2633   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2634   minorSize = 0;\r
2635   }\r
2636 }\r
2637 \r
2638 HBITMAP\r
2639 PieceBitmap(ChessSquare p, int kind)\r
2640 {\r
2641   if ((int) p >= (int) BlackPawn)\r
2642     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2643 \r
2644   return pieceBitmap[kind][(int) p];\r
2645 }\r
2646 \r
2647 /***************************************************************/\r
2648 \r
2649 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2650 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2651 /*\r
2652 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2653 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2654 */\r
2655 \r
2656 VOID\r
2657 SquareToPos(int row, int column, int * x, int * y)\r
2658 {\r
2659   if (flipView) {\r
2660     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2661     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2662   } else {\r
2663     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2664     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2665   }\r
2666 }\r
2667 \r
2668 VOID\r
2669 DrawCoordsOnDC(HDC hdc)\r
2670 {\r
2671   static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'};\r
2672   static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'};\r
2673   char str[2] = { NULLCHAR, NULLCHAR };\r
2674   int oldMode, oldAlign, x, y, start, i;\r
2675   HFONT oldFont;\r
2676   HBRUSH oldBrush;\r
2677 \r
2678   if (!appData.showCoords)\r
2679     return;\r
2680 \r
2681   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2682 \r
2683   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2684   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2685   oldAlign = GetTextAlign(hdc);\r
2686   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2687 \r
2688   y = boardRect.top + lineGap;\r
2689   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2690 \r
2691   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2692   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2693     str[0] = files[start + i];\r
2694     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2695     y += squareSize + lineGap;\r
2696   }\r
2697 \r
2698   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2699 \r
2700   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2701   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2702     str[0] = ranks[start + i];\r
2703     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2704     x += squareSize + lineGap;\r
2705   }    \r
2706 \r
2707   SelectObject(hdc, oldBrush);\r
2708   SetBkMode(hdc, oldMode);\r
2709   SetTextAlign(hdc, oldAlign);\r
2710   SelectObject(hdc, oldFont);\r
2711 }\r
2712 \r
2713 VOID\r
2714 DrawGridOnDC(HDC hdc)\r
2715 {\r
2716   HPEN oldPen;\r
2717  \r
2718   if (lineGap != 0) {\r
2719     oldPen = SelectObject(hdc, gridPen);\r
2720     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2721     SelectObject(hdc, oldPen);\r
2722   }\r
2723 }\r
2724 \r
2725 #define HIGHLIGHT_PEN 0\r
2726 #define PREMOVE_PEN   1\r
2727 \r
2728 VOID\r
2729 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2730 {\r
2731   int x1, y1;\r
2732   HPEN oldPen, hPen;\r
2733   if (lineGap == 0) return;\r
2734   if (flipView) {\r
2735     x1 = boardRect.left +\r
2736       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2737     y1 = boardRect.top +\r
2738       lineGap/2 + y * (squareSize + lineGap);\r
2739   } else {\r
2740     x1 = boardRect.left +\r
2741       lineGap/2 + x * (squareSize + lineGap);\r
2742     y1 = boardRect.top +\r
2743       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2744   }\r
2745   hPen = pen ? premovePen : highlightPen;\r
2746   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2747   MoveToEx(hdc, x1, y1, NULL);\r
2748   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2749   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2750   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2751   LineTo(hdc, x1, y1);\r
2752   SelectObject(hdc, oldPen);\r
2753 }\r
2754 \r
2755 VOID\r
2756 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2757 {\r
2758   int i;\r
2759   for (i=0; i<2; i++) {\r
2760     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2761       DrawHighlightOnDC(hdc, TRUE,\r
2762                         h->sq[i].x, h->sq[i].y,\r
2763                         pen);\r
2764   }\r
2765 }\r
2766 \r
2767 /* Note: sqcolor is used only in monoMode */\r
2768 /* Note that this code is largely duplicated in woptions.c,\r
2769    function DrawSampleSquare, so that needs to be updated too */\r
2770 VOID\r
2771 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2772 {\r
2773   HBITMAP oldBitmap;\r
2774   HBRUSH oldBrush;\r
2775   int tmpSize;\r
2776 \r
2777   if (appData.blindfold) return;\r
2778 \r
2779   /* [AS] Use font-based pieces if needed */\r
2780   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2781     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2782     CreatePiecesFromFont();\r
2783 \r
2784     if( fontBitmapSquareSize == squareSize ) {\r
2785         int index = TranslatePieceToFontPiece(piece);\r
2786 \r
2787         SelectObject( tmphdc, hPieceMask[ index ] );\r
2788 \r
2789       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2790         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2791       else\r
2792         BitBlt( hdc,\r
2793             x, y,\r
2794             squareSize, squareSize,\r
2795             tmphdc,\r
2796             0, 0,\r
2797             SRCAND );\r
2798 \r
2799         SelectObject( tmphdc, hPieceFace[ index ] );\r
2800 \r
2801       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2802         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2803       else\r
2804         BitBlt( hdc,\r
2805             x, y,\r
2806             squareSize, squareSize,\r
2807             tmphdc,\r
2808             0, 0,\r
2809             SRCPAINT );\r
2810 \r
2811         return;\r
2812     }\r
2813   }\r
2814 \r
2815   if (appData.monoMode) {\r
2816     SelectObject(tmphdc, PieceBitmap(piece, \r
2817       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2818     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2819            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2820   } else {\r
2821     tmpSize = squareSize;\r
2822     if(minorSize &&\r
2823         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2824          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2825       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2826       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2827       x += (squareSize - minorSize)>>1;\r
2828       y += squareSize - minorSize - 2;\r
2829       tmpSize = minorSize;\r
2830     }\r
2831     if (color || appData.allWhite ) {\r
2832       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2833       if( color )\r
2834               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2835       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2836       if(appData.upsideDown && color==flipView)\r
2837         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2838       else\r
2839         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2840       /* Use black for outline of white pieces */\r
2841       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2842       if(appData.upsideDown && color==flipView)\r
2843         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2844       else\r
2845         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2846     } else {\r
2847       /* Use square color for details of black pieces */\r
2848       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2849       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2850       if(appData.upsideDown && !flipView)\r
2851         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2852       else\r
2853         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2854     }\r
2855     SelectObject(hdc, oldBrush);\r
2856     SelectObject(tmphdc, oldBitmap);\r
2857   }\r
2858 }\r
2859 \r
2860 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2861 int GetBackTextureMode( int algo )\r
2862 {\r
2863     int result = BACK_TEXTURE_MODE_DISABLED;\r
2864 \r
2865     switch( algo ) \r
2866     {\r
2867         case BACK_TEXTURE_MODE_PLAIN:\r
2868             result = 1; /* Always use identity map */\r
2869             break;\r
2870         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2871             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2872             break;\r
2873     }\r
2874 \r
2875     return result;\r
2876 }\r
2877 \r
2878 /* \r
2879     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2880     to handle redraws cleanly (as random numbers would always be different).\r
2881 */\r
2882 VOID RebuildTextureSquareInfo()\r
2883 {\r
2884     BITMAP bi;\r
2885     int lite_w = 0;\r
2886     int lite_h = 0;\r
2887     int dark_w = 0;\r
2888     int dark_h = 0;\r
2889     int row;\r
2890     int col;\r
2891 \r
2892     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2893 \r
2894     if( liteBackTexture != NULL ) {\r
2895         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2896             lite_w = bi.bmWidth;\r
2897             lite_h = bi.bmHeight;\r
2898         }\r
2899     }\r
2900 \r
2901     if( darkBackTexture != NULL ) {\r
2902         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2903             dark_w = bi.bmWidth;\r
2904             dark_h = bi.bmHeight;\r
2905         }\r
2906     }\r
2907 \r
2908     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2909         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2910             if( (col + row) & 1 ) {\r
2911                 /* Lite square */\r
2912                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2913                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2914                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2915                   else\r
2916                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2917                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2918                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2919                   else\r
2920                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2921                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2922                 }\r
2923             }\r
2924             else {\r
2925                 /* Dark square */\r
2926                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2927                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2928                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2929                   else\r
2930                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2931                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2932                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2933                   else\r
2934                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2935                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2936                 }\r
2937             }\r
2938         }\r
2939     }\r
2940 }\r
2941 \r
2942 /* [AS] Arrow highlighting support */\r
2943 \r
2944 static int A_WIDTH = 5; /* Width of arrow body */\r
2945 \r
2946 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2947 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2948 \r
2949 static double Sqr( double x )\r
2950 {\r
2951     return x*x;\r
2952 }\r
2953 \r
2954 static int Round( double x )\r
2955 {\r
2956     return (int) (x + 0.5);\r
2957 }\r
2958 \r
2959 /* Draw an arrow between two points using current settings */\r
2960 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2961 {\r
2962     POINT arrow[7];\r
2963     double dx, dy, j, k, x, y;\r
2964 \r
2965     if( d_x == s_x ) {\r
2966         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2967 \r
2968         arrow[0].x = s_x + A_WIDTH;\r
2969         arrow[0].y = s_y;\r
2970 \r
2971         arrow[1].x = s_x + A_WIDTH;\r
2972         arrow[1].y = d_y - h;\r
2973 \r
2974         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
2975         arrow[2].y = d_y - h;\r
2976 \r
2977         arrow[3].x = d_x;\r
2978         arrow[3].y = d_y;\r
2979 \r
2980         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
2981         arrow[4].y = d_y - h;\r
2982 \r
2983         arrow[5].x = s_x - A_WIDTH;\r
2984         arrow[5].y = d_y - h;\r
2985 \r
2986         arrow[6].x = s_x - A_WIDTH;\r
2987         arrow[6].y = s_y;\r
2988     }\r
2989     else if( d_y == s_y ) {\r
2990         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2991 \r
2992         arrow[0].x = s_x;\r
2993         arrow[0].y = s_y + A_WIDTH;\r
2994 \r
2995         arrow[1].x = d_x - w;\r
2996         arrow[1].y = s_y + A_WIDTH;\r
2997 \r
2998         arrow[2].x = d_x - w;\r
2999         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3000 \r
3001         arrow[3].x = d_x;\r
3002         arrow[3].y = d_y;\r
3003 \r
3004         arrow[4].x = d_x - w;\r
3005         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3006 \r
3007         arrow[5].x = d_x - w;\r
3008         arrow[5].y = s_y - A_WIDTH;\r
3009 \r
3010         arrow[6].x = s_x;\r
3011         arrow[6].y = s_y - A_WIDTH;\r
3012     }\r
3013     else {\r
3014         /* [AS] Needed a lot of paper for this! :-) */\r
3015         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3016         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3017   \r
3018         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3019 \r
3020         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3021 \r
3022         x = s_x;\r
3023         y = s_y;\r
3024 \r
3025         arrow[0].x = Round(x - j);\r
3026         arrow[0].y = Round(y + j*dx);\r
3027 \r
3028         arrow[1].x = Round(x + j);\r
3029         arrow[1].y = Round(y - j*dx);\r
3030 \r
3031         if( d_x > s_x ) {\r
3032             x = (double) d_x - k;\r
3033             y = (double) d_y - k*dy;\r
3034         }\r
3035         else {\r
3036             x = (double) d_x + k;\r
3037             y = (double) d_y + k*dy;\r
3038         }\r
3039 \r
3040         arrow[2].x = Round(x + j);\r
3041         arrow[2].y = Round(y - j*dx);\r
3042 \r
3043         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3044         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3045 \r
3046         arrow[4].x = d_x;\r
3047         arrow[4].y = d_y;\r
3048 \r
3049         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3050         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3051 \r
3052         arrow[6].x = Round(x - j);\r
3053         arrow[6].y = Round(y + j*dx);\r
3054     }\r
3055 \r
3056     Polygon( hdc, arrow, 7 );\r
3057 }\r
3058 \r
3059 /* [AS] Draw an arrow between two squares */\r
3060 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3061 {\r
3062     int s_x, s_y, d_x, d_y;\r
3063     HPEN hpen;\r
3064     HPEN holdpen;\r
3065     HBRUSH hbrush;\r
3066     HBRUSH holdbrush;\r
3067     LOGBRUSH stLB;\r
3068 \r
3069     if( s_col == d_col && s_row == d_row ) {\r
3070         return;\r
3071     }\r
3072 \r
3073     /* Get source and destination points */\r
3074     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3075     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3076 \r
3077     if( d_y > s_y ) {\r
3078         d_y += squareSize / 4;\r
3079     }\r
3080     else if( d_y < s_y ) {\r
3081         d_y += 3 * squareSize / 4;\r
3082     }\r
3083     else {\r
3084         d_y += squareSize / 2;\r
3085     }\r
3086 \r
3087     if( d_x > s_x ) {\r
3088         d_x += squareSize / 4;\r
3089     }\r
3090     else if( d_x < s_x ) {\r
3091         d_x += 3 * squareSize / 4;\r
3092     }\r
3093     else {\r
3094         d_x += squareSize / 2;\r
3095     }\r
3096 \r
3097     s_x += squareSize / 2;\r
3098     s_y += squareSize / 2;\r
3099 \r
3100     /* Adjust width */\r
3101     A_WIDTH = squareSize / 14;\r
3102 \r
3103     /* Draw */\r
3104     stLB.lbStyle = BS_SOLID;\r
3105     stLB.lbColor = appData.highlightArrowColor;\r
3106     stLB.lbHatch = 0;\r
3107 \r
3108     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3109     holdpen = SelectObject( hdc, hpen );\r
3110     hbrush = CreateBrushIndirect( &stLB );\r
3111     holdbrush = SelectObject( hdc, hbrush );\r
3112 \r
3113     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3114 \r
3115     SelectObject( hdc, holdpen );\r
3116     SelectObject( hdc, holdbrush );\r
3117     DeleteObject( hpen );\r
3118     DeleteObject( hbrush );\r
3119 }\r
3120 \r
3121 BOOL HasHighlightInfo()\r
3122 {\r
3123     BOOL result = FALSE;\r
3124 \r
3125     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3126         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3127     {\r
3128         result = TRUE;\r
3129     }\r
3130 \r
3131     return result;\r
3132 }\r
3133 \r
3134 BOOL IsDrawArrowEnabled()\r
3135 {\r
3136     BOOL result = FALSE;\r
3137 \r
3138     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3139         result = TRUE;\r
3140     }\r
3141 \r
3142     return result;\r
3143 }\r
3144 \r
3145 VOID DrawArrowHighlight( HDC hdc )\r
3146 {\r
3147     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3148         DrawArrowBetweenSquares( hdc,\r
3149             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3150             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3151     }\r
3152 }\r
3153 \r
3154 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3155 {\r
3156     HRGN result = NULL;\r
3157 \r
3158     if( HasHighlightInfo() ) {\r
3159         int x1, y1, x2, y2;\r
3160         int sx, sy, dx, dy;\r
3161 \r
3162         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3163         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3164 \r
3165         sx = MIN( x1, x2 );\r
3166         sy = MIN( y1, y2 );\r
3167         dx = MAX( x1, x2 ) + squareSize;\r
3168         dy = MAX( y1, y2 ) + squareSize;\r
3169 \r
3170         result = CreateRectRgn( sx, sy, dx, dy );\r
3171     }\r
3172 \r
3173     return result;\r
3174 }\r
3175 \r
3176 /*\r
3177     Warning: this function modifies the behavior of several other functions. \r
3178     \r
3179     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3180     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3181     repaint is scattered all over the place, which is not good for features such as\r
3182     "arrow highlighting" that require a full repaint of the board.\r
3183 \r
3184     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3185     user interaction, when speed is not so important) but especially to avoid errors\r
3186     in the displayed graphics.\r
3187 \r
3188     In such patched places, I always try refer to this function so there is a single\r
3189     place to maintain knowledge.\r
3190     \r
3191     To restore the original behavior, just return FALSE unconditionally.\r
3192 */\r
3193 BOOL IsFullRepaintPreferrable()\r
3194 {\r
3195     BOOL result = FALSE;\r
3196 \r
3197     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3198         /* Arrow may appear on the board */\r
3199         result = TRUE;\r
3200     }\r
3201 \r
3202     return result;\r
3203 }\r
3204 \r
3205 /* \r
3206     This function is called by DrawPosition to know whether a full repaint must\r
3207     be forced or not.\r
3208 \r
3209     Only DrawPosition may directly call this function, which makes use of \r
3210     some state information. Other function should call DrawPosition specifying \r
3211     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3212 */\r
3213 BOOL DrawPositionNeedsFullRepaint()\r
3214 {\r
3215     BOOL result = FALSE;\r
3216 \r
3217     /* \r
3218         Probably a slightly better policy would be to trigger a full repaint\r
3219         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3220         but animation is fast enough that it's difficult to notice.\r
3221     */\r
3222     if( animInfo.piece == EmptySquare ) {\r
3223         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3224             result = TRUE;\r
3225         }\r
3226     }\r
3227 \r
3228     return result;\r
3229 }\r
3230 \r
3231 VOID\r
3232 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3233 {\r
3234   int row, column, x, y, square_color, piece_color;\r
3235   ChessSquare piece;\r
3236   HBRUSH oldBrush;\r
3237   HDC texture_hdc = NULL;\r
3238 \r
3239   /* [AS] Initialize background textures if needed */\r
3240   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3241       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3242       if( backTextureSquareSize != squareSize \r
3243        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3244           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3245           backTextureSquareSize = squareSize;\r
3246           RebuildTextureSquareInfo();\r
3247       }\r
3248 \r
3249       texture_hdc = CreateCompatibleDC( hdc );\r
3250   }\r
3251 \r
3252   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3253     for (column = 0; column < BOARD_WIDTH; column++) {\r
3254   \r
3255       SquareToPos(row, column, &x, &y);\r
3256 \r
3257       piece = board[row][column];\r
3258 \r
3259       square_color = ((column + row) % 2) == 1;\r
3260       if( gameInfo.variant == VariantXiangqi ) {\r
3261           square_color = !InPalace(row, column);\r
3262           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3263           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3264       }\r
3265       piece_color = (int) piece < (int) BlackPawn;\r
3266 \r
3267 \r
3268       /* [HGM] holdings file: light square or black */\r
3269       if(column == BOARD_LEFT-2) {\r
3270             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3271                 square_color = 1;\r
3272             else {\r
3273                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3274                 continue;\r
3275             }\r
3276       } else\r
3277       if(column == BOARD_RGHT + 1 ) {\r
3278             if( row < gameInfo.holdingsSize )\r
3279                 square_color = 1;\r
3280             else {\r
3281                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3282                 continue;\r
3283             }\r
3284       }\r
3285       if(column == BOARD_LEFT-1 ) /* left align */\r
3286             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3287       else if( column == BOARD_RGHT) /* right align */\r
3288             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3289       else\r
3290       if (appData.monoMode) {\r
3291         if (piece == EmptySquare) {\r
3292           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3293                  square_color ? WHITENESS : BLACKNESS);\r
3294         } else {\r
3295           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3296         }\r
3297       } \r
3298       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3299           /* [AS] Draw the square using a texture bitmap */\r
3300           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3301           int r = row, c = column; // [HGM] do not flip board in flipView\r
3302           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3303 \r
3304           DrawTile( x, y, \r
3305               squareSize, squareSize, \r
3306               hdc, \r
3307               texture_hdc,\r
3308               backTextureSquareInfo[r][c].mode,\r
3309               backTextureSquareInfo[r][c].x,\r
3310               backTextureSquareInfo[r][c].y );\r
3311 \r
3312           SelectObject( texture_hdc, hbm );\r
3313 \r
3314           if (piece != EmptySquare) {\r
3315               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3316           }\r
3317       }\r
3318       else {\r
3319         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3320 \r
3321         oldBrush = SelectObject(hdc, brush );\r
3322         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3323         SelectObject(hdc, oldBrush);\r
3324         if (piece != EmptySquare)\r
3325           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3326       }\r
3327     }\r
3328   }\r
3329 \r
3330   if( texture_hdc != NULL ) {\r
3331     DeleteDC( texture_hdc );\r
3332   }\r
3333 }\r
3334 \r
3335 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3336 void fputDW(FILE *f, int x)\r
3337 {\r
3338         fputc(x     & 255, f);\r
3339         fputc(x>>8  & 255, f);\r
3340         fputc(x>>16 & 255, f);\r
3341         fputc(x>>24 & 255, f);\r
3342 }\r
3343 \r
3344 #define MAX_CLIPS 200   /* more than enough */\r
3345 \r
3346 VOID\r
3347 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3348 {\r
3349 //  HBITMAP bufferBitmap;\r
3350   BITMAP bi;\r
3351 //  RECT Rect;\r
3352   HDC tmphdc;\r
3353   HBITMAP hbm;\r
3354   int w = 100, h = 50;\r
3355 \r
3356   if(logo == NULL) return;\r
3357 //  GetClientRect(hwndMain, &Rect);\r
3358 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3359 //                                      Rect.bottom-Rect.top+1);\r
3360   tmphdc = CreateCompatibleDC(hdc);\r
3361   hbm = SelectObject(tmphdc, logo);\r
3362   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3363             w = bi.bmWidth;\r
3364             h = bi.bmHeight;\r
3365   }\r
3366   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3367                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3368   SelectObject(tmphdc, hbm);\r
3369   DeleteDC(tmphdc);\r
3370 }\r
3371 \r
3372 static HDC hdcSeek;\r
3373 \r
3374 // [HGM] seekgraph\r
3375 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3376 {\r
3377     POINT stPt;\r
3378     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3379     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3380     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3381     SelectObject( hdcSeek, hp );\r
3382 }\r
3383 \r
3384 // front-end wrapper for drawing functions to do rectangles\r
3385 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3386 {\r
3387     HPEN hp;\r
3388     RECT rc;\r
3389 \r
3390     if (hdcSeek == NULL) {\r
3391     hdcSeek = GetDC(hwndMain);\r
3392       if (!appData.monoMode) {\r
3393         SelectPalette(hdcSeek, hPal, FALSE);\r
3394         RealizePalette(hdcSeek);\r
3395       }\r
3396     }\r
3397     hp = SelectObject( hdcSeek, gridPen );\r
3398     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3399     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3400     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3401     SelectObject( hdcSeek, hp );\r
3402 }\r
3403 \r
3404 // front-end wrapper for putting text in graph\r
3405 void DrawSeekText(char *buf, int x, int y)\r
3406 {\r
3407         SIZE stSize;\r
3408         SetBkMode( hdcSeek, TRANSPARENT );\r
3409         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3410         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3411 }\r
3412 \r
3413 void DrawSeekDot(int x, int y, int color)\r
3414 {\r
3415         int square = color & 0x80;\r
3416         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3417                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3418         color &= 0x7F;\r
3419         if(square)\r
3420             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3421                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3422         else\r
3423             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3424                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3425             SelectObject(hdcSeek, oldBrush);\r
3426 }\r
3427 \r
3428 VOID\r
3429 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3430 {\r
3431   static Board lastReq[2], lastDrawn[2];\r
3432   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3433   static int lastDrawnFlipView = 0;\r
3434   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3435   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3436   HDC tmphdc;\r
3437   HDC hdcmem;\r
3438   HBITMAP bufferBitmap;\r
3439   HBITMAP oldBitmap;\r
3440   RECT Rect;\r
3441   HRGN clips[MAX_CLIPS];\r
3442   ChessSquare dragged_piece = EmptySquare;\r
3443   int nr = twoBoards*partnerUp;\r
3444 \r
3445   /* I'm undecided on this - this function figures out whether a full\r
3446    * repaint is necessary on its own, so there's no real reason to have the\r
3447    * caller tell it that.  I think this can safely be set to FALSE - but\r
3448    * if we trust the callers not to request full repaints unnessesarily, then\r
3449    * we could skip some clipping work.  In other words, only request a full\r
3450    * redraw when the majority of pieces have changed positions (ie. flip, \r
3451    * gamestart and similar)  --Hawk\r
3452    */\r
3453   Boolean fullrepaint = repaint;\r
3454 \r
3455   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3456 \r
3457   if( DrawPositionNeedsFullRepaint() ) {\r
3458       fullrepaint = TRUE;\r
3459   }\r
3460 \r
3461   if (board == NULL) {\r
3462     if (!lastReqValid[nr]) {\r
3463       return;\r
3464     }\r
3465     board = lastReq[nr];\r
3466   } else {\r
3467     CopyBoard(lastReq[nr], board);\r
3468     lastReqValid[nr] = 1;\r
3469   }\r
3470 \r
3471   if (doingSizing) {\r
3472     return;\r
3473   }\r
3474 \r
3475   if (IsIconic(hwndMain)) {\r
3476     return;\r
3477   }\r
3478 \r
3479   if (hdc == NULL) {\r
3480     hdc = GetDC(hwndMain);\r
3481     if (!appData.monoMode) {\r
3482       SelectPalette(hdc, hPal, FALSE);\r
3483       RealizePalette(hdc);\r
3484     }\r
3485     releaseDC = TRUE;\r
3486   } else {\r
3487     releaseDC = FALSE;\r
3488   }\r
3489 \r
3490   /* Create some work-DCs */\r
3491   hdcmem = CreateCompatibleDC(hdc);\r
3492   tmphdc = CreateCompatibleDC(hdc);\r
3493 \r
3494   /* If dragging is in progress, we temporarely remove the piece */\r
3495   /* [HGM] or temporarily decrease count if stacked              */\r
3496   /*       !! Moved to before board compare !!                   */\r
3497   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3498     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3499     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3500             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3501         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3502     } else \r
3503     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3504             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3505         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3506     } else \r
3507         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3508   }\r
3509 \r
3510   /* Figure out which squares need updating by comparing the \r
3511    * newest board with the last drawn board and checking if\r
3512    * flipping has changed.\r
3513    */\r
3514   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3515     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3516       for (column = 0; column < BOARD_WIDTH; column++) {\r
3517         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3518           SquareToPos(row, column, &x, &y);\r
3519           clips[num_clips++] =\r
3520             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3521         }\r
3522       }\r
3523     }\r
3524    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3525     for (i=0; i<2; i++) {\r
3526       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3527           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3528         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3529             lastDrawnHighlight.sq[i].y >= 0) {\r
3530           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3531                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3532           clips[num_clips++] =\r
3533             CreateRectRgn(x - lineGap, y - lineGap, \r
3534                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3535         }\r
3536         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3537           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3538           clips[num_clips++] =\r
3539             CreateRectRgn(x - lineGap, y - lineGap, \r
3540                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3541         }\r
3542       }\r
3543     }\r
3544     for (i=0; i<2; i++) {\r
3545       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3546           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3547         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3548             lastDrawnPremove.sq[i].y >= 0) {\r
3549           SquareToPos(lastDrawnPremove.sq[i].y,\r
3550                       lastDrawnPremove.sq[i].x, &x, &y);\r
3551           clips[num_clips++] =\r
3552             CreateRectRgn(x - lineGap, y - lineGap, \r
3553                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3554         }\r
3555         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3556             premoveHighlightInfo.sq[i].y >= 0) {\r
3557           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3558                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3559           clips[num_clips++] =\r
3560             CreateRectRgn(x - lineGap, y - lineGap, \r
3561                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3562         }\r
3563       }\r
3564     }\r
3565    } else { // nr == 1\r
3566         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3567         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3568         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3569         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3570       for (i=0; i<2; i++) {\r
3571         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3572             partnerHighlightInfo.sq[i].y >= 0) {\r
3573           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3574                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3575           clips[num_clips++] =\r
3576             CreateRectRgn(x - lineGap, y - lineGap, \r
3577                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3578         }\r
3579         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3580             oldPartnerHighlight.sq[i].y >= 0) {\r
3581           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3582                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3583           clips[num_clips++] =\r
3584             CreateRectRgn(x - lineGap, y - lineGap, \r
3585                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3586         }\r
3587       }\r
3588    }\r
3589   } else {\r
3590     fullrepaint = TRUE;\r
3591   }\r
3592 \r
3593   /* Create a buffer bitmap - this is the actual bitmap\r
3594    * being written to.  When all the work is done, we can\r
3595    * copy it to the real DC (the screen).  This avoids\r
3596    * the problems with flickering.\r
3597    */\r
3598   GetClientRect(hwndMain, &Rect);\r
3599   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3600                                         Rect.bottom-Rect.top+1);\r
3601   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3602   if (!appData.monoMode) {\r
3603     SelectPalette(hdcmem, hPal, FALSE);\r
3604   }\r
3605 \r
3606   /* Create clips for dragging */\r
3607   if (!fullrepaint) {\r
3608     if (dragInfo.from.x >= 0) {\r
3609       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3610       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3611     }\r
3612     if (dragInfo.start.x >= 0) {\r
3613       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3614       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3615     }\r
3616     if (dragInfo.pos.x >= 0) {\r
3617       x = dragInfo.pos.x - squareSize / 2;\r
3618       y = dragInfo.pos.y - squareSize / 2;\r
3619       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3620     }\r
3621     if (dragInfo.lastpos.x >= 0) {\r
3622       x = dragInfo.lastpos.x - squareSize / 2;\r
3623       y = dragInfo.lastpos.y - squareSize / 2;\r
3624       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3625     }\r
3626   }\r
3627 \r
3628   /* Are we animating a move?  \r
3629    * If so, \r
3630    *   - remove the piece from the board (temporarely)\r
3631    *   - calculate the clipping region\r
3632    */\r
3633   if (!fullrepaint) {\r
3634     if (animInfo.piece != EmptySquare) {\r
3635       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3636       x = boardRect.left + animInfo.lastpos.x;\r
3637       y = boardRect.top + animInfo.lastpos.y;\r
3638       x2 = boardRect.left + animInfo.pos.x;\r
3639       y2 = boardRect.top + animInfo.pos.y;\r
3640       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3641       /* Slight kludge.  The real problem is that after AnimateMove is\r
3642          done, the position on the screen does not match lastDrawn.\r
3643          This currently causes trouble only on e.p. captures in\r
3644          atomic, where the piece moves to an empty square and then\r
3645          explodes.  The old and new positions both had an empty square\r
3646          at the destination, but animation has drawn a piece there and\r
3647          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3648       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3649     }\r
3650   }\r
3651 \r
3652   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3653   if (num_clips == 0)\r
3654     fullrepaint = TRUE;\r
3655 \r
3656   /* Set clipping on the memory DC */\r
3657   if (!fullrepaint) {\r
3658     SelectClipRgn(hdcmem, clips[0]);\r
3659     for (x = 1; x < num_clips; x++) {\r
3660       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3661         abort();  // this should never ever happen!\r
3662     }\r
3663   }\r
3664 \r
3665   /* Do all the drawing to the memory DC */\r
3666   if(explodeInfo.radius) { // [HGM] atomic\r
3667         HBRUSH oldBrush;\r
3668         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3669         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3670         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3671         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3672         x += squareSize/2;\r
3673         y += squareSize/2;\r
3674         if(!fullrepaint) {\r
3675           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3676           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3677         }\r
3678         DrawGridOnDC(hdcmem);\r
3679         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3680         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3681         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3682         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3683         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3684         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3685         SelectObject(hdcmem, oldBrush);\r
3686   } else {\r
3687     DrawGridOnDC(hdcmem);\r
3688     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3689         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3690         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3691     } else {\r
3692         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3693         oldPartnerHighlight = partnerHighlightInfo;\r
3694     }\r
3695     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3696   }\r
3697   if(nr == 0) // [HGM] dual: markers only on left board\r
3698   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3699     for (column = 0; column < BOARD_WIDTH; column++) {\r
3700         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3701             HBRUSH oldBrush = SelectObject(hdcmem, \r
3702                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3703             SquareToPos(row, column, &x, &y);\r
3704             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3705                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3706             SelectObject(hdcmem, oldBrush);\r
3707         }\r
3708     }\r
3709   }\r
3710   if(logoHeight) {\r
3711         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3712         if(appData.autoLogo) {\r
3713           \r
3714           switch(gameMode) { // pick logos based on game mode\r
3715             case IcsObserving:\r
3716                 whiteLogo = second.programLogo; // ICS logo\r
3717                 blackLogo = second.programLogo;\r
3718             default:\r
3719                 break;\r
3720             case IcsPlayingWhite:\r
3721                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3722                 blackLogo = second.programLogo; // ICS logo\r
3723                 break;\r
3724             case IcsPlayingBlack:\r
3725                 whiteLogo = second.programLogo; // ICS logo\r
3726                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3727                 break;\r
3728             case TwoMachinesPlay:\r
3729                 if(first.twoMachinesColor[0] == 'b') {\r
3730                     whiteLogo = second.programLogo;\r
3731                     blackLogo = first.programLogo;\r
3732                 }\r
3733                 break;\r
3734             case MachinePlaysWhite:\r
3735                 blackLogo = userLogo;\r
3736                 break;\r
3737             case MachinePlaysBlack:\r
3738                 whiteLogo = userLogo;\r
3739                 blackLogo = first.programLogo;\r
3740           }\r
3741         }\r
3742         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3743         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3744   }\r
3745 \r
3746   if( appData.highlightMoveWithArrow ) {\r
3747     DrawArrowHighlight(hdcmem);\r
3748   }\r
3749 \r
3750   DrawCoordsOnDC(hdcmem);\r
3751 \r
3752   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3753                  /* to make sure lastDrawn contains what is actually drawn */\r
3754 \r
3755   /* Put the dragged piece back into place and draw it (out of place!) */\r
3756     if (dragged_piece != EmptySquare) {\r
3757     /* [HGM] or restack */\r
3758     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3759                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3760     else\r
3761     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3762                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3763     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3764     x = dragInfo.pos.x - squareSize / 2;\r
3765     y = dragInfo.pos.y - squareSize / 2;\r
3766     DrawPieceOnDC(hdcmem, dragged_piece,\r
3767                   ((int) dragged_piece < (int) BlackPawn), \r
3768                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3769   }   \r
3770   \r
3771   /* Put the animated piece back into place and draw it */\r
3772   if (animInfo.piece != EmptySquare) {\r
3773     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3774     x = boardRect.left + animInfo.pos.x;\r
3775     y = boardRect.top + animInfo.pos.y;\r
3776     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3777                   ((int) animInfo.piece < (int) BlackPawn),\r
3778                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3779   }\r
3780 \r
3781   /* Release the bufferBitmap by selecting in the old bitmap \r
3782    * and delete the memory DC\r
3783    */\r
3784   SelectObject(hdcmem, oldBitmap);\r
3785   DeleteDC(hdcmem);\r
3786 \r
3787   /* Set clipping on the target DC */\r
3788   if (!fullrepaint) {\r
3789     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3790         RECT rect;\r
3791         GetRgnBox(clips[x], &rect);\r
3792         DeleteObject(clips[x]);\r
3793         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3794                           rect.right + wpMain.width/2, rect.bottom);\r
3795     }\r
3796     SelectClipRgn(hdc, clips[0]);\r
3797     for (x = 1; x < num_clips; x++) {\r
3798       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3799         abort();   // this should never ever happen!\r
3800     } \r
3801   }\r
3802 \r
3803   /* Copy the new bitmap onto the screen in one go.\r
3804    * This way we avoid any flickering\r
3805    */\r
3806   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3807   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3808          boardRect.right - boardRect.left,\r
3809          boardRect.bottom - boardRect.top,\r
3810          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3811   if(saveDiagFlag) { \r
3812     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
3813     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3814 \r
3815     GetObject(bufferBitmap, sizeof(b), &b);\r
3816     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
3817         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3818         bih.biWidth = b.bmWidth;\r
3819         bih.biHeight = b.bmHeight;\r
3820         bih.biPlanes = 1;\r
3821         bih.biBitCount = b.bmBitsPixel;\r
3822         bih.biCompression = 0;\r
3823         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3824         bih.biXPelsPerMeter = 0;\r
3825         bih.biYPelsPerMeter = 0;\r
3826         bih.biClrUsed = 0;\r
3827         bih.biClrImportant = 0;\r
3828 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3829 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3830         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3831 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3832 \r
3833         wb = b.bmWidthBytes;\r
3834         // count colors\r
3835         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3836                 int k = ((int*) pData)[i];\r
3837                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3838                 if(j >= 16) break;\r
3839                 color[j] = k;\r
3840                 if(j >= nrColors) nrColors = j+1;\r
3841         }\r
3842         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3843                 INT p = 0;\r
3844                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3845                     for(w=0; w<(wb>>2); w+=2) {\r
3846                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3847                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3848                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3849                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3850                         pData[p++] = m | j<<4;\r
3851                     }\r
3852                     while(p&3) pData[p++] = 0;\r
3853                 }\r
3854                 fac = 3;\r
3855                 wb = ((wb+31)>>5)<<2;\r
3856         }\r
3857         // write BITMAPFILEHEADER\r
3858         fprintf(diagFile, "BM");\r
3859         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3860         fputDW(diagFile, 0);\r
3861         fputDW(diagFile, 0x36 + (fac?64:0));\r
3862         // write BITMAPINFOHEADER\r
3863         fputDW(diagFile, 40);\r
3864         fputDW(diagFile, b.bmWidth);\r
3865         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3866         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3867         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3868         fputDW(diagFile, 0);\r
3869         fputDW(diagFile, 0);\r
3870         fputDW(diagFile, 0);\r
3871         fputDW(diagFile, 0);\r
3872         fputDW(diagFile, 0);\r
3873         fputDW(diagFile, 0);\r
3874         // write color table\r
3875         if(fac)\r
3876         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3877         // write bitmap data\r
3878         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3879                 fputc(pData[i], diagFile);\r
3880      }\r
3881   }\r
3882 \r
3883   SelectObject(tmphdc, oldBitmap);\r
3884 \r
3885   /* Massive cleanup */\r
3886   for (x = 0; x < num_clips; x++)\r
3887     DeleteObject(clips[x]);\r
3888 \r
3889   DeleteDC(tmphdc);\r
3890   DeleteObject(bufferBitmap);\r
3891 \r
3892   if (releaseDC) \r
3893     ReleaseDC(hwndMain, hdc);\r
3894   \r
3895   if (lastDrawnFlipView != flipView && nr == 0) {\r
3896     if (flipView)\r
3897       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3898     else\r
3899       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3900   }\r
3901 \r
3902 /*  CopyBoard(lastDrawn, board);*/\r
3903   lastDrawnHighlight = highlightInfo;\r
3904   lastDrawnPremove   = premoveHighlightInfo;\r
3905   lastDrawnFlipView = flipView;\r
3906   lastDrawnValid[nr] = 1;\r
3907 }\r
3908 \r
3909 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3910 int\r
3911 SaveDiagram(f)\r
3912      FILE *f;\r
3913 {\r
3914     saveDiagFlag = 1; diagFile = f;\r
3915     HDCDrawPosition(NULL, TRUE, NULL);\r
3916 \r
3917     saveDiagFlag = 0;\r
3918 \r
3919 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
3920     \r
3921     fclose(f);\r
3922     return TRUE;\r
3923 }\r
3924 \r
3925 \r
3926 /*---------------------------------------------------------------------------*\\r
3927 | CLIENT PAINT PROCEDURE\r
3928 |   This is the main event-handler for the WM_PAINT message.\r
3929 |\r
3930 \*---------------------------------------------------------------------------*/\r
3931 VOID\r
3932 PaintProc(HWND hwnd)\r
3933 {\r
3934   HDC         hdc;\r
3935   PAINTSTRUCT ps;\r
3936   HFONT       oldFont;\r
3937 \r
3938   if((hdc = BeginPaint(hwnd, &ps))) {\r
3939     if (IsIconic(hwnd)) {\r
3940       DrawIcon(hdc, 2, 2, iconCurrent);\r
3941     } else {\r
3942       if (!appData.monoMode) {\r
3943         SelectPalette(hdc, hPal, FALSE);\r
3944         RealizePalette(hdc);\r
3945       }\r
3946       HDCDrawPosition(hdc, 1, NULL);\r
3947       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3948         flipView = !flipView; partnerUp = !partnerUp;\r
3949         HDCDrawPosition(hdc, 1, NULL);\r
3950         flipView = !flipView; partnerUp = !partnerUp;\r
3951       }\r
3952       oldFont =\r
3953         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3954       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3955                  ETO_CLIPPED|ETO_OPAQUE,\r
3956                  &messageRect, messageText, strlen(messageText), NULL);\r
3957       SelectObject(hdc, oldFont);\r
3958       DisplayBothClocks();\r
3959     }\r
3960     EndPaint(hwnd,&ps);\r
3961   }\r
3962 \r
3963   return;\r
3964 }\r
3965 \r
3966 \r
3967 /*\r
3968  * If the user selects on a border boundary, return -1; if off the board,\r
3969  *   return -2.  Otherwise map the event coordinate to the square.\r
3970  * The offset boardRect.left or boardRect.top must already have been\r
3971  *   subtracted from x.\r
3972  */\r
3973 int EventToSquare(x, limit)\r
3974      int x, limit;\r
3975 {\r
3976   if (x <= 0)\r
3977     return -2;\r
3978   if (x < lineGap)\r
3979     return -1;\r
3980   x -= lineGap;\r
3981   if ((x % (squareSize + lineGap)) >= squareSize)\r
3982     return -1;\r
3983   x /= (squareSize + lineGap);\r
3984     if (x >= limit)\r
3985     return -2;\r
3986   return x;\r
3987 }\r
3988 \r
3989 typedef struct {\r
3990   char piece;\r
3991   int command;\r
3992   char* name;\r
3993 } DropEnable;\r
3994 \r
3995 DropEnable dropEnables[] = {\r
3996   { 'P', DP_Pawn, N_("Pawn") },\r
3997   { 'N', DP_Knight, N_("Knight") },\r
3998   { 'B', DP_Bishop, N_("Bishop") },\r
3999   { 'R', DP_Rook, N_("Rook") },\r
4000   { 'Q', DP_Queen, N_("Queen") },\r
4001 };\r
4002 \r
4003 VOID\r
4004 SetupDropMenu(HMENU hmenu)\r
4005 {\r
4006   int i, count, enable;\r
4007   char *p;\r
4008   extern char white_holding[], black_holding[];\r
4009   char item[MSG_SIZ];\r
4010 \r
4011   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4012     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4013                dropEnables[i].piece);\r
4014     count = 0;\r
4015     while (p && *p++ == dropEnables[i].piece) count++;\r
4016       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4017     enable = count > 0 || !appData.testLegality\r
4018       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4019                       && !appData.icsActive);\r
4020     ModifyMenu(hmenu, dropEnables[i].command,\r
4021                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4022                dropEnables[i].command, item);\r
4023   }\r
4024 }\r
4025 \r
4026 void DragPieceBegin(int x, int y)\r
4027 {\r
4028       dragInfo.lastpos.x = boardRect.left + x;\r
4029       dragInfo.lastpos.y = boardRect.top + y;\r
4030       dragInfo.from.x = fromX;\r
4031       dragInfo.from.y = fromY;\r
4032       dragInfo.start = dragInfo.from;\r
4033       SetCapture(hwndMain);\r
4034 }\r
4035 \r
4036 void DragPieceEnd(int x, int y)\r
4037 {\r
4038     ReleaseCapture();\r
4039     dragInfo.start.x = dragInfo.start.y = -1;\r
4040     dragInfo.from = dragInfo.start;\r
4041     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4042 }\r
4043 \r
4044 /* Event handler for mouse messages */\r
4045 VOID\r
4046 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4047 {\r
4048   int x, y, menuNr;\r
4049   POINT pt;\r
4050   static int recursive = 0;\r
4051   HMENU hmenu;\r
4052   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4053 \r
4054   if (recursive) {\r
4055     if (message == WM_MBUTTONUP) {\r
4056       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4057          to the middle button: we simulate pressing the left button too!\r
4058          */\r
4059       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4060       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4061     }\r
4062     return;\r
4063   }\r
4064   recursive++;\r
4065   \r
4066   pt.x = LOWORD(lParam);\r
4067   pt.y = HIWORD(lParam);\r
4068   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4069   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4070   if (!flipView && y >= 0) {\r
4071     y = BOARD_HEIGHT - 1 - y;\r
4072   }\r
4073   if (flipView && x >= 0) {\r
4074     x = BOARD_WIDTH - 1 - x;\r
4075   }\r
4076 \r
4077   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4078 \r
4079   switch (message) {\r
4080   case WM_LBUTTONDOWN:\r
4081       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4082         if (gameMode == EditPosition) {\r
4083           SetWhiteToPlayEvent();\r
4084         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4085           AdjustClock(flipClock, -1);\r
4086         } else if (gameMode == IcsPlayingBlack ||\r
4087                    gameMode == MachinePlaysWhite) {\r
4088           CallFlagEvent();\r
4089         }\r
4090       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4091         if (gameMode == EditPosition) {\r
4092           SetBlackToPlayEvent();\r
4093         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4094           AdjustClock(!flipClock, -1);\r
4095         } else if (gameMode == IcsPlayingWhite ||\r
4096                    gameMode == MachinePlaysBlack) {\r
4097           CallFlagEvent();\r
4098         }\r
4099       }\r
4100       dragInfo.start.x = dragInfo.start.y = -1;\r
4101       dragInfo.from = dragInfo.start;\r
4102     if(fromX == -1 && frozen) { // not sure where this is for\r
4103                 fromX = fromY = -1; \r
4104       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4105       break;\r
4106     }\r
4107       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4108       DrawPosition(TRUE, NULL);\r
4109     break;\r
4110 \r
4111   case WM_LBUTTONUP:\r
4112       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4113       DrawPosition(TRUE, NULL);\r
4114     break;\r
4115 \r
4116   case WM_MOUSEMOVE:\r
4117     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4118     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4119     if ((appData.animateDragging || appData.highlightDragging)\r
4120         && (wParam & MK_LBUTTON)\r
4121         && dragInfo.from.x >= 0) \r
4122     {\r
4123       BOOL full_repaint = FALSE;\r
4124 \r
4125       if (appData.animateDragging) {\r
4126         dragInfo.pos = pt;\r
4127       }\r
4128       if (appData.highlightDragging) {\r
4129         SetHighlights(fromX, fromY, x, y);\r
4130         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4131             full_repaint = TRUE;\r
4132         }\r
4133       }\r
4134       \r
4135       DrawPosition( full_repaint, NULL);\r
4136       \r
4137       dragInfo.lastpos = dragInfo.pos;\r
4138     }\r
4139     break;\r
4140 \r
4141   case WM_MOUSEWHEEL: // [DM]\r
4142     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4143        /* Mouse Wheel is being rolled forward\r
4144         * Play moves forward\r
4145         */\r
4146        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4147                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4148        /* Mouse Wheel is being rolled backward\r
4149         * Play moves backward\r
4150         */\r
4151        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4152                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4153     }\r
4154     break;\r
4155 \r
4156   case WM_MBUTTONUP:\r
4157   case WM_RBUTTONUP:\r
4158     ReleaseCapture();\r
4159     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4160     break;\r
4161  \r
4162   case WM_MBUTTONDOWN:\r
4163   case WM_RBUTTONDOWN:\r
4164     ErrorPopDown();\r
4165     ReleaseCapture();\r
4166     fromX = fromY = -1;\r
4167     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4168     dragInfo.start.x = dragInfo.start.y = -1;\r
4169     dragInfo.from = dragInfo.start;\r
4170     dragInfo.lastpos = dragInfo.pos;\r
4171     if (appData.highlightDragging) {\r
4172       ClearHighlights();\r
4173     }\r
4174     if(y == -2) {\r
4175       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4176       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4177           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4178       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4179           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4180       }\r
4181       break;\r
4182     }\r
4183     DrawPosition(TRUE, NULL);\r
4184 \r
4185     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4186     switch (menuNr) {\r
4187     case 0:\r
4188       if (message == WM_MBUTTONDOWN) {\r
4189         buttonCount = 3;  /* even if system didn't think so */\r
4190         if (wParam & MK_SHIFT) \r
4191           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4192         else\r
4193           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4194       } else { /* message == WM_RBUTTONDOWN */\r
4195         /* Just have one menu, on the right button.  Windows users don't\r
4196            think to try the middle one, and sometimes other software steals\r
4197            it, or it doesn't really exist. */\r
4198         if(gameInfo.variant != VariantShogi)\r
4199             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4200         else\r
4201             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4202       }\r
4203       break;\r
4204     case 2:\r
4205       SetCapture(hwndMain);
4206       break;\r
4207     case 1:\r
4208       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4209       SetupDropMenu(hmenu);\r
4210       MenuPopup(hwnd, pt, hmenu, -1);\r
4211     default:\r
4212       break;\r
4213     }\r
4214     break;\r
4215   }\r
4216 \r
4217   recursive--;\r
4218 }\r
4219 \r
4220 /* Preprocess messages for buttons in main window */\r
4221 LRESULT CALLBACK\r
4222 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4223 {\r
4224   int id = GetWindowLong(hwnd, GWL_ID);\r
4225   int i, dir;\r
4226 \r
4227   for (i=0; i<N_BUTTONS; i++) {\r
4228     if (buttonDesc[i].id == id) break;\r
4229   }\r
4230   if (i == N_BUTTONS) return 0;\r
4231   switch (message) {\r
4232   case WM_KEYDOWN:\r
4233     switch (wParam) {\r
4234     case VK_LEFT:\r
4235     case VK_RIGHT:\r
4236       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4237       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4238       return TRUE;\r
4239     }\r
4240     break;\r
4241   case WM_CHAR:\r
4242     switch (wParam) {\r
4243     case '\r':\r
4244       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4245       return TRUE;\r
4246     default:\r
4247       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4248         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4249         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4250         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4251         SetFocus(h);\r
4252         SendMessage(h, WM_CHAR, wParam, lParam);\r
4253         return TRUE;\r
4254       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4255         PopUpMoveDialog((char)wParam);\r
4256       }\r
4257       break;\r
4258     }\r
4259     break;\r
4260   }\r
4261   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4262 }\r
4263 \r
4264 /* Process messages for Promotion dialog box */\r
4265 LRESULT CALLBACK\r
4266 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4267 {\r
4268   char promoChar;\r
4269 \r
4270   switch (message) {\r
4271   case WM_INITDIALOG: /* message: initialize dialog box */\r
4272     /* Center the dialog over the application window */\r
4273     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4274     Translate(hDlg, DLG_PromotionKing);\r
4275     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4276       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4277        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4278                SW_SHOW : SW_HIDE);\r
4279     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4280     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4281        ((PieceToChar(WhiteAngel) >= 'A' &&\r
4282          PieceToChar(WhiteAngel) != '~') ||\r
4283         (PieceToChar(BlackAngel) >= 'A' &&\r
4284          PieceToChar(BlackAngel) != '~')   ) ?\r
4285                SW_SHOW : SW_HIDE);\r
4286     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4287        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
4288          PieceToChar(WhiteMarshall) != '~') ||\r
4289         (PieceToChar(BlackMarshall) >= 'A' &&\r
4290          PieceToChar(BlackMarshall) != '~')   ) ?\r
4291                SW_SHOW : SW_HIDE);\r
4292     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4293     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4294        gameInfo.variant != VariantShogi ?\r
4295                SW_SHOW : SW_HIDE);\r
4296     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4297        gameInfo.variant != VariantShogi ?\r
4298                SW_SHOW : SW_HIDE);\r
4299     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
4300        gameInfo.variant == VariantShogi ?\r
4301                SW_SHOW : SW_HIDE);\r
4302     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
4303        gameInfo.variant == VariantShogi ?\r
4304                SW_SHOW : SW_HIDE);\r
4305     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4306        gameInfo.variant == VariantSuper ?\r
4307                SW_SHOW : SW_HIDE);\r
4308     return TRUE;\r
4309 \r
4310   case WM_COMMAND: /* message: received a command */\r
4311     switch (LOWORD(wParam)) {\r
4312     case IDCANCEL:\r
4313       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4314       ClearHighlights();\r
4315       DrawPosition(FALSE, NULL);\r
4316       return TRUE;\r
4317     case PB_King:\r
4318       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4319       break;\r
4320     case PB_Queen:\r
4321       promoChar = gameInfo.variant == VariantShogi ? '^' : PieceToChar(BlackQueen);\r
4322       break;\r
4323     case PB_Rook:\r
4324       promoChar = PieceToChar(BlackRook);\r
4325       break;\r
4326     case PB_Bishop:\r
4327       promoChar = PieceToChar(BlackBishop);\r
4328       break;\r
4329     case PB_Chancellor:\r
4330       promoChar = PieceToChar(BlackMarshall);\r
4331       break;\r
4332     case PB_Archbishop:\r
4333       promoChar = PieceToChar(BlackAngel);\r
4334       break;\r
4335     case PB_Knight:\r
4336       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
4337       break;\r
4338     default:\r
4339       return FALSE;\r
4340     }\r
4341     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4342     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
4343        only show the popup when we are already sure the move is valid or\r
4344        legal. We pass a faulty move type, but the kludge is that FinishMove\r
4345        will figure out it is a promotion from the promoChar. */\r
4346     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4347     fromX = fromY = -1;\r
4348     if (!appData.highlightLastMove) {\r
4349       ClearHighlights();\r
4350       DrawPosition(FALSE, NULL);\r
4351     }\r
4352     return TRUE;\r
4353   }\r
4354   return FALSE;\r
4355 }\r
4356 \r
4357 /* Pop up promotion dialog */\r
4358 VOID\r
4359 PromotionPopup(HWND hwnd)\r
4360 {\r
4361   FARPROC lpProc;\r
4362 \r
4363   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4364   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4365     hwnd, (DLGPROC)lpProc);\r
4366   FreeProcInstance(lpProc);\r
4367 }\r
4368 \r
4369 void\r
4370 PromotionPopUp()\r
4371 {\r
4372   DrawPosition(TRUE, NULL);\r
4373   PromotionPopup(hwndMain);\r
4374 }\r
4375 \r
4376 /* Toggle ShowThinking */\r
4377 VOID\r
4378 ToggleShowThinking()\r
4379 {\r
4380   appData.showThinking = !appData.showThinking;\r
4381   ShowThinkingEvent();\r
4382 }\r
4383 \r
4384 VOID\r
4385 LoadGameDialog(HWND hwnd, char* title)\r
4386 {\r
4387   UINT number = 0;\r
4388   FILE *f;\r
4389   char fileTitle[MSG_SIZ];\r
4390   f = OpenFileDialog(hwnd, "rb", "",\r
4391                      appData.oldSaveStyle ? "gam" : "pgn",\r
4392                      GAME_FILT,\r
4393                      title, &number, fileTitle, NULL);\r
4394   if (f != NULL) {\r
4395     cmailMsgLoaded = FALSE;\r
4396     if (number == 0) {\r
4397       int error = GameListBuild(f);\r
4398       if (error) {\r
4399         DisplayError(_("Cannot build game list"), error);\r
4400       } else if (!ListEmpty(&gameList) &&\r
4401                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4402         GameListPopUp(f, fileTitle);\r
4403         return;\r
4404       }\r
4405       GameListDestroy();\r
4406       number = 1;\r
4407     }\r
4408     LoadGame(f, number, fileTitle, FALSE);\r
4409   }\r
4410 }\r
4411 \r
4412 int get_term_width()\r
4413 {\r
4414     HDC hdc;\r
4415     TEXTMETRIC tm;\r
4416     RECT rc;\r
4417     HFONT hfont, hold_font;\r
4418     LOGFONT lf;\r
4419     HWND hText;\r
4420 \r
4421     if (hwndConsole)\r
4422         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4423     else\r
4424         return 79;\r
4425 \r
4426     // get the text metrics\r
4427     hdc = GetDC(hText);\r
4428     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4429     if (consoleCF.dwEffects & CFE_BOLD)\r
4430         lf.lfWeight = FW_BOLD;\r
4431     if (consoleCF.dwEffects & CFE_ITALIC)\r
4432         lf.lfItalic = TRUE;\r
4433     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4434         lf.lfStrikeOut = TRUE;\r
4435     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4436         lf.lfUnderline = TRUE;\r
4437     hfont = CreateFontIndirect(&lf);\r
4438     hold_font = SelectObject(hdc, hfont);\r
4439     GetTextMetrics(hdc, &tm);\r
4440     SelectObject(hdc, hold_font);\r
4441     DeleteObject(hfont);\r
4442     ReleaseDC(hText, hdc);\r
4443 \r
4444     // get the rectangle\r
4445     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4446 \r
4447     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4448 }\r
4449 \r
4450 void UpdateICSWidth(HWND hText)\r
4451 {\r
4452     LONG old_width, new_width;\r
4453 \r
4454     new_width = get_term_width(hText, FALSE);\r
4455     old_width = GetWindowLong(hText, GWL_USERDATA);\r
4456     if (new_width != old_width)\r
4457     {\r
4458         ics_update_width(new_width);\r
4459         SetWindowLong(hText, GWL_USERDATA, new_width);\r
4460     }\r
4461 }\r
4462 \r
4463 VOID\r
4464 ChangedConsoleFont()\r
4465 {\r
4466   CHARFORMAT cfmt;\r
4467   CHARRANGE tmpsel, sel;\r
4468   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4469   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4470   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4471   PARAFORMAT paraf;\r
4472 \r
4473   cfmt.cbSize = sizeof(CHARFORMAT);\r
4474   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4475     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4476                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4477   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4478    * size.  This was undocumented in the version of MSVC++ that I had\r
4479    * when I wrote the code, but is apparently documented now.\r
4480    */\r
4481   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4482   cfmt.bCharSet = f->lf.lfCharSet;\r
4483   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4484   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4485   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4486   /* Why are the following seemingly needed too? */\r
4487   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4488   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4489   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4490   tmpsel.cpMin = 0;\r
4491   tmpsel.cpMax = -1; /*999999?*/\r
4492   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4493   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4494   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4495    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4496    */\r
4497   paraf.cbSize = sizeof(paraf);\r
4498   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4499   paraf.dxStartIndent = 0;\r
4500   paraf.dxOffset = WRAP_INDENT;\r
4501   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4502   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4503   UpdateICSWidth(hText);\r
4504 }\r
4505 \r
4506 /*---------------------------------------------------------------------------*\\r
4507  *\r
4508  * Window Proc for main window\r
4509  *\r
4510 \*---------------------------------------------------------------------------*/\r
4511 \r
4512 /* Process messages for main window, etc. */\r
4513 LRESULT CALLBACK\r
4514 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4515 {\r
4516   FARPROC lpProc;\r
4517   int wmId, wmEvent;\r
4518   char *defName;\r
4519   FILE *f;\r
4520   UINT number;\r
4521   char fileTitle[MSG_SIZ];\r
4522   char buf[MSG_SIZ];\r
4523   static SnapData sd;\r
4524 \r
4525   switch (message) {\r
4526 \r
4527   case WM_PAINT: /* message: repaint portion of window */\r
4528     PaintProc(hwnd);\r
4529     break;\r
4530 \r
4531   case WM_ERASEBKGND:\r
4532     if (IsIconic(hwnd)) {\r
4533       /* Cheat; change the message */\r
4534       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4535     } else {\r
4536       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4537     }\r
4538     break;\r
4539 \r
4540   case WM_LBUTTONDOWN:\r
4541   case WM_MBUTTONDOWN:\r
4542   case WM_RBUTTONDOWN:\r
4543   case WM_LBUTTONUP:\r
4544   case WM_MBUTTONUP:\r
4545   case WM_RBUTTONUP:\r
4546   case WM_MOUSEMOVE:\r
4547   case WM_MOUSEWHEEL:\r
4548     MouseEvent(hwnd, message, wParam, lParam);\r
4549     break;\r
4550 \r
4551   JAWS_KB_NAVIGATION\r
4552 \r
4553   case WM_CHAR:\r
4554     \r
4555     JAWS_ALT_INTERCEPT\r
4556 \r
4557     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4558         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4559         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4560         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4561         SetFocus(h);\r
4562         SendMessage(h, message, wParam, lParam);\r
4563     } else if(lParam != KF_REPEAT) {\r
4564         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4565                 PopUpMoveDialog((char)wParam);\r
4566         } else if((char)wParam == 003) CopyGameToClipboard();\r
4567          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4568     }\r
4569 \r
4570     break;\r
4571 \r
4572   case WM_PALETTECHANGED:\r
4573     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4574       int nnew;\r
4575       HDC hdc = GetDC(hwndMain);\r
4576       SelectPalette(hdc, hPal, TRUE);\r
4577       nnew = RealizePalette(hdc);\r
4578       if (nnew > 0) {\r
4579         paletteChanged = TRUE;\r
4580         InvalidateRect(hwnd, &boardRect, FALSE);\r
4581       }\r
4582       ReleaseDC(hwnd, hdc);\r
4583     }\r
4584     break;\r
4585 \r
4586   case WM_QUERYNEWPALETTE:\r
4587     if (!appData.monoMode /*&& paletteChanged*/) {\r
4588       int nnew;\r
4589       HDC hdc = GetDC(hwndMain);\r
4590       paletteChanged = FALSE;\r
4591       SelectPalette(hdc, hPal, FALSE);\r
4592       nnew = RealizePalette(hdc);\r
4593       if (nnew > 0) {\r
4594         InvalidateRect(hwnd, &boardRect, FALSE);\r
4595       }\r
4596       ReleaseDC(hwnd, hdc);\r
4597       return TRUE;\r
4598     }\r
4599     return FALSE;\r
4600 \r
4601   case WM_COMMAND: /* message: command from application menu */\r
4602     wmId    = LOWORD(wParam);\r
4603     wmEvent = HIWORD(wParam);\r
4604 \r
4605     switch (wmId) {\r
4606     case IDM_NewGame:\r
4607       ResetGameEvent();\r
4608       SAY("new game enter a move to play against the computer with white");\r
4609       break;\r
4610 \r
4611     case IDM_NewGameFRC:\r
4612       if( NewGameFRC() == 0 ) {\r
4613         ResetGameEvent();\r
4614       }\r
4615       break;\r
4616 \r
4617     case IDM_NewVariant:\r
4618       NewVariantPopup(hwnd);\r
4619       break;\r
4620 \r
4621     case IDM_LoadGame:\r
4622       LoadGameDialog(hwnd, _("Load Game from File"));\r
4623       break;\r
4624 \r
4625     case IDM_LoadNextGame:\r
4626       ReloadGame(1);\r
4627       break;\r
4628 \r
4629     case IDM_LoadPrevGame:\r
4630       ReloadGame(-1);\r
4631       break;\r
4632 \r
4633     case IDM_ReloadGame:\r
4634       ReloadGame(0);\r
4635       break;\r
4636 \r
4637     case IDM_LoadPosition:\r
4638       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4639         Reset(FALSE, TRUE);\r
4640       }\r
4641       number = 1;\r
4642       f = OpenFileDialog(hwnd, "rb", "",\r
4643                          appData.oldSaveStyle ? "pos" : "fen",\r
4644                          POSITION_FILT,\r
4645                          _("Load Position from File"), &number, fileTitle, NULL);\r
4646       if (f != NULL) {\r
4647         LoadPosition(f, number, fileTitle);\r
4648       }\r
4649       break;\r
4650 \r
4651     case IDM_LoadNextPosition:\r
4652       ReloadPosition(1);\r
4653       break;\r
4654 \r
4655     case IDM_LoadPrevPosition:\r
4656       ReloadPosition(-1);\r
4657       break;\r
4658 \r
4659     case IDM_ReloadPosition:\r
4660       ReloadPosition(0);\r
4661       break;\r
4662 \r
4663     case IDM_SaveGame:\r
4664       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4665       f = OpenFileDialog(hwnd, "a", defName,\r
4666                          appData.oldSaveStyle ? "gam" : "pgn",\r
4667                          GAME_FILT,\r
4668                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4669       if (f != NULL) {\r
4670         SaveGame(f, 0, "");\r
4671       }\r
4672       break;\r
4673 \r
4674     case IDM_SavePosition:\r
4675       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4676       f = OpenFileDialog(hwnd, "a", defName,\r
4677                          appData.oldSaveStyle ? "pos" : "fen",\r
4678                          POSITION_FILT,\r
4679                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4680       if (f != NULL) {\r
4681         SavePosition(f, 0, "");\r
4682       }\r
4683       break;\r
4684 \r
4685     case IDM_SaveDiagram:\r
4686       defName = "diagram";\r
4687       f = OpenFileDialog(hwnd, "wb", defName,\r
4688                          "bmp",\r
4689                          DIAGRAM_FILT,\r
4690                          "Save Diagram to File", NULL, fileTitle, NULL);\r
4691       if (f != NULL) {\r
4692         SaveDiagram(f);\r
4693       }\r
4694       break;\r
4695 \r
4696     case IDM_CopyGame:\r
4697       CopyGameToClipboard();\r
4698       break;\r
4699 \r
4700     case IDM_PasteGame:\r
4701       PasteGameFromClipboard();\r
4702       break;\r
4703 \r
4704     case IDM_CopyGameListToClipboard:\r
4705       CopyGameListToClipboard();\r
4706       break;\r
4707 \r
4708     /* [AS] Autodetect FEN or PGN data */\r
4709     case IDM_PasteAny:\r
4710       PasteGameOrFENFromClipboard();\r
4711       break;\r
4712 \r
4713     /* [AS] Move history */\r
4714     case IDM_ShowMoveHistory:\r
4715         if( MoveHistoryIsUp() ) {\r
4716             MoveHistoryPopDown();\r
4717         }\r
4718         else {\r
4719             MoveHistoryPopUp();\r
4720         }\r
4721         break;\r
4722 \r
4723     /* [AS] Eval graph */\r
4724     case IDM_ShowEvalGraph:\r
4725         if( EvalGraphIsUp() ) {\r
4726             EvalGraphPopDown();\r
4727         }\r
4728         else {\r
4729             EvalGraphPopUp();\r
4730             SetFocus(hwndMain);\r
4731         }\r
4732         break;\r
4733 \r
4734     /* [AS] Engine output */\r
4735     case IDM_ShowEngineOutput:\r
4736         if( EngineOutputIsUp() ) {\r
4737             EngineOutputPopDown();\r
4738         }\r
4739         else {\r
4740             EngineOutputPopUp();\r
4741         }\r
4742         break;\r
4743 \r
4744     /* [AS] User adjudication */\r
4745     case IDM_UserAdjudication_White:\r
4746         UserAdjudicationEvent( +1 );\r
4747         break;\r
4748 \r
4749     case IDM_UserAdjudication_Black:\r
4750         UserAdjudicationEvent( -1 );\r
4751         break;\r
4752 \r
4753     case IDM_UserAdjudication_Draw:\r
4754         UserAdjudicationEvent( 0 );\r
4755         break;\r
4756 \r
4757     /* [AS] Game list options dialog */\r
4758     case IDM_GameListOptions:\r
4759       GameListOptions();\r
4760       break;\r
4761 \r
4762     case IDM_NewChat:\r
4763       ChatPopUp(NULL);\r
4764       break;\r
4765 \r
4766     case IDM_CopyPosition:\r
4767       CopyFENToClipboard();\r
4768       break;\r
4769 \r
4770     case IDM_PastePosition:\r
4771       PasteFENFromClipboard();\r
4772       break;\r
4773 \r
4774     case IDM_MailMove:\r
4775       MailMoveEvent();\r
4776       break;\r
4777 \r
4778     case IDM_ReloadCMailMsg:\r
4779       Reset(TRUE, TRUE);\r
4780       ReloadCmailMsgEvent(FALSE);\r
4781       break;\r
4782 \r
4783     case IDM_Minimize:\r
4784       ShowWindow(hwnd, SW_MINIMIZE);\r
4785       break;\r
4786 \r
4787     case IDM_Exit:\r
4788       ExitEvent(0);\r
4789       break;\r
4790 \r
4791     case IDM_MachineWhite:\r
4792       MachineWhiteEvent();\r
4793       /*\r
4794        * refresh the tags dialog only if it's visible\r
4795        */\r
4796       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4797           char *tags;\r
4798           tags = PGNTags(&gameInfo);\r
4799           TagsPopUp(tags, CmailMsg());\r
4800           free(tags);\r
4801       }\r
4802       SAY("computer starts playing white");\r
4803       break;\r
4804 \r
4805     case IDM_MachineBlack:\r
4806       MachineBlackEvent();\r
4807       /*\r
4808        * refresh the tags dialog only if it's visible\r
4809        */\r
4810       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4811           char *tags;\r
4812           tags = PGNTags(&gameInfo);\r
4813           TagsPopUp(tags, CmailMsg());\r
4814           free(tags);\r
4815       }\r
4816       SAY("computer starts playing black");\r
4817       break;\r
4818 \r
4819     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4820       if(gameMode != BeginningOfGame) break; // allow menu item to remain enabled for better mode highligting\r
4821       matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)\r
4822       appData.matchGames = appData.defaultMatchGames;\r
4823       matchGame = 1;\r
4824 \r
4825     case IDM_TwoMachines:\r
4826       TwoMachinesEvent();\r
4827       /*\r
4828        * refresh the tags dialog only if it's visible\r
4829        */\r
4830       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4831           char *tags;\r
4832           tags = PGNTags(&gameInfo);\r
4833           TagsPopUp(tags, CmailMsg());\r
4834           free(tags);\r
4835       }\r
4836       SAY("computer starts playing both sides");\r
4837       break;\r
4838 \r
4839     case IDM_AnalysisMode:\r
4840       if (!first.analysisSupport) {\r
4841         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4842         DisplayError(buf, 0);\r
4843       } else {\r
4844         SAY("analyzing current position");\r
4845         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4846         if (appData.icsActive) {\r
4847                if (gameMode != IcsObserving) {\r
4848                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4849                        DisplayError(buf, 0);\r
4850                        /* secure check */\r
4851                        if (appData.icsEngineAnalyze) {\r
4852                                if (appData.debugMode) \r
4853                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4854                                ExitAnalyzeMode();\r
4855                                ModeHighlight();\r
4856                                break;\r
4857                        }\r
4858                        break;\r
4859                } else {\r
4860                        /* if enable, user want disable icsEngineAnalyze */\r
4861                        if (appData.icsEngineAnalyze) {\r
4862                                ExitAnalyzeMode();\r
4863                                ModeHighlight();\r
4864                                break;\r
4865                        }\r
4866                        appData.icsEngineAnalyze = TRUE;\r
4867                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4868                }\r
4869         } \r
4870         if (!appData.showThinking) ToggleShowThinking();\r
4871         AnalyzeModeEvent();\r
4872       }\r
4873       break;\r
4874 \r
4875     case IDM_AnalyzeFile:\r
4876       if (!first.analysisSupport) {\r
4877         char buf[MSG_SIZ];\r
4878           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4879         DisplayError(buf, 0);\r
4880       } else {\r
4881         if (!appData.showThinking) ToggleShowThinking();\r
4882         AnalyzeFileEvent();\r
4883         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4884         AnalysisPeriodicEvent(1);\r
4885       }\r
4886       break;\r
4887 \r
4888     case IDM_IcsClient:\r
4889       IcsClientEvent();\r
4890       break;\r
4891 \r
4892     case IDM_EditGame:\r
4893       EditGameEvent();\r
4894       SAY("edit game");\r
4895       break;\r
4896 \r
4897     case IDM_EditPosition:\r
4898       EditPositionEvent();\r
4899       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4900       break;\r
4901 \r
4902     case IDM_Training:\r
4903       TrainingEvent();\r
4904       break;\r
4905 \r
4906     case IDM_ShowGameList:\r
4907       ShowGameListProc();\r
4908       break;\r
4909 \r
4910     case IDM_EditTags:\r
4911       EditTagsProc();\r
4912       break;\r
4913 \r
4914     case IDM_EditComment:\r
4915       if (commentUp && editComment) {\r
4916         CommentPopDown();\r
4917       } else {\r
4918         EditCommentEvent();\r
4919       }\r
4920       break;\r
4921 \r
4922     case IDM_Pause:\r
4923       PauseEvent();\r
4924       break;\r
4925 \r
4926     case IDM_Accept:\r
4927       AcceptEvent();\r
4928       break;\r
4929 \r
4930     case IDM_Decline:\r
4931       DeclineEvent();\r
4932       break;\r
4933 \r
4934     case IDM_Rematch:\r
4935       RematchEvent();\r
4936       break;\r
4937 \r
4938     case IDM_CallFlag:\r
4939       CallFlagEvent();\r
4940       break;\r
4941 \r
4942     case IDM_Draw:\r
4943       DrawEvent();\r
4944       break;\r
4945 \r
4946     case IDM_Adjourn:\r
4947       AdjournEvent();\r
4948       break;\r
4949 \r
4950     case IDM_Abort:\r
4951       AbortEvent();\r
4952       break;\r
4953 \r
4954     case IDM_Resign:\r
4955       ResignEvent();\r
4956       break;\r
4957 \r
4958     case IDM_StopObserving:\r
4959       StopObservingEvent();\r
4960       break;\r
4961 \r
4962     case IDM_StopExamining:\r
4963       StopExaminingEvent();\r
4964       break;\r
4965 \r
4966     case IDM_Upload:\r
4967       UploadGameEvent();\r
4968       break;\r
4969 \r
4970     case IDM_TypeInMove:\r
4971       PopUpMoveDialog('\000');\r
4972       break;\r
4973 \r
4974     case IDM_TypeInName:\r
4975       PopUpNameDialog('\000');\r
4976       break;\r
4977 \r
4978     case IDM_Backward:\r
4979       BackwardEvent();\r
4980       SetFocus(hwndMain);\r
4981       break;\r
4982 \r
4983     JAWS_MENU_ITEMS\r
4984 \r
4985     case IDM_Forward:\r
4986       ForwardEvent();\r
4987       SetFocus(hwndMain);\r
4988       break;\r
4989 \r
4990     case IDM_ToStart:\r
4991       ToStartEvent();\r
4992       SetFocus(hwndMain);\r
4993       break;\r
4994 \r
4995     case IDM_ToEnd:\r
4996       ToEndEvent();\r
4997       SetFocus(hwndMain);\r
4998       break;\r
4999 \r
5000     case IDM_Revert:\r
5001       RevertEvent(FALSE);\r
5002       break;\r
5003 \r
5004     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5005       RevertEvent(TRUE);\r
5006       break;\r
5007 \r
5008     case IDM_TruncateGame:\r
5009       TruncateGameEvent();\r
5010       break;\r
5011 \r
5012     case IDM_MoveNow:\r
5013       MoveNowEvent();\r
5014       break;\r
5015 \r
5016     case IDM_RetractMove:\r
5017       RetractMoveEvent();\r
5018       break;\r
5019 \r
5020     case IDM_FlipView:\r
5021       flipView = !flipView;\r
5022       DrawPosition(FALSE, NULL);\r
5023       break;\r
5024 \r
5025     case IDM_FlipClock:\r
5026       flipClock = !flipClock;\r
5027       DisplayBothClocks();\r
5028       DrawPosition(FALSE, NULL);\r
5029       break;\r
5030 \r
5031     case IDM_MuteSounds:\r
5032       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5033       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5034                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5035       break;\r
5036 \r
5037     case IDM_GeneralOptions:\r
5038       GeneralOptionsPopup(hwnd);\r
5039       DrawPosition(TRUE, NULL);\r
5040       break;\r
5041 \r
5042     case IDM_BoardOptions:\r
5043       BoardOptionsPopup(hwnd);\r
5044       break;\r
5045 \r
5046     case IDM_EnginePlayOptions:\r
5047       EnginePlayOptionsPopup(hwnd);\r
5048       break;\r
5049 \r
5050     case IDM_Engine1Options:\r
5051       EngineOptionsPopup(hwnd, &first);\r
5052       break;\r
5053 \r
5054     case IDM_Engine2Options:\r
5055       EngineOptionsPopup(hwnd, &second);\r
5056       break;\r
5057 \r
5058     case IDM_OptionsUCI:\r
5059       UciOptionsPopup(hwnd);\r
5060       break;\r
5061 \r
5062     case IDM_IcsOptions:\r
5063       IcsOptionsPopup(hwnd);\r
5064       break;\r
5065 \r
5066     case IDM_Fonts:\r
5067       FontsOptionsPopup(hwnd);\r
5068       break;\r
5069 \r
5070     case IDM_Sounds:\r
5071       SoundOptionsPopup(hwnd);\r
5072       break;\r
5073 \r
5074     case IDM_CommPort:\r
5075       CommPortOptionsPopup(hwnd);\r
5076       break;\r
5077 \r
5078     case IDM_LoadOptions:\r
5079       LoadOptionsPopup(hwnd);\r
5080       break;\r
5081 \r
5082     case IDM_SaveOptions:\r
5083       SaveOptionsPopup(hwnd);\r
5084       break;\r
5085 \r
5086     case IDM_TimeControl:\r
5087       TimeControlOptionsPopup(hwnd);\r
5088       break;\r
5089 \r
5090     case IDM_SaveSettings:\r
5091       SaveSettings(settingsFileName);\r
5092       break;\r
5093 \r
5094     case IDM_SaveSettingsOnExit:\r
5095       saveSettingsOnExit = !saveSettingsOnExit;\r
5096       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5097                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5098                                          MF_CHECKED : MF_UNCHECKED));\r
5099       break;\r
5100 \r
5101     case IDM_Hint:\r
5102       HintEvent();\r
5103       break;\r
5104 \r
5105     case IDM_Book:\r
5106       BookEvent();\r
5107       break;\r
5108 \r
5109     case IDM_AboutGame:\r
5110       AboutGameEvent();\r
5111       break;\r
5112 \r
5113     case IDM_Debug:\r
5114       appData.debugMode = !appData.debugMode;\r
5115       if (appData.debugMode) {\r
5116         char dir[MSG_SIZ];\r
5117         GetCurrentDirectory(MSG_SIZ, dir);\r
5118         SetCurrentDirectory(installDir);\r
5119         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5120         SetCurrentDirectory(dir);\r
5121         setbuf(debugFP, NULL);\r
5122       } else {\r
5123         fclose(debugFP);\r
5124         debugFP = NULL;\r
5125       }\r
5126       break;\r
5127 \r
5128     case IDM_HELPCONTENTS:\r
5129       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5130           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5131           MessageBox (GetFocus(),\r
5132                     _("Unable to activate help"),\r
5133                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5134       }\r
5135       break;\r
5136 \r
5137     case IDM_HELPSEARCH:\r
5138         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5139             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5140         MessageBox (GetFocus(),\r
5141                     _("Unable to activate help"),\r
5142                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5143       }\r
5144       break;\r
5145 \r
5146     case IDM_HELPHELP:\r
5147       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5148         MessageBox (GetFocus(),\r
5149                     _("Unable to activate help"),\r
5150                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5151       }\r
5152       break;\r
5153 \r
5154     case IDM_ABOUT:\r
5155       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5156       DialogBox(hInst, \r
5157         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5158         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5159       FreeProcInstance(lpProc);\r
5160       break;\r
5161 \r
5162     case IDM_DirectCommand1:\r
5163       AskQuestionEvent(_("Direct Command"),\r
5164                        _("Send to chess program:"), "", "1");\r
5165       break;\r
5166     case IDM_DirectCommand2:\r
5167       AskQuestionEvent(_("Direct Command"),\r
5168                        _("Send to second chess program:"), "", "2");\r
5169       break;\r
5170 \r
5171     case EP_WhitePawn:\r
5172       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5173       fromX = fromY = -1;\r
5174       break;\r
5175 \r
5176     case EP_WhiteKnight:\r
5177       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5178       fromX = fromY = -1;\r
5179       break;\r
5180 \r
5181     case EP_WhiteBishop:\r
5182       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5183       fromX = fromY = -1;\r
5184       break;\r
5185 \r
5186     case EP_WhiteRook:\r
5187       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5188       fromX = fromY = -1;\r
5189       break;\r
5190 \r
5191     case EP_WhiteQueen:\r
5192       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5193       fromX = fromY = -1;\r
5194       break;\r
5195 \r
5196     case EP_WhiteFerz:\r
5197       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5198       fromX = fromY = -1;\r
5199       break;\r
5200 \r
5201     case EP_WhiteWazir:\r
5202       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5203       fromX = fromY = -1;\r
5204       break;\r
5205 \r
5206     case EP_WhiteAlfil:\r
5207       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5208       fromX = fromY = -1;\r
5209       break;\r
5210 \r
5211     case EP_WhiteCannon:\r
5212       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5213       fromX = fromY = -1;\r
5214       break;\r
5215 \r
5216     case EP_WhiteCardinal:\r
5217       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5218       fromX = fromY = -1;\r
5219       break;\r
5220 \r
5221     case EP_WhiteMarshall:\r
5222       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5223       fromX = fromY = -1;\r
5224       break;\r
5225 \r
5226     case EP_WhiteKing:\r
5227       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5228       fromX = fromY = -1;\r
5229       break;\r
5230 \r
5231     case EP_BlackPawn:\r
5232       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5233       fromX = fromY = -1;\r
5234       break;\r
5235 \r
5236     case EP_BlackKnight:\r
5237       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5238       fromX = fromY = -1;\r
5239       break;\r
5240 \r
5241     case EP_BlackBishop:\r
5242       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5243       fromX = fromY = -1;\r
5244       break;\r
5245 \r
5246     case EP_BlackRook:\r
5247       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5248       fromX = fromY = -1;\r
5249       break;\r
5250 \r
5251     case EP_BlackQueen:\r
5252       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5253       fromX = fromY = -1;\r
5254       break;\r
5255 \r
5256     case EP_BlackFerz:\r
5257       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5258       fromX = fromY = -1;\r
5259       break;\r
5260 \r
5261     case EP_BlackWazir:\r
5262       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5263       fromX = fromY = -1;\r
5264       break;\r
5265 \r
5266     case EP_BlackAlfil:\r
5267       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5268       fromX = fromY = -1;\r
5269       break;\r
5270 \r
5271     case EP_BlackCannon:\r
5272       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5273       fromX = fromY = -1;\r
5274       break;\r
5275 \r
5276     case EP_BlackCardinal:\r
5277       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5278       fromX = fromY = -1;\r
5279       break;\r
5280 \r
5281     case EP_BlackMarshall:\r
5282       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5283       fromX = fromY = -1;\r
5284       break;\r
5285 \r
5286     case EP_BlackKing:\r
5287       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5288       fromX = fromY = -1;\r
5289       break;\r
5290 \r
5291     case EP_EmptySquare:\r
5292       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5293       fromX = fromY = -1;\r
5294       break;\r
5295 \r
5296     case EP_ClearBoard:\r
5297       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5298       fromX = fromY = -1;\r
5299       break;\r
5300 \r
5301     case EP_White:\r
5302       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5303       fromX = fromY = -1;\r
5304       break;\r
5305 \r
5306     case EP_Black:\r
5307       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5308       fromX = fromY = -1;\r
5309       break;\r
5310 \r
5311     case EP_Promote:\r
5312       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5313       fromX = fromY = -1;\r
5314       break;\r
5315 \r
5316     case EP_Demote:\r
5317       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5318       fromX = fromY = -1;\r
5319       break;\r
5320 \r
5321     case DP_Pawn:\r
5322       DropMenuEvent(WhitePawn, fromX, fromY);\r
5323       fromX = fromY = -1;\r
5324       break;\r
5325 \r
5326     case DP_Knight:\r
5327       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5328       fromX = fromY = -1;\r
5329       break;\r
5330 \r
5331     case DP_Bishop:\r
5332       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5333       fromX = fromY = -1;\r
5334       break;\r
5335 \r
5336     case DP_Rook:\r
5337       DropMenuEvent(WhiteRook, fromX, fromY);\r
5338       fromX = fromY = -1;\r
5339       break;\r
5340 \r
5341     case DP_Queen:\r
5342       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5343       fromX = fromY = -1;\r
5344       break;\r
5345 \r
5346     case IDM_English:\r
5347       barbaric = 0;\r
5348       TranslateMenus(0);\r
5349       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5350       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5351       lastChecked = wmId;\r
5352       break;\r
5353 \r
5354     default:\r
5355       if(wmId > IDM_English && wmId < IDM_English+5) {\r
5356           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5357           TranslateMenus(0);\r
5358           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5359           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5360           lastChecked = wmId;\r
5361           break;\r
5362       }\r
5363       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5364     }\r
5365     break;\r
5366 \r
5367   case WM_TIMER:\r
5368     switch (wParam) {\r
5369     case CLOCK_TIMER_ID:\r
5370       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5371       clockTimerEvent = 0;\r
5372       DecrementClocks(); /* call into back end */\r
5373       break;\r
5374     case LOAD_GAME_TIMER_ID:\r
5375       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5376       loadGameTimerEvent = 0;\r
5377       AutoPlayGameLoop(); /* call into back end */\r
5378       break;\r
5379     case ANALYSIS_TIMER_ID:\r
5380       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5381                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5382         AnalysisPeriodicEvent(0);\r
5383       } else {\r
5384         KillTimer(hwnd, analysisTimerEvent);\r
5385         analysisTimerEvent = 0;\r
5386       }\r
5387       break;\r
5388     case DELAYED_TIMER_ID:\r
5389       KillTimer(hwnd, delayedTimerEvent);\r
5390       delayedTimerEvent = 0;\r
5391       delayedTimerCallback();\r
5392       break;\r
5393     }\r
5394     break;\r
5395 \r
5396   case WM_USER_Input:\r
5397     InputEvent(hwnd, message, wParam, lParam);\r
5398     break;\r
5399 \r
5400   /* [AS] Also move "attached" child windows */\r
5401   case WM_WINDOWPOSCHANGING:\r
5402 \r
5403     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5404         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5405 \r
5406         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5407             /* Window is moving */\r
5408             RECT rcMain;\r
5409 \r
5410 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5411             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5412             rcMain.right  = wpMain.x + wpMain.width;\r
5413             rcMain.top    = wpMain.y;\r
5414             rcMain.bottom = wpMain.y + wpMain.height;\r
5415             \r
5416             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5417             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5418             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5419             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5420             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5421             wpMain.x = lpwp->x;\r
5422             wpMain.y = lpwp->y;\r
5423         }\r
5424     }\r
5425     break;\r
5426 \r
5427   /* [AS] Snapping */\r
5428   case WM_ENTERSIZEMOVE:\r
5429     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5430     if (hwnd == hwndMain) {\r
5431       doingSizing = TRUE;\r
5432       lastSizing = 0;\r
5433     }\r
5434     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5435     break;\r
5436 \r
5437   case WM_SIZING:\r
5438     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5439     if (hwnd == hwndMain) {\r
5440       lastSizing = wParam;\r
5441     }\r
5442     break;\r
5443 \r
5444   case WM_MOVING:\r
5445     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5446       return OnMoving( &sd, hwnd, wParam, lParam );\r
5447 \r
5448   case WM_EXITSIZEMOVE:\r
5449     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5450     if (hwnd == hwndMain) {\r
5451       RECT client;\r
5452       doingSizing = FALSE;\r
5453       InvalidateRect(hwnd, &boardRect, FALSE);\r
5454       GetClientRect(hwnd, &client);\r
5455       ResizeBoard(client.right, client.bottom, lastSizing);\r
5456       lastSizing = 0;\r
5457       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5458     }\r
5459     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5460     break;\r
5461 \r
5462   case WM_DESTROY: /* message: window being destroyed */\r
5463     PostQuitMessage(0);\r
5464     break;\r
5465 \r
5466   case WM_CLOSE:\r
5467     if (hwnd == hwndMain) {\r
5468       ExitEvent(0);\r
5469     }\r
5470     break;\r
5471 \r
5472   default:      /* Passes it on if unprocessed */\r
5473     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5474   }\r
5475   return 0;\r
5476 }\r
5477 \r
5478 /*---------------------------------------------------------------------------*\\r
5479  *\r
5480  * Misc utility routines\r
5481  *\r
5482 \*---------------------------------------------------------------------------*/\r
5483 \r
5484 /*\r
5485  * Decent random number generator, at least not as bad as Windows\r
5486  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5487  */\r
5488 unsigned int randstate;\r
5489 \r
5490 int\r
5491 myrandom(void)\r
5492 {\r
5493   randstate = randstate * 1664525 + 1013904223;\r
5494   return (int) randstate & 0x7fffffff;\r
5495 }\r
5496 \r
5497 void\r
5498 mysrandom(unsigned int seed)\r
5499 {\r
5500   randstate = seed;\r
5501 }\r
5502 \r
5503 \r
5504 /* \r
5505  * returns TRUE if user selects a different color, FALSE otherwise \r
5506  */\r
5507 \r
5508 BOOL\r
5509 ChangeColor(HWND hwnd, COLORREF *which)\r
5510 {\r
5511   static BOOL firstTime = TRUE;\r
5512   static DWORD customColors[16];\r
5513   CHOOSECOLOR cc;\r
5514   COLORREF newcolor;\r
5515   int i;\r
5516   ColorClass ccl;\r
5517 \r
5518   if (firstTime) {\r
5519     /* Make initial colors in use available as custom colors */\r
5520     /* Should we put the compiled-in defaults here instead? */\r
5521     i = 0;\r
5522     customColors[i++] = lightSquareColor & 0xffffff;\r
5523     customColors[i++] = darkSquareColor & 0xffffff;\r
5524     customColors[i++] = whitePieceColor & 0xffffff;\r
5525     customColors[i++] = blackPieceColor & 0xffffff;\r
5526     customColors[i++] = highlightSquareColor & 0xffffff;\r
5527     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5528 \r
5529     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5530       customColors[i++] = textAttribs[ccl].color;\r
5531     }\r
5532     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5533     firstTime = FALSE;\r
5534   }\r
5535 \r
5536   cc.lStructSize = sizeof(cc);\r
5537   cc.hwndOwner = hwnd;\r
5538   cc.hInstance = NULL;\r
5539   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5540   cc.lpCustColors = (LPDWORD) customColors;\r
5541   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5542 \r
5543   if (!ChooseColor(&cc)) return FALSE;\r
5544 \r
5545   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5546   if (newcolor == *which) return FALSE;\r
5547   *which = newcolor;\r
5548   return TRUE;\r
5549 \r
5550   /*\r
5551   InitDrawingColors();\r
5552   InvalidateRect(hwnd, &boardRect, FALSE);\r
5553   */\r
5554 }\r
5555 \r
5556 BOOLEAN\r
5557 MyLoadSound(MySound *ms)\r
5558 {\r
5559   BOOL ok = FALSE;\r
5560   struct stat st;\r
5561   FILE *f;\r
5562 \r
5563   if (ms->data) free(ms->data);\r
5564   ms->data = NULL;\r
5565 \r
5566   switch (ms->name[0]) {\r
5567   case NULLCHAR:\r
5568     /* Silence */\r
5569     ok = TRUE;\r
5570     break;\r
5571   case '$':\r
5572     /* System sound from Control Panel.  Don't preload here. */\r
5573     ok = TRUE;\r
5574     break;\r
5575   case '!':\r
5576     if (ms->name[1] == NULLCHAR) {\r
5577       /* "!" alone = silence */\r
5578       ok = TRUE;\r
5579     } else {\r
5580       /* Builtin wave resource.  Error if not found. */\r
5581       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5582       if (h == NULL) break;\r
5583       ms->data = (void *)LoadResource(hInst, h);\r
5584       if (h == NULL) break;\r
5585       ok = TRUE;\r
5586     }\r
5587     break;\r
5588   default:\r
5589     /* .wav file.  Error if not found. */\r
5590     f = fopen(ms->name, "rb");\r
5591     if (f == NULL) break;\r
5592     if (fstat(fileno(f), &st) < 0) break;\r
5593     ms->data = malloc(st.st_size);\r
5594     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5595     fclose(f);\r
5596     ok = TRUE;\r
5597     break;\r
5598   }\r
5599   if (!ok) {\r
5600     char buf[MSG_SIZ];\r
5601       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5602     DisplayError(buf, GetLastError());\r
5603   }\r
5604   return ok;\r
5605 }\r
5606 \r
5607 BOOLEAN\r
5608 MyPlaySound(MySound *ms)\r
5609 {\r
5610   BOOLEAN ok = FALSE;\r
5611 \r
5612   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5613   switch (ms->name[0]) {\r
5614   case NULLCHAR:\r
5615         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5616     /* Silence */\r
5617     ok = TRUE;\r
5618     break;\r
5619   case '$':\r
5620     /* System sound from Control Panel (deprecated feature).\r
5621        "$" alone or an unset sound name gets default beep (still in use). */\r
5622     if (ms->name[1]) {\r
5623       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5624     }\r
5625     if (!ok) ok = MessageBeep(MB_OK);\r
5626     break; \r
5627   case '!':\r
5628     /* Builtin wave resource, or "!" alone for silence */\r
5629     if (ms->name[1]) {\r
5630       if (ms->data == NULL) return FALSE;\r
5631       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5632     } else {\r
5633       ok = TRUE;\r
5634     }\r
5635     break;\r
5636   default:\r
5637     /* .wav file.  Error if not found. */\r
5638     if (ms->data == NULL) return FALSE;\r
5639     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5640     break;\r
5641   }\r
5642   /* Don't print an error: this can happen innocently if the sound driver\r
5643      is busy; for instance, if another instance of WinBoard is playing\r
5644      a sound at about the same time. */\r
5645   return ok;\r
5646 }\r
5647 \r
5648 \r
5649 LRESULT CALLBACK\r
5650 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5651 {\r
5652   BOOL ok;\r
5653   OPENFILENAME *ofn;\r
5654   static UINT *number; /* gross that this is static */\r
5655 \r
5656   switch (message) {\r
5657   case WM_INITDIALOG: /* message: initialize dialog box */\r
5658     /* Center the dialog over the application window */\r
5659     ofn = (OPENFILENAME *) lParam;\r
5660     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5661       number = (UINT *) ofn->lCustData;\r
5662       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5663     } else {\r
5664       number = NULL;\r
5665     }\r
5666     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5667     Translate(hDlg, 1536);\r
5668     return FALSE;  /* Allow for further processing */\r
5669 \r
5670   case WM_COMMAND:\r
5671     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5672       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5673     }\r
5674     return FALSE;  /* Allow for further processing */\r
5675   }\r
5676   return FALSE;\r
5677 }\r
5678 \r
5679 UINT APIENTRY\r
5680 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5681 {\r
5682   static UINT *number;\r
5683   OPENFILENAME *ofname;\r
5684   OFNOTIFY *ofnot;\r
5685   switch (uiMsg) {\r
5686   case WM_INITDIALOG:\r
5687     Translate(hdlg, DLG_IndexNumber);\r
5688     ofname = (OPENFILENAME *)lParam;\r
5689     number = (UINT *)(ofname->lCustData);\r
5690     break;\r
5691   case WM_NOTIFY:\r
5692     ofnot = (OFNOTIFY *)lParam;\r
5693     if (ofnot->hdr.code == CDN_FILEOK) {\r
5694       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5695     }\r
5696     break;\r
5697   }\r
5698   return 0;\r
5699 }\r
5700 \r
5701 \r
5702 FILE *\r
5703 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5704                char *nameFilt, char *dlgTitle, UINT *number,\r
5705                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5706 {\r
5707   OPENFILENAME openFileName;\r
5708   char buf1[MSG_SIZ];\r
5709   FILE *f;\r
5710 \r
5711   if (fileName == NULL) fileName = buf1;\r
5712   if (defName == NULL) {\r
5713     safeStrCpy(fileName, "*.", sizeof(fileName)/sizeof(fileName[0]) );\r
5714     strcat(fileName, defExt);\r
5715   } else {\r
5716     safeStrCpy(fileName, defName, sizeof(fileName)/sizeof(fileName[0]) );\r
5717   }\r
5718     if (fileTitle) safeStrCpy(fileTitle, "", sizeof(fileTitle)/sizeof(fileTitle[0]) );\r
5719   if (number) *number = 0;\r
5720 \r
5721   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5722   openFileName.hwndOwner         = hwnd;\r
5723   openFileName.hInstance         = (HANDLE) hInst;\r
5724   openFileName.lpstrFilter       = nameFilt;\r
5725   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5726   openFileName.nMaxCustFilter    = 0L;\r
5727   openFileName.nFilterIndex      = 1L;\r
5728   openFileName.lpstrFile         = fileName;\r
5729   openFileName.nMaxFile          = MSG_SIZ;\r
5730   openFileName.lpstrFileTitle    = fileTitle;\r
5731   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5732   openFileName.lpstrInitialDir   = NULL;\r
5733   openFileName.lpstrTitle        = dlgTitle;\r
5734   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5735     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5736     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5737     | (oldDialog ? 0 : OFN_EXPLORER);\r
5738   openFileName.nFileOffset       = 0;\r
5739   openFileName.nFileExtension    = 0;\r
5740   openFileName.lpstrDefExt       = defExt;\r
5741   openFileName.lCustData         = (LONG) number;\r
5742   openFileName.lpfnHook          = oldDialog ?\r
5743     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5744   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5745 \r
5746   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5747                         GetOpenFileName(&openFileName)) {\r
5748     /* open the file */\r
5749     f = fopen(openFileName.lpstrFile, write);\r
5750     if (f == NULL) {\r
5751       MessageBox(hwnd, _("File open failed"), NULL,\r
5752                  MB_OK|MB_ICONEXCLAMATION);\r
5753       return NULL;\r
5754     }\r
5755   } else {\r
5756     int err = CommDlgExtendedError();\r
5757     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5758     return FALSE;\r
5759   }\r
5760   return f;\r
5761 }\r
5762 \r
5763 \r
5764 \r
5765 VOID APIENTRY\r
5766 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5767 {\r
5768   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5769 \r
5770   /*\r
5771    * Get the first pop-up menu in the menu template. This is the\r
5772    * menu that TrackPopupMenu displays.\r
5773    */\r
5774   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5775 \r
5776   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5777 \r
5778   /*\r
5779    * TrackPopup uses screen coordinates, so convert the\r
5780    * coordinates of the mouse click to screen coordinates.\r
5781    */\r
5782   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5783 \r
5784   /* Draw and track the floating pop-up menu. */\r
5785   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5786                  pt.x, pt.y, 0, hwnd, NULL);\r
5787 \r
5788   /* Destroy the menu.*/\r
5789   DestroyMenu(hmenu);\r
5790 }\r
5791    \r
5792 typedef struct {\r
5793   HWND hDlg, hText;\r
5794   int sizeX, sizeY, newSizeX, newSizeY;\r
5795   HDWP hdwp;\r
5796 } ResizeEditPlusButtonsClosure;\r
5797 \r
5798 BOOL CALLBACK\r
5799 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5800 {\r
5801   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5802   RECT rect;\r
5803   POINT pt;\r
5804 \r
5805   if (hChild == cl->hText) return TRUE;\r
5806   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5807   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5808   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5809   ScreenToClient(cl->hDlg, &pt);\r
5810   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5811     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5812   return TRUE;\r
5813 }\r
5814 \r
5815 /* Resize a dialog that has a (rich) edit field filling most of\r
5816    the top, with a row of buttons below */\r
5817 VOID\r
5818 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5819 {\r
5820   RECT rectText;\r
5821   int newTextHeight, newTextWidth;\r
5822   ResizeEditPlusButtonsClosure cl;\r
5823   \r
5824   /*if (IsIconic(hDlg)) return;*/\r
5825   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5826   \r
5827   cl.hdwp = BeginDeferWindowPos(8);\r
5828 \r
5829   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5830   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5831   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5832   if (newTextHeight < 0) {\r
5833     newSizeY += -newTextHeight;\r
5834     newTextHeight = 0;\r
5835   }\r
5836   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5837     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5838 \r
5839   cl.hDlg = hDlg;\r
5840   cl.hText = hText;\r
5841   cl.sizeX = sizeX;\r
5842   cl.sizeY = sizeY;\r
5843   cl.newSizeX = newSizeX;\r
5844   cl.newSizeY = newSizeY;\r
5845   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5846 \r
5847   EndDeferWindowPos(cl.hdwp);\r
5848 }\r
5849 \r
5850 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5851 {\r
5852     RECT    rChild, rParent;\r
5853     int     wChild, hChild, wParent, hParent;\r
5854     int     wScreen, hScreen, xNew, yNew;\r
5855     HDC     hdc;\r
5856 \r
5857     /* Get the Height and Width of the child window */\r
5858     GetWindowRect (hwndChild, &rChild);\r
5859     wChild = rChild.right - rChild.left;\r
5860     hChild = rChild.bottom - rChild.top;\r
5861 \r
5862     /* Get the Height and Width of the parent window */\r
5863     GetWindowRect (hwndParent, &rParent);\r
5864     wParent = rParent.right - rParent.left;\r
5865     hParent = rParent.bottom - rParent.top;\r
5866 \r
5867     /* Get the display limits */\r
5868     hdc = GetDC (hwndChild);\r
5869     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5870     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5871     ReleaseDC(hwndChild, hdc);\r
5872 \r
5873     /* Calculate new X position, then adjust for screen */\r
5874     xNew = rParent.left + ((wParent - wChild) /2);\r
5875     if (xNew < 0) {\r
5876         xNew = 0;\r
5877     } else if ((xNew+wChild) > wScreen) {\r
5878         xNew = wScreen - wChild;\r
5879     }\r
5880 \r
5881     /* Calculate new Y position, then adjust for screen */\r
5882     if( mode == 0 ) {\r
5883         yNew = rParent.top  + ((hParent - hChild) /2);\r
5884     }\r
5885     else {\r
5886         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5887     }\r
5888 \r
5889     if (yNew < 0) {\r
5890         yNew = 0;\r
5891     } else if ((yNew+hChild) > hScreen) {\r
5892         yNew = hScreen - hChild;\r
5893     }\r
5894 \r
5895     /* Set it, and return */\r
5896     return SetWindowPos (hwndChild, NULL,\r
5897                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5898 }\r
5899 \r
5900 /* Center one window over another */\r
5901 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5902 {\r
5903     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5904 }\r
5905 \r
5906 /*---------------------------------------------------------------------------*\\r
5907  *\r
5908  * Startup Dialog functions\r
5909  *\r
5910 \*---------------------------------------------------------------------------*/\r
5911 void\r
5912 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5913 {\r
5914   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5915 \r
5916   while (*cd != NULL) {\r
5917     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5918     cd++;\r
5919   }\r
5920 }\r
5921 \r
5922 void\r
5923 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5924 {\r
5925   char buf1[MAX_ARG_LEN];\r
5926   int len;\r
5927 \r
5928   if (str[0] == '@') {\r
5929     FILE* f = fopen(str + 1, "r");\r
5930     if (f == NULL) {\r
5931       DisplayFatalError(str + 1, errno, 2);\r
5932       return;\r
5933     }\r
5934     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5935     fclose(f);\r
5936     buf1[len] = NULLCHAR;\r
5937     str = buf1;\r
5938   }\r
5939 \r
5940   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5941 \r
5942   for (;;) {\r
5943     char buf[MSG_SIZ];\r
5944     char *end = strchr(str, '\n');\r
5945     if (end == NULL) return;\r
5946     memcpy(buf, str, end - str);\r
5947     buf[end - str] = NULLCHAR;\r
5948     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5949     str = end + 1;\r
5950   }\r
5951 }\r
5952 \r
5953 void\r
5954 SetStartupDialogEnables(HWND hDlg)\r
5955 {\r
5956   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
5957     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5958     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
5959   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5960     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
5961   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
5962     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
5963   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
5964     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
5965   EnableWindow(GetDlgItem(hDlg, IDOK),\r
5966     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5967     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
5968     IsDlgButtonChecked(hDlg, OPT_View));\r
5969 }\r
5970 \r
5971 char *\r
5972 QuoteForFilename(char *filename)\r
5973 {\r
5974   int dquote, space;\r
5975   dquote = strchr(filename, '"') != NULL;\r
5976   space = strchr(filename, ' ') != NULL;\r
5977   if (dquote || space) {\r
5978     if (dquote) {\r
5979       return "'";\r
5980     } else {\r
5981       return "\"";\r
5982     }\r
5983   } else {\r
5984     return "";\r
5985   }\r
5986 }\r
5987 \r
5988 VOID\r
5989 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
5990 {\r
5991   char buf[MSG_SIZ];\r
5992   char *q;\r
5993 \r
5994   InitComboStringsFromOption(hwndCombo, nthnames);\r
5995   q = QuoteForFilename(nthcp);\r
5996     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
5997   if (*nthdir != NULLCHAR) {\r
5998     q = QuoteForFilename(nthdir);\r
5999       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6000   }\r
6001   if (*nthcp == NULLCHAR) {\r
6002     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6003   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6004     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6005     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6006   }\r
6007 }\r
6008 \r
6009 LRESULT CALLBACK\r
6010 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6011 {\r
6012   char buf[MSG_SIZ];\r
6013   HANDLE hwndCombo;\r
6014   char *p;\r
6015 \r
6016   switch (message) {\r
6017   case WM_INITDIALOG:\r
6018     /* Center the dialog */\r
6019     CenterWindow (hDlg, GetDesktopWindow());\r
6020     Translate(hDlg, DLG_Startup);\r
6021     /* Initialize the dialog items */\r
6022     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6023                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6024                   firstChessProgramNames);\r
6025     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6026                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6027                   secondChessProgramNames);\r
6028     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6029     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6030       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6031     if (*appData.icsHelper != NULLCHAR) {\r
6032       char *q = QuoteForFilename(appData.icsHelper);\r
6033       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6034     }\r
6035     if (*appData.icsHost == NULLCHAR) {\r
6036       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6037       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6038     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6039       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6040       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6041     }\r
6042 \r
6043     if (appData.icsActive) {\r
6044       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6045     }\r
6046     else if (appData.noChessProgram) {\r
6047       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6048     }\r
6049     else {\r
6050       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6051     }\r
6052 \r
6053     SetStartupDialogEnables(hDlg);\r
6054     return TRUE;\r
6055 \r
6056   case WM_COMMAND:\r
6057     switch (LOWORD(wParam)) {\r
6058     case IDOK:\r
6059       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6060         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6061         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6062         p = buf;\r
6063         ParseArgs(StringGet, &p);\r
6064         safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6065         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6066         p = buf;\r
6067         ParseArgs(StringGet, &p);\r
6068         appData.noChessProgram = FALSE;\r
6069         appData.icsActive = FALSE;\r
6070       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6071         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6072         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6073         p = buf;\r
6074         ParseArgs(StringGet, &p);\r
6075         if (appData.zippyPlay) {\r
6076           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6077           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6078           p = buf;\r
6079           ParseArgs(StringGet, &p);\r
6080         }\r
6081       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6082         appData.noChessProgram = TRUE;\r
6083         appData.icsActive = FALSE;\r
6084       } else {\r
6085         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6086                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6087         return TRUE;\r
6088       }\r
6089       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6090         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6091         p = buf;\r
6092         ParseArgs(StringGet, &p);\r
6093       }\r
6094       EndDialog(hDlg, TRUE);\r
6095       return TRUE;\r
6096 \r
6097     case IDCANCEL:\r
6098       ExitEvent(0);\r
6099       return TRUE;\r
6100 \r
6101     case IDM_HELPCONTENTS:\r
6102       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6103         MessageBox (GetFocus(),\r
6104                     _("Unable to activate help"),\r
6105                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6106       }\r
6107       break;\r
6108 \r
6109     default:\r
6110       SetStartupDialogEnables(hDlg);\r
6111       break;\r
6112     }\r
6113     break;\r
6114   }\r
6115   return FALSE;\r
6116 }\r
6117 \r
6118 /*---------------------------------------------------------------------------*\\r
6119  *\r
6120  * About box dialog functions\r
6121  *\r
6122 \*---------------------------------------------------------------------------*/\r
6123 \r
6124 /* Process messages for "About" dialog box */\r
6125 LRESULT CALLBACK\r
6126 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6127 {\r
6128   switch (message) {\r
6129   case WM_INITDIALOG: /* message: initialize dialog box */\r
6130     /* Center the dialog over the application window */\r
6131     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6132     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6133     Translate(hDlg, ABOUTBOX);\r
6134     JAWS_COPYRIGHT\r
6135     return (TRUE);\r
6136 \r
6137   case WM_COMMAND: /* message: received a command */\r
6138     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6139         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6140       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6141       return (TRUE);\r
6142     }\r
6143     break;\r
6144   }\r
6145   return (FALSE);\r
6146 }\r
6147 \r
6148 /*---------------------------------------------------------------------------*\\r
6149  *\r
6150  * Comment Dialog functions\r
6151  *\r
6152 \*---------------------------------------------------------------------------*/\r
6153 \r
6154 LRESULT CALLBACK\r
6155 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6156 {\r
6157   static HANDLE hwndText = NULL;\r
6158   int len, newSizeX, newSizeY, flags;\r
6159   static int sizeX, sizeY;\r
6160   char *str;\r
6161   RECT rect;\r
6162   MINMAXINFO *mmi;\r
6163 \r
6164   switch (message) {\r
6165   case WM_INITDIALOG: /* message: initialize dialog box */\r
6166     /* Initialize the dialog items */\r
6167     Translate(hDlg, DLG_EditComment);\r
6168     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6169     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6170     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6171     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6172     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6173     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6174     SetWindowText(hDlg, commentTitle);\r
6175     if (editComment) {\r
6176       SetFocus(hwndText);\r
6177     } else {\r
6178       SetFocus(GetDlgItem(hDlg, IDOK));\r
6179     }\r
6180     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6181                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6182                 MAKELPARAM(FALSE, 0));\r
6183     /* Size and position the dialog */\r
6184     if (!commentDialog) {\r
6185       commentDialog = hDlg;\r
6186       flags = SWP_NOZORDER;\r
6187       GetClientRect(hDlg, &rect);\r
6188       sizeX = rect.right;\r
6189       sizeY = rect.bottom;\r
6190       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6191           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6192         WINDOWPLACEMENT wp;\r
6193         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6194         wp.length = sizeof(WINDOWPLACEMENT);\r
6195         wp.flags = 0;\r
6196         wp.showCmd = SW_SHOW;\r
6197         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6198         wp.rcNormalPosition.left = wpComment.x;\r
6199         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6200         wp.rcNormalPosition.top = wpComment.y;\r
6201         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6202         SetWindowPlacement(hDlg, &wp);\r
6203 \r
6204         GetClientRect(hDlg, &rect);\r
6205         newSizeX = rect.right;\r
6206         newSizeY = rect.bottom;\r
6207         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6208                               newSizeX, newSizeY);\r
6209         sizeX = newSizeX;\r
6210         sizeY = newSizeY;\r
6211       }\r
6212     }\r
6213     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );\r
6214     return FALSE;\r
6215 \r
6216   case WM_COMMAND: /* message: received a command */\r
6217     switch (LOWORD(wParam)) {\r
6218     case IDOK:\r
6219       if (editComment) {\r
6220         char *p, *q;\r
6221         /* Read changed options from the dialog box */\r
6222         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6223         len = GetWindowTextLength(hwndText);\r
6224         str = (char *) malloc(len + 1);\r
6225         GetWindowText(hwndText, str, len + 1);\r
6226         p = q = str;\r
6227         while (*q) {\r
6228           if (*q == '\r')\r
6229             q++;\r
6230           else\r
6231             *p++ = *q++;\r
6232         }\r
6233         *p = NULLCHAR;\r
6234         ReplaceComment(commentIndex, str);\r
6235         free(str);\r
6236       }\r
6237       CommentPopDown();\r
6238       return TRUE;\r
6239 \r
6240     case IDCANCEL:\r
6241     case OPT_CancelComment:\r
6242       CommentPopDown();\r
6243       return TRUE;\r
6244 \r
6245     case OPT_ClearComment:\r
6246       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6247       break;\r
6248 \r
6249     case OPT_EditComment:\r
6250       EditCommentEvent();\r
6251       return TRUE;\r
6252 \r
6253     default:\r
6254       break;\r
6255     }\r
6256     break;\r
6257 \r
6258   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6259         if( wParam == OPT_CommentText ) {\r
6260             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6261 \r
6262             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {\r
6263                 POINTL pt;\r
6264                 LRESULT index;\r
6265 \r
6266                 pt.x = LOWORD( lpMF->lParam );\r
6267                 pt.y = HIWORD( lpMF->lParam );\r
6268 \r
6269                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6270 \r
6271                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6272                 len = GetWindowTextLength(hwndText);\r
6273                 str = (char *) malloc(len + 1);\r
6274                 GetWindowText(hwndText, str, len + 1);\r
6275                 ReplaceComment(commentIndex, str);\r
6276                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6277                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6278                 free(str);\r
6279 \r
6280                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6281                 lpMF->msg = WM_USER;\r
6282 \r
6283                 return TRUE;\r
6284             }\r
6285         }\r
6286         break;\r
6287 \r
6288   case WM_SIZE:\r
6289     newSizeX = LOWORD(lParam);\r
6290     newSizeY = HIWORD(lParam);\r
6291     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6292     sizeX = newSizeX;\r
6293     sizeY = newSizeY;\r
6294     break;\r
6295 \r
6296   case WM_GETMINMAXINFO:\r
6297     /* Prevent resizing window too small */\r
6298     mmi = (MINMAXINFO *) lParam;\r
6299     mmi->ptMinTrackSize.x = 100;\r
6300     mmi->ptMinTrackSize.y = 100;\r
6301     break;\r
6302   }\r
6303   return FALSE;\r
6304 }\r
6305 \r
6306 VOID\r
6307 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6308 {\r
6309   FARPROC lpProc;\r
6310   char *p, *q;\r
6311 \r
6312   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6313 \r
6314   if (str == NULL) str = "";\r
6315   p = (char *) malloc(2 * strlen(str) + 2);\r
6316   q = p;\r
6317   while (*str) {\r
6318     if (*str == '\n') *q++ = '\r';\r
6319     *q++ = *str++;\r
6320   }\r
6321   *q = NULLCHAR;\r
6322   if (commentText != NULL) free(commentText);\r
6323 \r
6324   commentIndex = index;\r
6325   commentTitle = title;\r
6326   commentText = p;\r
6327   editComment = edit;\r
6328 \r
6329   if (commentDialog) {\r
6330     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6331     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6332   } else {\r
6333     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6334     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6335                  hwndMain, (DLGPROC)lpProc);\r
6336     FreeProcInstance(lpProc);\r
6337   }\r
6338   commentUp = TRUE;\r
6339 }\r
6340 \r
6341 \r
6342 /*---------------------------------------------------------------------------*\\r
6343  *\r
6344  * Type-in move dialog functions\r
6345  * \r
6346 \*---------------------------------------------------------------------------*/\r
6347 \r
6348 LRESULT CALLBACK\r
6349 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6350 {\r
6351   char move[MSG_SIZ];\r
6352   HWND hInput;\r
6353   ChessMove moveType;\r
6354   int fromX, fromY, toX, toY;\r
6355   char promoChar;\r
6356 \r
6357   switch (message) {\r
6358   case WM_INITDIALOG:\r
6359     move[0] = (char) lParam;\r
6360     move[1] = NULLCHAR;\r
6361     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6362     Translate(hDlg, DLG_TypeInMove);\r
6363     hInput = GetDlgItem(hDlg, OPT_Move);\r
6364     SetWindowText(hInput, move);\r
6365     SetFocus(hInput);\r
6366     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6367     return FALSE;\r
6368 \r
6369   case WM_COMMAND:\r
6370     switch (LOWORD(wParam)) {\r
6371     case IDOK:
6372       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6373       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6374       { int n; Board board;\r
6375         // [HGM] FENedit\r
6376         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
6377                 EditPositionPasteFEN(move);\r
6378                 EndDialog(hDlg, TRUE);\r
6379                 return TRUE;\r
6380         }\r
6381         // [HGM] movenum: allow move number to be typed in any mode\r
6382         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
6383           ToNrEvent(2*n-1);\r
6384           EndDialog(hDlg, TRUE);\r
6385           return TRUE;\r
6386         }\r
6387       }\r
6388       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6389         gameMode != Training) {\r
6390         DisplayMoveError(_("Displayed move is not current"));\r
6391       } else {\r
6392 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
6393         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6394           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
6395         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
6396         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6397           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6398           if (gameMode != Training)\r
6399               forwardMostMove = currentMove;\r
6400           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6401         } else {\r
6402           DisplayMoveError(_("Could not parse move"));\r
6403         }\r
6404       }\r
6405       EndDialog(hDlg, TRUE);\r
6406       return TRUE;\r
6407     case IDCANCEL:\r
6408       EndDialog(hDlg, FALSE);\r
6409       return TRUE;\r
6410     default:\r
6411       break;\r
6412     }\r
6413     break;\r
6414   }\r
6415   return FALSE;\r
6416 }\r
6417 \r
6418 VOID\r
6419 PopUpMoveDialog(char firstchar)\r
6420 {\r
6421     FARPROC lpProc;\r
6422     \r
6423     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6424         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6425         gameMode == AnalyzeMode || gameMode == EditGame || \r
6426         gameMode == EditPosition || gameMode == IcsExamining ||\r
6427         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6428         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
6429                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
6430                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
6431         gameMode == Training) {\r
6432       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6433       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6434         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6435       FreeProcInstance(lpProc);\r
6436     }\r
6437 }\r
6438 \r
6439 /*---------------------------------------------------------------------------*\\r
6440  *\r
6441  * Type-in name dialog functions\r
6442  * \r
6443 \*---------------------------------------------------------------------------*/\r
6444 \r
6445 LRESULT CALLBACK\r
6446 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6447 {\r
6448   char move[MSG_SIZ];\r
6449   HWND hInput;\r
6450 \r
6451   switch (message) {\r
6452   case WM_INITDIALOG:\r
6453     move[0] = (char) lParam;\r
6454     move[1] = NULLCHAR;\r
6455     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6456     Translate(hDlg, DLG_TypeInName);\r
6457     hInput = GetDlgItem(hDlg, OPT_Name);\r
6458     SetWindowText(hInput, move);\r
6459     SetFocus(hInput);\r
6460     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6461     return FALSE;\r
6462 \r
6463   case WM_COMMAND:\r
6464     switch (LOWORD(wParam)) {\r
6465     case IDOK:\r
6466       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6467       appData.userName = strdup(move);\r
6468       SetUserLogo();\r
6469       SetGameInfo();\r
6470       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6471         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6472         DisplayTitle(move);\r
6473       }\r
6474 \r
6475 \r
6476       EndDialog(hDlg, TRUE);\r
6477       return TRUE;\r
6478     case IDCANCEL:\r
6479       EndDialog(hDlg, FALSE);\r
6480       return TRUE;\r
6481     default:\r
6482       break;\r
6483     }\r
6484     break;\r
6485   }\r
6486   return FALSE;\r
6487 }\r
6488 \r
6489 VOID\r
6490 PopUpNameDialog(char firstchar)\r
6491 {\r
6492     FARPROC lpProc;\r
6493     \r
6494       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6495       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6496         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6497       FreeProcInstance(lpProc);\r
6498 }\r
6499 \r
6500 /*---------------------------------------------------------------------------*\\r
6501  *\r
6502  *  Error dialogs\r
6503  * \r
6504 \*---------------------------------------------------------------------------*/\r
6505 \r
6506 /* Nonmodal error box */\r
6507 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6508                              WPARAM wParam, LPARAM lParam);\r
6509 \r
6510 VOID\r
6511 ErrorPopUp(char *title, char *content)\r
6512 {\r
6513   FARPROC lpProc;\r
6514   char *p, *q;\r
6515   BOOLEAN modal = hwndMain == NULL;\r
6516 \r
6517   p = content;\r
6518   q = errorMessage;\r
6519   while (*p) {\r
6520     if (*p == '\n') {\r
6521       if (modal) {\r
6522         *q++ = ' ';\r
6523         p++;\r
6524       } else {\r
6525         *q++ = '\r';\r
6526         *q++ = *p++;\r
6527       }\r
6528     } else {\r
6529       *q++ = *p++;\r
6530     }\r
6531   }\r
6532   *q = NULLCHAR;\r
6533   strncpy(errorTitle, title, sizeof(errorTitle));\r
6534   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6535   \r
6536   if (modal) {\r
6537     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6538   } else {\r
6539     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6540     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6541                  hwndMain, (DLGPROC)lpProc);\r
6542     FreeProcInstance(lpProc);\r
6543   }\r
6544 }\r
6545 \r
6546 VOID\r
6547 ErrorPopDown()\r
6548 {\r
6549   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6550   if (errorDialog == NULL) return;\r
6551   DestroyWindow(errorDialog);\r
6552   errorDialog = NULL;\r
6553   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6554 }\r
6555 \r
6556 LRESULT CALLBACK\r
6557 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6558 {\r
6559   HANDLE hwndText;\r
6560   RECT rChild;\r
6561 \r
6562   switch (message) {\r
6563   case WM_INITDIALOG:\r
6564     GetWindowRect(hDlg, &rChild);\r
6565 \r
6566     /*\r
6567     SetWindowPos(hDlg, NULL, rChild.left,\r
6568       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6569       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6570     */\r
6571 \r
6572     /* \r
6573         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6574         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6575         and it doesn't work when you resize the dialog.\r
6576         For now, just give it a default position.\r
6577     */\r
6578     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6579     Translate(hDlg, DLG_Error);\r
6580 \r
6581     errorDialog = hDlg;\r
6582     SetWindowText(hDlg, errorTitle);\r
6583     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6584     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6585     return FALSE;\r
6586 \r
6587   case WM_COMMAND:\r
6588     switch (LOWORD(wParam)) {\r
6589     case IDOK:\r
6590     case IDCANCEL:\r
6591       if (errorDialog == hDlg) errorDialog = NULL;\r
6592       DestroyWindow(hDlg);\r
6593       return TRUE;\r
6594 \r
6595     default:\r
6596       break;\r
6597     }\r
6598     break;\r
6599   }\r
6600   return FALSE;\r
6601 }\r
6602 \r
6603 #ifdef GOTHIC\r
6604 HWND gothicDialog = NULL;\r
6605 \r
6606 LRESULT CALLBACK\r
6607 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6608 {\r
6609   HANDLE hwndText;\r
6610   RECT rChild;\r
6611   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6612 \r
6613   switch (message) {\r
6614   case WM_INITDIALOG:\r
6615     GetWindowRect(hDlg, &rChild);\r
6616 \r
6617     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6618                                                              SWP_NOZORDER);\r
6619 \r
6620     /* \r
6621         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6622         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6623         and it doesn't work when you resize the dialog.\r
6624         For now, just give it a default position.\r
6625     */\r
6626     gothicDialog = hDlg;\r
6627     SetWindowText(hDlg, errorTitle);\r
6628     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6629     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6630     return FALSE;\r
6631 \r
6632   case WM_COMMAND:\r
6633     switch (LOWORD(wParam)) {\r
6634     case IDOK:\r
6635     case IDCANCEL:\r
6636       if (errorDialog == hDlg) errorDialog = NULL;\r
6637       DestroyWindow(hDlg);\r
6638       return TRUE;\r
6639 \r
6640     default:\r
6641       break;\r
6642     }\r
6643     break;\r
6644   }\r
6645   return FALSE;\r
6646 }\r
6647 \r
6648 VOID\r
6649 GothicPopUp(char *title, VariantClass variant)\r
6650 {\r
6651   FARPROC lpProc;\r
6652   static char *lastTitle;\r
6653 \r
6654   strncpy(errorTitle, title, sizeof(errorTitle));\r
6655   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6656 \r
6657   if(lastTitle != title && gothicDialog != NULL) {\r
6658     DestroyWindow(gothicDialog);\r
6659     gothicDialog = NULL;\r
6660   }\r
6661   if(variant != VariantNormal && gothicDialog == NULL) {\r
6662     title = lastTitle;\r
6663     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6664     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6665                  hwndMain, (DLGPROC)lpProc);\r
6666     FreeProcInstance(lpProc);\r
6667   }\r
6668 }\r
6669 #endif\r
6670 \r
6671 /*---------------------------------------------------------------------------*\\r
6672  *\r
6673  *  Ics Interaction console functions\r
6674  *\r
6675 \*---------------------------------------------------------------------------*/\r
6676 \r
6677 #define HISTORY_SIZE 64\r
6678 static char *history[HISTORY_SIZE];\r
6679 int histIn = 0, histP = 0;\r
6680 \r
6681 VOID\r
6682 SaveInHistory(char *cmd)\r
6683 {\r
6684   if (history[histIn] != NULL) {\r
6685     free(history[histIn]);\r
6686     history[histIn] = NULL;\r
6687   }\r
6688   if (*cmd == NULLCHAR) return;\r
6689   history[histIn] = StrSave(cmd);\r
6690   histIn = (histIn + 1) % HISTORY_SIZE;\r
6691   if (history[histIn] != NULL) {\r
6692     free(history[histIn]);\r
6693     history[histIn] = NULL;\r
6694   }\r
6695   histP = histIn;\r
6696 }\r
6697 \r
6698 char *\r
6699 PrevInHistory(char *cmd)\r
6700 {\r
6701   int newhp;\r
6702   if (histP == histIn) {\r
6703     if (history[histIn] != NULL) free(history[histIn]);\r
6704     history[histIn] = StrSave(cmd);\r
6705   }\r
6706   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6707   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6708   histP = newhp;\r
6709   return history[histP];\r
6710 }\r
6711 \r
6712 char *\r
6713 NextInHistory()\r
6714 {\r
6715   if (histP == histIn) return NULL;\r
6716   histP = (histP + 1) % HISTORY_SIZE;\r
6717   return history[histP];   \r
6718 }\r
6719 \r
6720 HMENU\r
6721 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6722 {\r
6723   HMENU hmenu, h;\r
6724   int i = 0;\r
6725   hmenu = LoadMenu(hInst, "TextMenu");\r
6726   h = GetSubMenu(hmenu, 0);\r
6727   while (e->item) {\r
6728     if (strcmp(e->item, "-") == 0) {\r
6729       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6730     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6731       int flags = MF_STRING, j = 0;\r
6732       if (e->item[0] == '|') {\r
6733         flags |= MF_MENUBARBREAK;\r
6734         j++;\r
6735       }\r
6736       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6737       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6738     }\r
6739     e++;\r
6740     i++;\r
6741   } \r
6742   return hmenu;\r
6743 }\r
6744 \r
6745 WNDPROC consoleTextWindowProc;\r
6746 \r
6747 void\r
6748 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6749 {\r
6750   char buf[MSG_SIZ], name[MSG_SIZ];\r
6751   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6752   CHARRANGE sel;\r
6753 \r
6754   if (!getname) {\r
6755     SetWindowText(hInput, command);\r
6756     if (immediate) {\r
6757       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6758     } else {\r
6759       sel.cpMin = 999999;\r
6760       sel.cpMax = 999999;\r
6761       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6762       SetFocus(hInput);\r
6763     }\r
6764     return;\r
6765   }    \r
6766   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6767   if (sel.cpMin == sel.cpMax) {\r
6768     /* Expand to surrounding word */\r
6769     TEXTRANGE tr;\r
6770     do {\r
6771       tr.chrg.cpMax = sel.cpMin;\r
6772       tr.chrg.cpMin = --sel.cpMin;\r
6773       if (sel.cpMin < 0) break;\r
6774       tr.lpstrText = name;\r
6775       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6776     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6777     sel.cpMin++;\r
6778 \r
6779     do {\r
6780       tr.chrg.cpMin = sel.cpMax;\r
6781       tr.chrg.cpMax = ++sel.cpMax;\r
6782       tr.lpstrText = name;\r
6783       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6784     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6785     sel.cpMax--;\r
6786 \r
6787     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6788       MessageBeep(MB_ICONEXCLAMATION);\r
6789       return;\r
6790     }\r
6791     tr.chrg = sel;\r
6792     tr.lpstrText = name;\r
6793     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6794   } else {\r
6795     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6796       MessageBeep(MB_ICONEXCLAMATION);\r
6797       return;\r
6798     }\r
6799     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6800   }\r
6801   if (immediate) {\r
6802     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6803     SetWindowText(hInput, buf);\r
6804     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6805   } else {\r
6806     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6807       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6808     SetWindowText(hInput, buf);\r
6809     sel.cpMin = 999999;\r
6810     sel.cpMax = 999999;\r
6811     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6812     SetFocus(hInput);\r
6813   }\r
6814 }\r
6815 \r
6816 LRESULT CALLBACK \r
6817 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6818 {\r
6819   HWND hInput;\r
6820   CHARRANGE sel;\r
6821 \r
6822   switch (message) {\r
6823   case WM_KEYDOWN:\r
6824     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6825     switch (wParam) {\r
6826     case VK_PRIOR:\r
6827       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6828       return 0;\r
6829     case VK_NEXT:\r
6830       sel.cpMin = 999999;\r
6831       sel.cpMax = 999999;\r
6832       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6833       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6834       return 0;\r
6835     }\r
6836     break;\r
6837   case WM_CHAR:\r
6838    if(wParam != '\022') {\r
6839     if (wParam == '\t') {\r
6840       if (GetKeyState(VK_SHIFT) < 0) {\r
6841         /* shifted */\r
6842         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6843         if (buttonDesc[0].hwnd) {\r
6844           SetFocus(buttonDesc[0].hwnd);\r
6845         } else {\r
6846           SetFocus(hwndMain);\r
6847         }\r
6848       } else {\r
6849         /* unshifted */\r
6850         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6851       }\r
6852     } else {\r
6853       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6854       JAWS_DELETE( SetFocus(hInput); )\r
6855       SendMessage(hInput, message, wParam, lParam);\r
6856     }\r
6857     return 0;\r
6858    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
6859   case WM_RBUTTONDOWN:\r
6860     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6861       /* Move selection here if it was empty */\r
6862       POINT pt;\r
6863       pt.x = LOWORD(lParam);\r
6864       pt.y = HIWORD(lParam);\r
6865       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6866       if (sel.cpMin == sel.cpMax) {\r
6867         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6868         sel.cpMax = sel.cpMin;\r
6869         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6870       }\r
6871       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6872 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6873       POINT pt;\r
6874       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6875       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6876       if (sel.cpMin == sel.cpMax) {\r
6877         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6878         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6879       }\r
6880       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6881         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6882       }\r
6883       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6884       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6885       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6886       MenuPopup(hwnd, pt, hmenu, -1);\r
6887 }\r
6888     }\r
6889     return 0;\r
6890   case WM_RBUTTONUP:\r
6891     if (GetKeyState(VK_SHIFT) & ~1) {\r
6892       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6893         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6894     }\r
6895     return 0;\r
6896   case WM_PASTE:\r
6897     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6898     SetFocus(hInput);\r
6899     return SendMessage(hInput, message, wParam, lParam);\r
6900   case WM_MBUTTONDOWN:\r
6901     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6902   case WM_COMMAND:\r
6903     switch (LOWORD(wParam)) {\r
6904     case IDM_QuickPaste:\r
6905       {\r
6906         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6907         if (sel.cpMin == sel.cpMax) {\r
6908           MessageBeep(MB_ICONEXCLAMATION);\r
6909           return 0;\r
6910         }\r
6911         SendMessage(hwnd, WM_COPY, 0, 0);\r
6912         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6913         SendMessage(hInput, WM_PASTE, 0, 0);\r
6914         SetFocus(hInput);\r
6915         return 0;\r
6916       }\r
6917     case IDM_Cut:\r
6918       SendMessage(hwnd, WM_CUT, 0, 0);\r
6919       return 0;\r
6920     case IDM_Paste:\r
6921       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6922       return 0;\r
6923     case IDM_Copy:\r
6924       SendMessage(hwnd, WM_COPY, 0, 0);\r
6925       return 0;\r
6926     default:\r
6927       {\r
6928         int i = LOWORD(wParam) - IDM_CommandX;\r
6929         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6930             icsTextMenuEntry[i].command != NULL) {\r
6931           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6932                    icsTextMenuEntry[i].getname,\r
6933                    icsTextMenuEntry[i].immediate);\r
6934           return 0;\r
6935         }\r
6936       }\r
6937       break;\r
6938     }\r
6939     break;\r
6940   }\r
6941   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6942 }\r
6943 \r
6944 WNDPROC consoleInputWindowProc;\r
6945 \r
6946 LRESULT CALLBACK\r
6947 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6948 {\r
6949   char buf[MSG_SIZ];\r
6950   char *p;\r
6951   static BOOL sendNextChar = FALSE;\r
6952   static BOOL quoteNextChar = FALSE;\r
6953   InputSource *is = consoleInputSource;\r
6954   CHARFORMAT cf;\r
6955   CHARRANGE sel;\r
6956 \r
6957   switch (message) {\r
6958   case WM_CHAR:\r
6959     if (!appData.localLineEditing || sendNextChar) {\r
6960       is->buf[0] = (CHAR) wParam;\r
6961       is->count = 1;\r
6962       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6963       sendNextChar = FALSE;\r
6964       return 0;\r
6965     }\r
6966     if (quoteNextChar) {\r
6967       buf[0] = (char) wParam;\r
6968       buf[1] = NULLCHAR;\r
6969       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
6970       quoteNextChar = FALSE;\r
6971       return 0;\r
6972     }\r
6973     switch (wParam) {\r
6974     case '\r':   /* Enter key */\r
6975       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
6976       if (consoleEcho) SaveInHistory(is->buf);\r
6977       is->buf[is->count++] = '\n';\r
6978       is->buf[is->count] = NULLCHAR;\r
6979       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6980       if (consoleEcho) {\r
6981         ConsoleOutput(is->buf, is->count, TRUE);\r
6982       } else if (appData.localLineEditing) {\r
6983         ConsoleOutput("\n", 1, TRUE);\r
6984       }\r
6985       /* fall thru */\r
6986     case '\033': /* Escape key */\r
6987       SetWindowText(hwnd, "");\r
6988       cf.cbSize = sizeof(CHARFORMAT);\r
6989       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
6990       if (consoleEcho) {\r
6991         cf.crTextColor = textAttribs[ColorNormal].color;\r
6992       } else {\r
6993         cf.crTextColor = COLOR_ECHOOFF;\r
6994       }\r
6995       cf.dwEffects = textAttribs[ColorNormal].effects;\r
6996       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
6997       return 0;\r
6998     case '\t':   /* Tab key */\r
6999       if (GetKeyState(VK_SHIFT) < 0) {\r
7000         /* shifted */\r
7001         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7002       } else {\r
7003         /* unshifted */\r
7004         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7005         if (buttonDesc[0].hwnd) {\r
7006           SetFocus(buttonDesc[0].hwnd);\r
7007         } else {\r
7008           SetFocus(hwndMain);\r
7009         }\r
7010       }\r
7011       return 0;\r
7012     case '\023': /* Ctrl+S */\r
7013       sendNextChar = TRUE;\r
7014       return 0;\r
7015     case '\021': /* Ctrl+Q */\r
7016       quoteNextChar = TRUE;\r
7017       return 0;\r
7018     JAWS_REPLAY\r
7019     default:\r
7020       break;\r
7021     }\r
7022     break;\r
7023   case WM_KEYDOWN:\r
7024     switch (wParam) {\r
7025     case VK_UP:\r
7026       GetWindowText(hwnd, buf, MSG_SIZ);\r
7027       p = PrevInHistory(buf);\r
7028       if (p != NULL) {\r
7029         SetWindowText(hwnd, p);\r
7030         sel.cpMin = 999999;\r
7031         sel.cpMax = 999999;\r
7032         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7033         return 0;\r
7034       }\r
7035       break;\r
7036     case VK_DOWN:\r
7037       p = NextInHistory();\r
7038       if (p != NULL) {\r
7039         SetWindowText(hwnd, p);\r
7040         sel.cpMin = 999999;\r
7041         sel.cpMax = 999999;\r
7042         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7043         return 0;\r
7044       }\r
7045       break;\r
7046     case VK_HOME:\r
7047     case VK_END:\r
7048       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7049       /* fall thru */\r
7050     case VK_PRIOR:\r
7051     case VK_NEXT:\r
7052       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7053       return 0;\r
7054     }\r
7055     break;\r
7056   case WM_MBUTTONDOWN:\r
7057     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7058       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7059     break;\r
7060   case WM_RBUTTONUP:\r
7061     if (GetKeyState(VK_SHIFT) & ~1) {\r
7062       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7063         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7064     } else {\r
7065       POINT pt;\r
7066       HMENU hmenu;\r
7067       hmenu = LoadMenu(hInst, "InputMenu");\r
7068       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7069       if (sel.cpMin == sel.cpMax) {\r
7070         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7071         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7072       }\r
7073       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7074         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7075       }\r
7076       pt.x = LOWORD(lParam);\r
7077       pt.y = HIWORD(lParam);\r
7078       MenuPopup(hwnd, pt, hmenu, -1);\r
7079     }\r
7080     return 0;\r
7081   case WM_COMMAND:\r
7082     switch (LOWORD(wParam)) { \r
7083     case IDM_Undo:\r
7084       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7085       return 0;\r
7086     case IDM_SelectAll:\r
7087       sel.cpMin = 0;\r
7088       sel.cpMax = -1; /*999999?*/\r
7089       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7090       return 0;\r
7091     case IDM_Cut:\r
7092       SendMessage(hwnd, WM_CUT, 0, 0);\r
7093       return 0;\r
7094     case IDM_Paste:\r
7095       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7096       return 0;\r
7097     case IDM_Copy:\r
7098       SendMessage(hwnd, WM_COPY, 0, 0);\r
7099       return 0;\r
7100     }\r
7101     break;\r
7102   }\r
7103   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7104 }\r
7105 \r
7106 #define CO_MAX  100000\r
7107 #define CO_TRIM   1000\r
7108 \r
7109 LRESULT CALLBACK\r
7110 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7111 {\r
7112   static SnapData sd;\r
7113   HWND hText, hInput;\r
7114   RECT rect;\r
7115   static int sizeX, sizeY;\r
7116   int newSizeX, newSizeY;\r
7117   MINMAXINFO *mmi;\r
7118   WORD wMask;\r
7119 \r
7120   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7121   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7122 \r
7123   switch (message) {\r
7124   case WM_NOTIFY:\r
7125     if (((NMHDR*)lParam)->code == EN_LINK)\r
7126     {\r
7127       ENLINK *pLink = (ENLINK*)lParam;\r
7128       if (pLink->msg == WM_LBUTTONUP)\r
7129       {\r
7130         TEXTRANGE tr;\r
7131 \r
7132         tr.chrg = pLink->chrg;\r
7133         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7134         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7135         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7136         free(tr.lpstrText);\r
7137       }\r
7138     }\r
7139     break;\r
7140   case WM_INITDIALOG: /* message: initialize dialog box */\r
7141     hwndConsole = hDlg;\r
7142     SetFocus(hInput);\r
7143     consoleTextWindowProc = (WNDPROC)\r
7144       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7145     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7146     consoleInputWindowProc = (WNDPROC)\r
7147       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7148     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7149     Colorize(ColorNormal, TRUE);\r
7150     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7151     ChangedConsoleFont();\r
7152     GetClientRect(hDlg, &rect);\r
7153     sizeX = rect.right;\r
7154     sizeY = rect.bottom;\r
7155     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7156         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7157       WINDOWPLACEMENT wp;\r
7158       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7159       wp.length = sizeof(WINDOWPLACEMENT);\r
7160       wp.flags = 0;\r
7161       wp.showCmd = SW_SHOW;\r
7162       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7163       wp.rcNormalPosition.left = wpConsole.x;\r
7164       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7165       wp.rcNormalPosition.top = wpConsole.y;\r
7166       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7167       SetWindowPlacement(hDlg, &wp);\r
7168     }\r
7169 \r
7170    // [HGM] Chessknight's change 2004-07-13\r
7171    else { /* Determine Defaults */\r
7172        WINDOWPLACEMENT wp;\r
7173        wpConsole.x = wpMain.width + 1;\r
7174        wpConsole.y = wpMain.y;\r
7175        wpConsole.width = screenWidth -  wpMain.width;\r
7176        wpConsole.height = wpMain.height;\r
7177        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7178        wp.length = sizeof(WINDOWPLACEMENT);\r
7179        wp.flags = 0;\r
7180        wp.showCmd = SW_SHOW;\r
7181        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7182        wp.rcNormalPosition.left = wpConsole.x;\r
7183        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7184        wp.rcNormalPosition.top = wpConsole.y;\r
7185        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7186        SetWindowPlacement(hDlg, &wp);\r
7187     }\r
7188 \r
7189    // Allow hText to highlight URLs and send notifications on them\r
7190    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7191    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7192    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7193    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
7194 \r
7195     return FALSE;\r
7196 \r
7197   case WM_SETFOCUS:\r
7198     SetFocus(hInput);\r
7199     return 0;\r
7200 \r
7201   case WM_CLOSE:\r
7202     ExitEvent(0);\r
7203     /* not reached */\r
7204     break;\r
7205 \r
7206   case WM_SIZE:\r
7207     if (IsIconic(hDlg)) break;\r
7208     newSizeX = LOWORD(lParam);\r
7209     newSizeY = HIWORD(lParam);\r
7210     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7211       RECT rectText, rectInput;\r
7212       POINT pt;\r
7213       int newTextHeight, newTextWidth;\r
7214       GetWindowRect(hText, &rectText);\r
7215       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7216       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7217       if (newTextHeight < 0) {\r
7218         newSizeY += -newTextHeight;\r
7219         newTextHeight = 0;\r
7220       }\r
7221       SetWindowPos(hText, NULL, 0, 0,\r
7222         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7223       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7224       pt.x = rectInput.left;\r
7225       pt.y = rectInput.top + newSizeY - sizeY;\r
7226       ScreenToClient(hDlg, &pt);\r
7227       SetWindowPos(hInput, NULL, \r
7228         pt.x, pt.y, /* needs client coords */   \r
7229         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7230         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7231     }\r
7232     sizeX = newSizeX;\r
7233     sizeY = newSizeY;\r
7234     break;\r
7235 \r
7236   case WM_GETMINMAXINFO:\r
7237     /* Prevent resizing window too small */\r
7238     mmi = (MINMAXINFO *) lParam;\r
7239     mmi->ptMinTrackSize.x = 100;\r
7240     mmi->ptMinTrackSize.y = 100;\r
7241     break;\r
7242 \r
7243   /* [AS] Snapping */\r
7244   case WM_ENTERSIZEMOVE:\r
7245     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7246 \r
7247   case WM_SIZING:\r
7248     return OnSizing( &sd, hDlg, wParam, lParam );\r
7249 \r
7250   case WM_MOVING:\r
7251     return OnMoving( &sd, hDlg, wParam, lParam );\r
7252 \r
7253   case WM_EXITSIZEMOVE:\r
7254         UpdateICSWidth(hText);\r
7255     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7256   }\r
7257 \r
7258   return DefWindowProc(hDlg, message, wParam, lParam);\r
7259 }\r
7260 \r
7261 \r
7262 VOID\r
7263 ConsoleCreate()\r
7264 {\r
7265   HWND hCons;\r
7266   if (hwndConsole) return;\r
7267   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7268   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7269 }\r
7270 \r
7271 \r
7272 VOID\r
7273 ConsoleOutput(char* data, int length, int forceVisible)\r
7274 {\r
7275   HWND hText;\r
7276   int trim, exlen;\r
7277   char *p, *q;\r
7278   char buf[CO_MAX+1];\r
7279   POINT pEnd;\r
7280   RECT rect;\r
7281   static int delayLF = 0;\r
7282   CHARRANGE savesel, sel;\r
7283 \r
7284   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7285   p = data;\r
7286   q = buf;\r
7287   if (delayLF) {\r
7288     *q++ = '\r';\r
7289     *q++ = '\n';\r
7290     delayLF = 0;\r
7291   }\r
7292   while (length--) {\r
7293     if (*p == '\n') {\r
7294       if (*++p) {\r
7295         *q++ = '\r';\r
7296         *q++ = '\n';\r
7297       } else {\r
7298         delayLF = 1;\r
7299       }\r
7300     } else if (*p == '\007') {\r
7301        MyPlaySound(&sounds[(int)SoundBell]);\r
7302        p++;\r
7303     } else {\r
7304       *q++ = *p++;\r
7305     }\r
7306   }\r
7307   *q = NULLCHAR;\r
7308   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7309   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7310   /* Save current selection */\r
7311   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7312   exlen = GetWindowTextLength(hText);\r
7313   /* Find out whether current end of text is visible */\r
7314   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7315   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7316   /* Trim existing text if it's too long */\r
7317   if (exlen + (q - buf) > CO_MAX) {\r
7318     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7319     sel.cpMin = 0;\r
7320     sel.cpMax = trim;\r
7321     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7322     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7323     exlen -= trim;\r
7324     savesel.cpMin -= trim;\r
7325     savesel.cpMax -= trim;\r
7326     if (exlen < 0) exlen = 0;\r
7327     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7328     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7329   }\r
7330   /* Append the new text */\r
7331   sel.cpMin = exlen;\r
7332   sel.cpMax = exlen;\r
7333   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7334   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7335   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7336   if (forceVisible || exlen == 0 ||\r
7337       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7338        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7339     /* Scroll to make new end of text visible if old end of text\r
7340        was visible or new text is an echo of user typein */\r
7341     sel.cpMin = 9999999;\r
7342     sel.cpMax = 9999999;\r
7343     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7344     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7345     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7346     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7347   }\r
7348   if (savesel.cpMax == exlen || forceVisible) {\r
7349     /* Move insert point to new end of text if it was at the old\r
7350        end of text or if the new text is an echo of user typein */\r
7351     sel.cpMin = 9999999;\r
7352     sel.cpMax = 9999999;\r
7353     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7354   } else {\r
7355     /* Restore previous selection */\r
7356     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7357   }\r
7358   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7359 }\r
7360 \r
7361 /*---------*/\r
7362 \r
7363 \r
7364 void\r
7365 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7366 {\r
7367   char buf[100];\r
7368   char *str;\r
7369   COLORREF oldFg, oldBg;\r
7370   HFONT oldFont;\r
7371   RECT rect;\r
7372 \r
7373   if(copyNumber > 1)
7374     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7375 \r
7376   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7377   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7378   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7379 \r
7380   rect.left = x;\r
7381   rect.right = x + squareSize;\r
7382   rect.top  = y;\r
7383   rect.bottom = y + squareSize;\r
7384   str = buf;\r
7385 \r
7386   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7387                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7388              y, ETO_CLIPPED|ETO_OPAQUE,\r
7389              &rect, str, strlen(str), NULL);\r
7390 \r
7391   (void) SetTextColor(hdc, oldFg);\r
7392   (void) SetBkColor(hdc, oldBg);\r
7393   (void) SelectObject(hdc, oldFont);\r
7394 }\r
7395 \r
7396 void\r
7397 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7398               RECT *rect, char *color, char *flagFell)\r
7399 {\r
7400   char buf[100];\r
7401   char *str;\r
7402   COLORREF oldFg, oldBg;\r
7403   HFONT oldFont;\r
7404 \r
7405   if (appData.clockMode) {\r
7406     if (tinyLayout)\r
7407       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7408     else\r
7409       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7410     str = buf;\r
7411   } else {\r
7412     str = color;\r
7413   }\r
7414 \r
7415   if (highlight) {\r
7416     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7417     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7418   } else {\r
7419     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7420     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7421   }\r
7422   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7423 \r
7424   JAWS_SILENCE\r
7425 \r
7426   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7427              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7428              rect, str, strlen(str), NULL);\r
7429   if(logoHeight > 0 && appData.clockMode) {\r
7430       RECT r;\r
7431       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s %s", buf+7, flagFell);\r
7432       r.top = rect->top + logoHeight/2;\r
7433       r.left = rect->left;\r
7434       r.right = rect->right;\r
7435       r.bottom = rect->bottom;\r
7436       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7437                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7438                  &r, str, strlen(str), NULL);\r
7439   }\r
7440   (void) SetTextColor(hdc, oldFg);\r
7441   (void) SetBkColor(hdc, oldBg);\r
7442   (void) SelectObject(hdc, oldFont);\r
7443 }\r
7444 \r
7445 \r
7446 int\r
7447 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7448            OVERLAPPED *ovl)\r
7449 {\r
7450   int ok, err;\r
7451 \r
7452   /* [AS]  */\r
7453   if( count <= 0 ) {\r
7454     if (appData.debugMode) {\r
7455       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7456     }\r
7457 \r
7458     return ERROR_INVALID_USER_BUFFER;\r
7459   }\r
7460 \r
7461   ResetEvent(ovl->hEvent);\r
7462   ovl->Offset = ovl->OffsetHigh = 0;\r
7463   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7464   if (ok) {\r
7465     err = NO_ERROR;\r
7466   } else {\r
7467     err = GetLastError();\r
7468     if (err == ERROR_IO_PENDING) {\r
7469       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7470       if (ok)\r
7471         err = NO_ERROR;\r
7472       else\r
7473         err = GetLastError();\r
7474     }\r
7475   }\r
7476   return err;\r
7477 }\r
7478 \r
7479 int\r
7480 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7481             OVERLAPPED *ovl)\r
7482 {\r
7483   int ok, err;\r
7484 \r
7485   ResetEvent(ovl->hEvent);\r
7486   ovl->Offset = ovl->OffsetHigh = 0;\r
7487   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7488   if (ok) {\r
7489     err = NO_ERROR;\r
7490   } else {\r
7491     err = GetLastError();\r
7492     if (err == ERROR_IO_PENDING) {\r
7493       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7494       if (ok)\r
7495         err = NO_ERROR;\r
7496       else\r
7497         err = GetLastError();\r
7498     }\r
7499   }\r
7500   return err;\r
7501 }\r
7502 \r
7503 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7504 void CheckForInputBufferFull( InputSource * is )\r
7505 {\r
7506     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7507         /* Look for end of line */\r
7508         char * p = is->buf;\r
7509         \r
7510         while( p < is->next && *p != '\n' ) {\r
7511             p++;\r
7512         }\r
7513 \r
7514         if( p >= is->next ) {\r
7515             if (appData.debugMode) {\r
7516                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7517             }\r
7518 \r
7519             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7520             is->count = (DWORD) -1;\r
7521             is->next = is->buf;\r
7522         }\r
7523     }\r
7524 }\r
7525 \r
7526 DWORD\r
7527 InputThread(LPVOID arg)\r
7528 {\r
7529   InputSource *is;\r
7530   OVERLAPPED ovl;\r
7531 \r
7532   is = (InputSource *) arg;\r
7533   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7534   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7535   while (is->hThread != NULL) {\r
7536     is->error = DoReadFile(is->hFile, is->next,\r
7537                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7538                            &is->count, &ovl);\r
7539     if (is->error == NO_ERROR) {\r
7540       is->next += is->count;\r
7541     } else {\r
7542       if (is->error == ERROR_BROKEN_PIPE) {\r
7543         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7544         is->count = 0;\r
7545       } else {\r
7546         is->count = (DWORD) -1;\r
7547         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7548         break; \r
7549       }\r
7550     }\r
7551 \r
7552     CheckForInputBufferFull( is );\r
7553 \r
7554     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7555 \r
7556     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7557 \r
7558     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7559   }\r
7560 \r
7561   CloseHandle(ovl.hEvent);\r
7562   CloseHandle(is->hFile);\r
7563 \r
7564   if (appData.debugMode) {\r
7565     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7566   }\r
7567 \r
7568   return 0;\r
7569 }\r
7570 \r
7571 \r
7572 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7573 DWORD\r
7574 NonOvlInputThread(LPVOID arg)\r
7575 {\r
7576   InputSource *is;\r
7577   char *p, *q;\r
7578   int i;\r
7579   char prev;\r
7580 \r
7581   is = (InputSource *) arg;\r
7582   while (is->hThread != NULL) {\r
7583     is->error = ReadFile(is->hFile, is->next,\r
7584                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7585                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7586     if (is->error == NO_ERROR) {\r
7587       /* Change CRLF to LF */\r
7588       if (is->next > is->buf) {\r
7589         p = is->next - 1;\r
7590         i = is->count + 1;\r
7591       } else {\r
7592         p = is->next;\r
7593         i = is->count;\r
7594       }\r
7595       q = p;\r
7596       prev = NULLCHAR;\r
7597       while (i > 0) {\r
7598         if (prev == '\r' && *p == '\n') {\r
7599           *(q-1) = '\n';\r
7600           is->count--;\r
7601         } else { \r
7602           *q++ = *p;\r
7603         }\r
7604         prev = *p++;\r
7605         i--;\r
7606       }\r
7607       *q = NULLCHAR;\r
7608       is->next = q;\r
7609     } else {\r
7610       if (is->error == ERROR_BROKEN_PIPE) {\r
7611         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7612         is->count = 0; \r
7613       } else {\r
7614         is->count = (DWORD) -1;\r
7615       }\r
7616     }\r
7617 \r
7618     CheckForInputBufferFull( is );\r
7619 \r
7620     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7621 \r
7622     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7623 \r
7624     if (is->count < 0) break;  /* Quit on error */\r
7625   }\r
7626   CloseHandle(is->hFile);\r
7627   return 0;\r
7628 }\r
7629 \r
7630 DWORD\r
7631 SocketInputThread(LPVOID arg)\r
7632 {\r
7633   InputSource *is;\r
7634 \r
7635   is = (InputSource *) arg;\r
7636   while (is->hThread != NULL) {\r
7637     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7638     if ((int)is->count == SOCKET_ERROR) {\r
7639       is->count = (DWORD) -1;\r
7640       is->error = WSAGetLastError();\r
7641     } else {\r
7642       is->error = NO_ERROR;\r
7643       is->next += is->count;\r
7644       if (is->count == 0 && is->second == is) {\r
7645         /* End of file on stderr; quit with no message */\r
7646         break;\r
7647       }\r
7648     }\r
7649     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7650 \r
7651     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7652 \r
7653     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7654   }\r
7655   return 0;\r
7656 }\r
7657 \r
7658 VOID\r
7659 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7660 {\r
7661   InputSource *is;\r
7662 \r
7663   is = (InputSource *) lParam;\r
7664   if (is->lineByLine) {\r
7665     /* Feed in lines one by one */\r
7666     char *p = is->buf;\r
7667     char *q = p;\r
7668     while (q < is->next) {\r
7669       if (*q++ == '\n') {\r
7670         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7671         p = q;\r
7672       }\r
7673     }\r
7674     \r
7675     /* Move any partial line to the start of the buffer */\r
7676     q = is->buf;\r
7677     while (p < is->next) {\r
7678       *q++ = *p++;\r
7679     }\r
7680     is->next = q;\r
7681 \r
7682     if (is->error != NO_ERROR || is->count == 0) {\r
7683       /* Notify backend of the error.  Note: If there was a partial\r
7684          line at the end, it is not flushed through. */\r
7685       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7686     }\r
7687   } else {\r
7688     /* Feed in the whole chunk of input at once */\r
7689     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7690     is->next = is->buf;\r
7691   }\r
7692 }\r
7693 \r
7694 /*---------------------------------------------------------------------------*\\r
7695  *\r
7696  *  Menu enables. Used when setting various modes.\r
7697  *\r
7698 \*---------------------------------------------------------------------------*/\r
7699 \r
7700 typedef struct {\r
7701   int item;\r
7702   int flags;\r
7703 } Enables;\r
7704 \r
7705 VOID\r
7706 GreyRevert(Boolean grey)\r
7707 { // [HGM] vari: for retracting variations in local mode\r
7708   HMENU hmenu = GetMenu(hwndMain);\r
7709   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7710   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7711 }\r
7712 \r
7713 VOID\r
7714 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7715 {\r
7716   while (enab->item > 0) {\r
7717     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7718     enab++;\r
7719   }\r
7720 }\r
7721 \r
7722 Enables gnuEnables[] = {\r
7723   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7724   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7725   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7726   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7727   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7728   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7729   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7730   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7731   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7732   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7733   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7734   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7735   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7736   { -1, -1 }\r
7737 };\r
7738 \r
7739 Enables icsEnables[] = {\r
7740   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7741   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7742   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7743   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7744   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7745   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7746   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7747   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7748   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7749   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7750   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7751   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7752   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7753   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7754   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7755   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7756   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7757   { -1, -1 }\r
7758 };\r
7759 \r
7760 #if ZIPPY\r
7761 Enables zippyEnables[] = {\r
7762   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7763   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7764   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7765   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7766   { -1, -1 }\r
7767 };\r
7768 #endif\r
7769 \r
7770 Enables ncpEnables[] = {\r
7771   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7772   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7773   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7774   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7775   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7776   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7777   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7778   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7779   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7780   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7781   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7782   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7783   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7784   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7785   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7786   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7787   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7788   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7789   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7790   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7791   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7792   { -1, -1 }\r
7793 };\r
7794 \r
7795 Enables trainingOnEnables[] = {\r
7796   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7797   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7798   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7799   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7800   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7801   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7802   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7803   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7804   { -1, -1 }\r
7805 };\r
7806 \r
7807 Enables trainingOffEnables[] = {\r
7808   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7809   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7810   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7811   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7812   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7813   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7814   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7815   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7816   { -1, -1 }\r
7817 };\r
7818 \r
7819 /* These modify either ncpEnables or gnuEnables */\r
7820 Enables cmailEnables[] = {\r
7821   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7822   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7823   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7824   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7825   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7826   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7827   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7828   { -1, -1 }\r
7829 };\r
7830 \r
7831 Enables machineThinkingEnables[] = {\r
7832   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7833   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7834   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7835   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7836   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7837   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7838   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7839   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7840   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7841   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7842   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7843   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7844   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7845   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7846   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7847   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7848   { -1, -1 }\r
7849 };\r
7850 \r
7851 Enables userThinkingEnables[] = {\r
7852   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7853   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7854   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7855   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7856   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7857   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7858   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7859   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7860   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7861   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7862   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7863   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7864   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7865   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7866   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7867   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7868   { -1, -1 }\r
7869 };\r
7870 \r
7871 /*---------------------------------------------------------------------------*\\r
7872  *\r
7873  *  Front-end interface functions exported by XBoard.\r
7874  *  Functions appear in same order as prototypes in frontend.h.\r
7875  * \r
7876 \*---------------------------------------------------------------------------*/\r
7877 VOID\r
7878 ModeHighlight()\r
7879 {\r
7880   static UINT prevChecked = 0;\r
7881   static int prevPausing = 0;\r
7882   UINT nowChecked;\r
7883 \r
7884   if (pausing != prevPausing) {\r
7885     prevPausing = pausing;\r
7886     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7887                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7888     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7889   }\r
7890 \r
7891   switch (gameMode) {\r
7892   case BeginningOfGame:\r
7893     if (appData.icsActive)\r
7894       nowChecked = IDM_IcsClient;\r
7895     else if (appData.noChessProgram)\r
7896       nowChecked = IDM_EditGame;\r
7897     else\r
7898       nowChecked = IDM_MachineBlack;\r
7899     break;\r
7900   case MachinePlaysBlack:\r
7901     nowChecked = IDM_MachineBlack;\r
7902     break;\r
7903   case MachinePlaysWhite:\r
7904     nowChecked = IDM_MachineWhite;\r
7905     break;\r
7906   case TwoMachinesPlay:\r
7907     nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match\r
7908     break;\r
7909   case AnalyzeMode:\r
7910     nowChecked = IDM_AnalysisMode;\r
7911     break;\r
7912   case AnalyzeFile:\r
7913     nowChecked = IDM_AnalyzeFile;\r
7914     break;\r
7915   case EditGame:\r
7916     nowChecked = IDM_EditGame;\r
7917     break;\r
7918   case PlayFromGameFile:\r
7919     nowChecked = IDM_LoadGame;\r
7920     break;\r
7921   case EditPosition:\r
7922     nowChecked = IDM_EditPosition;\r
7923     break;\r
7924   case Training:\r
7925     nowChecked = IDM_Training;\r
7926     break;\r
7927   case IcsPlayingWhite:\r
7928   case IcsPlayingBlack:\r
7929   case IcsObserving:\r
7930   case IcsIdle:\r
7931     nowChecked = IDM_IcsClient;\r
7932     break;\r
7933   default:\r
7934   case EndOfGame:\r
7935     nowChecked = 0;\r
7936     break;\r
7937   }\r
7938   if (prevChecked != 0)\r
7939     (void) CheckMenuItem(GetMenu(hwndMain),\r
7940                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7941   if (nowChecked != 0)\r
7942     (void) CheckMenuItem(GetMenu(hwndMain),\r
7943                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
7944 \r
7945   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7946     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7947                           MF_BYCOMMAND|MF_ENABLED);\r
7948   } else {\r
7949     (void) EnableMenuItem(GetMenu(hwndMain), \r
7950                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7951   }\r
7952 \r
7953   prevChecked = nowChecked;\r
7954 \r
7955   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
7956   if (appData.icsActive) {\r
7957        if (appData.icsEngineAnalyze) {\r
7958                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7959                        MF_BYCOMMAND|MF_CHECKED);\r
7960        } else {\r
7961                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7962                        MF_BYCOMMAND|MF_UNCHECKED);\r
7963        }\r
7964   }\r
7965 }\r
7966 \r
7967 VOID\r
7968 SetICSMode()\r
7969 {\r
7970   HMENU hmenu = GetMenu(hwndMain);\r
7971   SetMenuEnables(hmenu, icsEnables);\r
7972   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
7973     MF_BYPOSITION|MF_ENABLED);\r
7974 #if ZIPPY\r
7975   if (appData.zippyPlay) {\r
7976     SetMenuEnables(hmenu, zippyEnables);\r
7977     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
7978          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7979           MF_BYCOMMAND|MF_ENABLED);\r
7980   }\r
7981 #endif\r
7982 }\r
7983 \r
7984 VOID\r
7985 SetGNUMode()\r
7986 {\r
7987   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
7988 }\r
7989 \r
7990 VOID\r
7991 SetNCPMode()\r
7992 {\r
7993   HMENU hmenu = GetMenu(hwndMain);\r
7994   SetMenuEnables(hmenu, ncpEnables);\r
7995   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
7996     MF_BYPOSITION|MF_GRAYED);\r
7997     DrawMenuBar(hwndMain);\r
7998 }\r
7999 \r
8000 VOID\r
8001 SetCmailMode()\r
8002 {\r
8003   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8004 }\r
8005 \r
8006 VOID \r
8007 SetTrainingModeOn()\r
8008 {\r
8009   int i;\r
8010   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8011   for (i = 0; i < N_BUTTONS; i++) {\r
8012     if (buttonDesc[i].hwnd != NULL)\r
8013       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8014   }\r
8015   CommentPopDown();\r
8016 }\r
8017 \r
8018 VOID SetTrainingModeOff()\r
8019 {\r
8020   int i;\r
8021   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8022   for (i = 0; i < N_BUTTONS; i++) {\r
8023     if (buttonDesc[i].hwnd != NULL)\r
8024       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8025   }\r
8026 }\r
8027 \r
8028 \r
8029 VOID\r
8030 SetUserThinkingEnables()\r
8031 {\r
8032   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8033 }\r
8034 \r
8035 VOID\r
8036 SetMachineThinkingEnables()\r
8037 {\r
8038   HMENU hMenu = GetMenu(hwndMain);\r
8039   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8040 \r
8041   SetMenuEnables(hMenu, machineThinkingEnables);\r
8042 \r
8043   if (gameMode == MachinePlaysBlack) {\r
8044     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8045   } else if (gameMode == MachinePlaysWhite) {\r
8046     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8047   } else if (gameMode == TwoMachinesPlay) {\r
8048     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8049   }\r
8050 }\r
8051 \r
8052 \r
8053 VOID\r
8054 DisplayTitle(char *str)\r
8055 {\r
8056   char title[MSG_SIZ], *host;\r
8057   if (str[0] != NULLCHAR) {\r
8058     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8059   } else if (appData.icsActive) {\r
8060     if (appData.icsCommPort[0] != NULLCHAR)\r
8061       host = "ICS";\r
8062     else \r
8063       host = appData.icsHost;\r
8064       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8065   } else if (appData.noChessProgram) {\r
8066     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8067   } else {\r
8068     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8069     strcat(title, ": ");\r
8070     strcat(title, first.tidy);\r
8071   }\r
8072   SetWindowText(hwndMain, title);\r
8073 }\r
8074 \r
8075 \r
8076 VOID\r
8077 DisplayMessage(char *str1, char *str2)\r
8078 {\r
8079   HDC hdc;\r
8080   HFONT oldFont;\r
8081   int remain = MESSAGE_TEXT_MAX - 1;\r
8082   int len;\r
8083 \r
8084   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8085   messageText[0] = NULLCHAR;\r
8086   if (*str1) {\r
8087     len = strlen(str1);\r
8088     if (len > remain) len = remain;\r
8089     strncpy(messageText, str1, len);\r
8090     messageText[len] = NULLCHAR;\r
8091     remain -= len;\r
8092   }\r
8093   if (*str2 && remain >= 2) {\r
8094     if (*str1) {\r
8095       strcat(messageText, "  ");\r
8096       remain -= 2;\r
8097     }\r
8098     len = strlen(str2);\r
8099     if (len > remain) len = remain;\r
8100     strncat(messageText, str2, len);\r
8101   }\r
8102   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8103 \r
8104   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8105 \r
8106   SAYMACHINEMOVE();\r
8107 \r
8108   hdc = GetDC(hwndMain);\r
8109   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8110   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8111              &messageRect, messageText, strlen(messageText), NULL);\r
8112   (void) SelectObject(hdc, oldFont);\r
8113   (void) ReleaseDC(hwndMain, hdc);\r
8114 }\r
8115 \r
8116 VOID\r
8117 DisplayError(char *str, int error)\r
8118 {\r
8119   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8120   int len;\r
8121 \r
8122   if (error == 0) {\r
8123     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8124   } else {\r
8125     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8126                         NULL, error, LANG_NEUTRAL,\r
8127                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8128     if (len > 0) {\r
8129       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8130     } else {\r
8131       ErrorMap *em = errmap;\r
8132       while (em->err != 0 && em->err != error) em++;\r
8133       if (em->err != 0) {\r
8134         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8135       } else {\r
8136         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8137       }\r
8138     }\r
8139   }\r
8140   \r
8141   ErrorPopUp(_("Error"), buf);\r
8142 }\r
8143 \r
8144 \r
8145 VOID\r
8146 DisplayMoveError(char *str)\r
8147 {\r
8148   fromX = fromY = -1;\r
8149   ClearHighlights();\r
8150   DrawPosition(FALSE, NULL);\r
8151   if (appData.popupMoveErrors) {\r
8152     ErrorPopUp(_("Error"), str);\r
8153   } else {\r
8154     DisplayMessage(str, "");\r
8155     moveErrorMessageUp = TRUE;\r
8156   }\r
8157 }\r
8158 \r
8159 VOID\r
8160 DisplayFatalError(char *str, int error, int exitStatus)\r
8161 {\r
8162   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8163   int len;\r
8164   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8165 \r
8166   if (error != 0) {\r
8167     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8168                         NULL, error, LANG_NEUTRAL,\r
8169                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8170     if (len > 0) {\r
8171       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8172     } else {\r
8173       ErrorMap *em = errmap;\r
8174       while (em->err != 0 && em->err != error) em++;\r
8175       if (em->err != 0) {\r
8176         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8177       } else {\r
8178         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8179       }\r
8180     }\r
8181     str = buf;\r
8182   }\r
8183   if (appData.debugMode) {\r
8184     fprintf(debugFP, "%s: %s\n", label, str);\r
8185   }\r
8186   if (appData.popupExitMessage) {\r
8187     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8188                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8189   }\r
8190   ExitEvent(exitStatus);\r
8191 }\r
8192 \r
8193 \r
8194 VOID\r
8195 DisplayInformation(char *str)\r
8196 {\r
8197   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8198 }\r
8199 \r
8200 \r
8201 VOID\r
8202 DisplayNote(char *str)\r
8203 {\r
8204   ErrorPopUp(_("Note"), str);\r
8205 }\r
8206 \r
8207 \r
8208 typedef struct {\r
8209   char *title, *question, *replyPrefix;\r
8210   ProcRef pr;\r
8211 } QuestionParams;\r
8212 \r
8213 LRESULT CALLBACK\r
8214 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8215 {\r
8216   static QuestionParams *qp;\r
8217   char reply[MSG_SIZ];\r
8218   int len, err;\r
8219 \r
8220   switch (message) {\r
8221   case WM_INITDIALOG:\r
8222     qp = (QuestionParams *) lParam;\r
8223     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8224     Translate(hDlg, DLG_Question);\r
8225     SetWindowText(hDlg, qp->title);\r
8226     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8227     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8228     return FALSE;\r
8229 \r
8230   case WM_COMMAND:\r
8231     switch (LOWORD(wParam)) {\r
8232     case IDOK:\r
8233       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8234       if (*reply) strcat(reply, " ");\r
8235       len = strlen(reply);\r
8236       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8237       strcat(reply, "\n");\r
8238       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8239       EndDialog(hDlg, TRUE);\r
8240       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8241       return TRUE;\r
8242     case IDCANCEL:\r
8243       EndDialog(hDlg, FALSE);\r
8244       return TRUE;\r
8245     default:\r
8246       break;\r
8247     }\r
8248     break;\r
8249   }\r
8250   return FALSE;\r
8251 }\r
8252 \r
8253 VOID\r
8254 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8255 {\r
8256     QuestionParams qp;\r
8257     FARPROC lpProc;\r
8258     \r
8259     qp.title = title;\r
8260     qp.question = question;\r
8261     qp.replyPrefix = replyPrefix;\r
8262     qp.pr = pr;\r
8263     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8264     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8265       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8266     FreeProcInstance(lpProc);\r
8267 }\r
8268 \r
8269 /* [AS] Pick FRC position */\r
8270 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8271 {\r
8272     static int * lpIndexFRC;\r
8273     BOOL index_is_ok;\r
8274     char buf[16];\r
8275 \r
8276     switch( message )\r
8277     {\r
8278     case WM_INITDIALOG:\r
8279         lpIndexFRC = (int *) lParam;\r
8280 \r
8281         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8282         Translate(hDlg, DLG_NewGameFRC);\r
8283 \r
8284         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8285         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8286         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8287         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8288 \r
8289         break;\r
8290 \r
8291     case WM_COMMAND:\r
8292         switch( LOWORD(wParam) ) {\r
8293         case IDOK:\r
8294             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8295             EndDialog( hDlg, 0 );\r
8296             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8297             return TRUE;\r
8298         case IDCANCEL:\r
8299             EndDialog( hDlg, 1 );   \r
8300             return TRUE;\r
8301         case IDC_NFG_Edit:\r
8302             if( HIWORD(wParam) == EN_CHANGE ) {\r
8303                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8304 \r
8305                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8306             }\r
8307             return TRUE;\r
8308         case IDC_NFG_Random:\r
8309           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8310             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8311             return TRUE;\r
8312         }\r
8313 \r
8314         break;\r
8315     }\r
8316 \r
8317     return FALSE;\r
8318 }\r
8319 \r
8320 int NewGameFRC()\r
8321 {\r
8322     int result;\r
8323     int index = appData.defaultFrcPosition;\r
8324     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8325 \r
8326     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8327 \r
8328     if( result == 0 ) {\r
8329         appData.defaultFrcPosition = index;\r
8330     }\r
8331 \r
8332     return result;\r
8333 }\r
8334 \r
8335 /* [AS] Game list options. Refactored by HGM */\r
8336 \r
8337 HWND gameListOptionsDialog;\r
8338 \r
8339 // low-level front-end: clear text edit / list widget\r
8340 void\r
8341 GLT_ClearList()\r
8342 {\r
8343     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8344 }\r
8345 \r
8346 // low-level front-end: clear text edit / list widget\r
8347 void\r
8348 GLT_DeSelectList()\r
8349 {\r
8350     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8351 }\r
8352 \r
8353 // low-level front-end: append line to text edit / list widget\r
8354 void\r
8355 GLT_AddToList( char *name )\r
8356 {\r
8357     if( name != 0 ) {\r
8358             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8359     }\r
8360 }\r
8361 \r
8362 // low-level front-end: get line from text edit / list widget\r
8363 Boolean\r
8364 GLT_GetFromList( int index, char *name )\r
8365 {\r
8366     if( name != 0 ) {\r
8367             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8368                 return TRUE;\r
8369     }\r
8370     return FALSE;\r
8371 }\r
8372 \r
8373 void GLT_MoveSelection( HWND hDlg, int delta )\r
8374 {\r
8375     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8376     int idx2 = idx1 + delta;\r
8377     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8378 \r
8379     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8380         char buf[128];\r
8381 \r
8382         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8383         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8384         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8385         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8386     }\r
8387 }\r
8388 \r
8389 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8390 {\r
8391     switch( message )\r
8392     {\r
8393     case WM_INITDIALOG:\r
8394         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8395         \r
8396         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8397         Translate(hDlg, DLG_GameListOptions);\r
8398 \r
8399         /* Initialize list */\r
8400         GLT_TagsToList( lpUserGLT );\r
8401 \r
8402         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8403 \r
8404         break;\r
8405 \r
8406     case WM_COMMAND:\r
8407         switch( LOWORD(wParam) ) {\r
8408         case IDOK:\r
8409             GLT_ParseList();\r
8410             EndDialog( hDlg, 0 );\r
8411             return TRUE;\r
8412         case IDCANCEL:\r
8413             EndDialog( hDlg, 1 );\r
8414             return TRUE;\r
8415 \r
8416         case IDC_GLT_Default:\r
8417             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8418             return TRUE;\r
8419 \r
8420         case IDC_GLT_Restore:\r
8421             GLT_TagsToList( appData.gameListTags );\r
8422             return TRUE;\r
8423 \r
8424         case IDC_GLT_Up:\r
8425             GLT_MoveSelection( hDlg, -1 );\r
8426             return TRUE;\r
8427 \r
8428         case IDC_GLT_Down:\r
8429             GLT_MoveSelection( hDlg, +1 );\r
8430             return TRUE;\r
8431         }\r
8432 \r
8433         break;\r
8434     }\r
8435 \r
8436     return FALSE;\r
8437 }\r
8438 \r
8439 int GameListOptions()\r
8440 {\r
8441     int result;\r
8442     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8443 \r
8444       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8445 \r
8446     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8447 \r
8448     if( result == 0 ) {\r
8449         /* [AS] Memory leak here! */\r
8450         appData.gameListTags = strdup( lpUserGLT ); \r
8451     }\r
8452 \r
8453     return result;\r
8454 }\r
8455 \r
8456 VOID\r
8457 DisplayIcsInteractionTitle(char *str)\r
8458 {\r
8459   char consoleTitle[MSG_SIZ];\r
8460 \r
8461     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8462   SetWindowText(hwndConsole, consoleTitle);\r
8463 }\r
8464 \r
8465 void\r
8466 DrawPosition(int fullRedraw, Board board)\r
8467 {\r
8468   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8469 }\r
8470 \r
8471 void NotifyFrontendLogin()\r
8472 {\r
8473         if (hwndConsole)\r
8474                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8475 }\r
8476 \r
8477 VOID\r
8478 ResetFrontEnd()\r
8479 {\r
8480   fromX = fromY = -1;\r
8481   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8482     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8483     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8484     dragInfo.lastpos = dragInfo.pos;\r
8485     dragInfo.start.x = dragInfo.start.y = -1;\r
8486     dragInfo.from = dragInfo.start;\r
8487     ReleaseCapture();\r
8488     DrawPosition(TRUE, NULL);\r
8489   }\r
8490   TagsPopDown();\r
8491 }\r
8492 \r
8493 \r
8494 VOID\r
8495 CommentPopUp(char *title, char *str)\r
8496 {\r
8497   HWND hwnd = GetActiveWindow();\r
8498   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8499   SAY(str);\r
8500   SetActiveWindow(hwnd);\r
8501 }\r
8502 \r
8503 VOID\r
8504 CommentPopDown(void)\r
8505 {\r
8506   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
8507   if (commentDialog) {\r
8508     ShowWindow(commentDialog, SW_HIDE);\r
8509   }\r
8510   commentUp = FALSE;\r
8511 }\r
8512 \r
8513 VOID\r
8514 EditCommentPopUp(int index, char *title, char *str)\r
8515 {\r
8516   EitherCommentPopUp(index, title, str, TRUE);\r
8517 }\r
8518 \r
8519 \r
8520 VOID\r
8521 RingBell()\r
8522 {\r
8523   MyPlaySound(&sounds[(int)SoundMove]);\r
8524 }\r
8525 \r
8526 VOID PlayIcsWinSound()\r
8527 {\r
8528   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8529 }\r
8530 \r
8531 VOID PlayIcsLossSound()\r
8532 {\r
8533   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8534 }\r
8535 \r
8536 VOID PlayIcsDrawSound()\r
8537 {\r
8538   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8539 }\r
8540 \r
8541 VOID PlayIcsUnfinishedSound()\r
8542 {\r
8543   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8544 }\r
8545 \r
8546 VOID\r
8547 PlayAlarmSound()\r
8548 {\r
8549   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8550 }\r
8551 \r
8552 \r
8553 VOID\r
8554 EchoOn()\r
8555 {\r
8556   HWND hInput;\r
8557   consoleEcho = TRUE;\r
8558   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8559   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8560   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8561 }\r
8562 \r
8563 \r
8564 VOID\r
8565 EchoOff()\r
8566 {\r
8567   CHARFORMAT cf;\r
8568   HWND hInput;\r
8569   consoleEcho = FALSE;\r
8570   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8571   /* This works OK: set text and background both to the same color */\r
8572   cf = consoleCF;\r
8573   cf.crTextColor = COLOR_ECHOOFF;\r
8574   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8575   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8576 }\r
8577 \r
8578 /* No Raw()...? */\r
8579 \r
8580 void Colorize(ColorClass cc, int continuation)\r
8581 {\r
8582   currentColorClass = cc;\r
8583   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8584   consoleCF.crTextColor = textAttribs[cc].color;\r
8585   consoleCF.dwEffects = textAttribs[cc].effects;\r
8586   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8587 }\r
8588 \r
8589 char *\r
8590 UserName()\r
8591 {\r
8592   static char buf[MSG_SIZ];\r
8593   DWORD bufsiz = MSG_SIZ;\r
8594 \r
8595   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8596         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8597   }\r
8598   if (!GetUserName(buf, &bufsiz)) {\r
8599     /*DisplayError("Error getting user name", GetLastError());*/\r
8600     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8601   }\r
8602   return buf;\r
8603 }\r
8604 \r
8605 char *\r
8606 HostName()\r
8607 {\r
8608   static char buf[MSG_SIZ];\r
8609   DWORD bufsiz = MSG_SIZ;\r
8610 \r
8611   if (!GetComputerName(buf, &bufsiz)) {\r
8612     /*DisplayError("Error getting host name", GetLastError());*/\r
8613     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8614   }\r
8615   return buf;\r
8616 }\r
8617 \r
8618 \r
8619 int\r
8620 ClockTimerRunning()\r
8621 {\r
8622   return clockTimerEvent != 0;\r
8623 }\r
8624 \r
8625 int\r
8626 StopClockTimer()\r
8627 {\r
8628   if (clockTimerEvent == 0) return FALSE;\r
8629   KillTimer(hwndMain, clockTimerEvent);\r
8630   clockTimerEvent = 0;\r
8631   return TRUE;\r
8632 }\r
8633 \r
8634 void\r
8635 StartClockTimer(long millisec)\r
8636 {\r
8637   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8638                              (UINT) millisec, NULL);\r
8639 }\r
8640 \r
8641 void\r
8642 DisplayWhiteClock(long timeRemaining, int highlight)\r
8643 {\r
8644   HDC hdc;\r
8645   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8646 \r
8647   if(appData.noGUI) return;\r
8648   hdc = GetDC(hwndMain);\r
8649   if (!IsIconic(hwndMain)) {\r
8650     DisplayAClock(hdc, timeRemaining, highlight, \r
8651                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8652   }\r
8653   if (highlight && iconCurrent == iconBlack) {\r
8654     iconCurrent = iconWhite;\r
8655     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8656     if (IsIconic(hwndMain)) {\r
8657       DrawIcon(hdc, 2, 2, iconCurrent);\r
8658     }\r
8659   }\r
8660   (void) ReleaseDC(hwndMain, hdc);\r
8661   if (hwndConsole)\r
8662     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8663 }\r
8664 \r
8665 void\r
8666 DisplayBlackClock(long timeRemaining, int highlight)\r
8667 {\r
8668   HDC hdc;\r
8669   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8670 \r
8671   if(appData.noGUI) return;\r
8672   hdc = GetDC(hwndMain);\r
8673   if (!IsIconic(hwndMain)) {\r
8674     DisplayAClock(hdc, timeRemaining, highlight, \r
8675                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8676   }\r
8677   if (highlight && iconCurrent == iconWhite) {\r
8678     iconCurrent = iconBlack;\r
8679     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8680     if (IsIconic(hwndMain)) {\r
8681       DrawIcon(hdc, 2, 2, iconCurrent);\r
8682     }\r
8683   }\r
8684   (void) ReleaseDC(hwndMain, hdc);\r
8685   if (hwndConsole)\r
8686     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8687 }\r
8688 \r
8689 \r
8690 int\r
8691 LoadGameTimerRunning()\r
8692 {\r
8693   return loadGameTimerEvent != 0;\r
8694 }\r
8695 \r
8696 int\r
8697 StopLoadGameTimer()\r
8698 {\r
8699   if (loadGameTimerEvent == 0) return FALSE;\r
8700   KillTimer(hwndMain, loadGameTimerEvent);\r
8701   loadGameTimerEvent = 0;\r
8702   return TRUE;\r
8703 }\r
8704 \r
8705 void\r
8706 StartLoadGameTimer(long millisec)\r
8707 {\r
8708   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8709                                 (UINT) millisec, NULL);\r
8710 }\r
8711 \r
8712 void\r
8713 AutoSaveGame()\r
8714 {\r
8715   char *defName;\r
8716   FILE *f;\r
8717   char fileTitle[MSG_SIZ];\r
8718 \r
8719   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8720   f = OpenFileDialog(hwndMain, "a", defName,\r
8721                      appData.oldSaveStyle ? "gam" : "pgn",\r
8722                      GAME_FILT, \r
8723                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8724   if (f != NULL) {\r
8725     SaveGame(f, 0, "");\r
8726     fclose(f);\r
8727   }\r
8728 }\r
8729 \r
8730 \r
8731 void\r
8732 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8733 {\r
8734   if (delayedTimerEvent != 0) {\r
8735     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8736       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8737     }\r
8738     KillTimer(hwndMain, delayedTimerEvent);\r
8739     delayedTimerEvent = 0;\r
8740     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8741     delayedTimerCallback();\r
8742   }\r
8743   delayedTimerCallback = cb;\r
8744   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8745                                 (UINT) millisec, NULL);\r
8746 }\r
8747 \r
8748 DelayedEventCallback\r
8749 GetDelayedEvent()\r
8750 {\r
8751   if (delayedTimerEvent) {\r
8752     return delayedTimerCallback;\r
8753   } else {\r
8754     return NULL;\r
8755   }\r
8756 }\r
8757 \r
8758 void\r
8759 CancelDelayedEvent()\r
8760 {\r
8761   if (delayedTimerEvent) {\r
8762     KillTimer(hwndMain, delayedTimerEvent);\r
8763     delayedTimerEvent = 0;\r
8764   }\r
8765 }\r
8766 \r
8767 DWORD GetWin32Priority(int nice)\r
8768 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8769 /*\r
8770 REALTIME_PRIORITY_CLASS     0x00000100\r
8771 HIGH_PRIORITY_CLASS         0x00000080\r
8772 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8773 NORMAL_PRIORITY_CLASS       0x00000020\r
8774 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8775 IDLE_PRIORITY_CLASS         0x00000040\r
8776 */\r
8777         if (nice < -15) return 0x00000080;\r
8778         if (nice < 0)   return 0x00008000;\r
8779         if (nice == 0)  return 0x00000020;\r
8780         if (nice < 15)  return 0x00004000;\r
8781         return 0x00000040;\r
8782 }\r
8783 \r
8784 /* Start a child process running the given program.\r
8785    The process's standard output can be read from "from", and its\r
8786    standard input can be written to "to".\r
8787    Exit with fatal error if anything goes wrong.\r
8788    Returns an opaque pointer that can be used to destroy the process\r
8789    later.\r
8790 */\r
8791 int\r
8792 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8793 {\r
8794 #define BUFSIZE 4096\r
8795 \r
8796   HANDLE hChildStdinRd, hChildStdinWr,\r
8797     hChildStdoutRd, hChildStdoutWr;\r
8798   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8799   SECURITY_ATTRIBUTES saAttr;\r
8800   BOOL fSuccess;\r
8801   PROCESS_INFORMATION piProcInfo;\r
8802   STARTUPINFO siStartInfo;\r
8803   ChildProc *cp;\r
8804   char buf[MSG_SIZ];\r
8805   DWORD err;\r
8806 \r
8807   if (appData.debugMode) {\r
8808     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8809   }\r
8810 \r
8811   *pr = NoProc;\r
8812 \r
8813   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8814   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8815   saAttr.bInheritHandle = TRUE;\r
8816   saAttr.lpSecurityDescriptor = NULL;\r
8817 \r
8818   /*\r
8819    * The steps for redirecting child's STDOUT:\r
8820    *     1. Create anonymous pipe to be STDOUT for child.\r
8821    *     2. Create a noninheritable duplicate of read handle,\r
8822    *         and close the inheritable read handle.\r
8823    */\r
8824 \r
8825   /* Create a pipe for the child's STDOUT. */\r
8826   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8827     return GetLastError();\r
8828   }\r
8829 \r
8830   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8831   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8832                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8833                              FALSE,     /* not inherited */\r
8834                              DUPLICATE_SAME_ACCESS);\r
8835   if (! fSuccess) {\r
8836     return GetLastError();\r
8837   }\r
8838   CloseHandle(hChildStdoutRd);\r
8839 \r
8840   /*\r
8841    * The steps for redirecting child's STDIN:\r
8842    *     1. Create anonymous pipe to be STDIN for child.\r
8843    *     2. Create a noninheritable duplicate of write handle,\r
8844    *         and close the inheritable write handle.\r
8845    */\r
8846 \r
8847   /* Create a pipe for the child's STDIN. */\r
8848   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8849     return GetLastError();\r
8850   }\r
8851 \r
8852   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8853   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8854                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8855                              FALSE,     /* not inherited */\r
8856                              DUPLICATE_SAME_ACCESS);\r
8857   if (! fSuccess) {\r
8858     return GetLastError();\r
8859   }\r
8860   CloseHandle(hChildStdinWr);\r
8861 \r
8862   /* Arrange to (1) look in dir for the child .exe file, and\r
8863    * (2) have dir be the child's working directory.  Interpret\r
8864    * dir relative to the directory WinBoard loaded from. */\r
8865   GetCurrentDirectory(MSG_SIZ, buf);\r
8866   SetCurrentDirectory(installDir);\r
8867   SetCurrentDirectory(dir);\r
8868 \r
8869   /* Now create the child process. */\r
8870 \r
8871   siStartInfo.cb = sizeof(STARTUPINFO);\r
8872   siStartInfo.lpReserved = NULL;\r
8873   siStartInfo.lpDesktop = NULL;\r
8874   siStartInfo.lpTitle = NULL;\r
8875   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8876   siStartInfo.cbReserved2 = 0;\r
8877   siStartInfo.lpReserved2 = NULL;\r
8878   siStartInfo.hStdInput = hChildStdinRd;\r
8879   siStartInfo.hStdOutput = hChildStdoutWr;\r
8880   siStartInfo.hStdError = hChildStdoutWr;\r
8881 \r
8882   fSuccess = CreateProcess(NULL,\r
8883                            cmdLine,        /* command line */\r
8884                            NULL,           /* process security attributes */\r
8885                            NULL,           /* primary thread security attrs */\r
8886                            TRUE,           /* handles are inherited */\r
8887                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8888                            NULL,           /* use parent's environment */\r
8889                            NULL,\r
8890                            &siStartInfo, /* STARTUPINFO pointer */\r
8891                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8892 \r
8893   err = GetLastError();\r
8894   SetCurrentDirectory(buf); /* return to prev directory */\r
8895   if (! fSuccess) {\r
8896     return err;\r
8897   }\r
8898 \r
8899   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8900     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8901     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8902   }\r
8903 \r
8904   /* Close the handles we don't need in the parent */\r
8905   CloseHandle(piProcInfo.hThread);\r
8906   CloseHandle(hChildStdinRd);\r
8907   CloseHandle(hChildStdoutWr);\r
8908 \r
8909   /* Prepare return value */\r
8910   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8911   cp->kind = CPReal;\r
8912   cp->hProcess = piProcInfo.hProcess;\r
8913   cp->pid = piProcInfo.dwProcessId;\r
8914   cp->hFrom = hChildStdoutRdDup;\r
8915   cp->hTo = hChildStdinWrDup;\r
8916 \r
8917   *pr = (void *) cp;\r
8918 \r
8919   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8920      2000 where engines sometimes don't see the initial command(s)\r
8921      from WinBoard and hang.  I don't understand how that can happen,\r
8922      but the Sleep is harmless, so I've put it in.  Others have also\r
8923      reported what may be the same problem, so hopefully this will fix\r
8924      it for them too.  */\r
8925   Sleep(500);\r
8926 \r
8927   return NO_ERROR;\r
8928 }\r
8929 \r
8930 \r
8931 void\r
8932 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8933 {\r
8934   ChildProc *cp; int result;\r
8935 \r
8936   cp = (ChildProc *) pr;\r
8937   if (cp == NULL) return;\r
8938 \r
8939   switch (cp->kind) {\r
8940   case CPReal:\r
8941     /* TerminateProcess is considered harmful, so... */\r
8942     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8943     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8944     /* The following doesn't work because the chess program\r
8945        doesn't "have the same console" as WinBoard.  Maybe\r
8946        we could arrange for this even though neither WinBoard\r
8947        nor the chess program uses a console for stdio? */\r
8948     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8949 \r
8950     /* [AS] Special termination modes for misbehaving programs... */\r
8951     if( signal == 9 ) { \r
8952         result = TerminateProcess( cp->hProcess, 0 );\r
8953 \r
8954         if ( appData.debugMode) {\r
8955             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
8956         }\r
8957     }\r
8958     else if( signal == 10 ) {\r
8959         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
8960 \r
8961         if( dw != WAIT_OBJECT_0 ) {\r
8962             result = TerminateProcess( cp->hProcess, 0 );\r
8963 \r
8964             if ( appData.debugMode) {\r
8965                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
8966             }\r
8967 \r
8968         }\r
8969     }\r
8970 \r
8971     CloseHandle(cp->hProcess);\r
8972     break;\r
8973 \r
8974   case CPComm:\r
8975     if (cp->hFrom) CloseHandle(cp->hFrom);\r
8976     break;\r
8977 \r
8978   case CPSock:\r
8979     closesocket(cp->sock);\r
8980     WSACleanup();\r
8981     break;\r
8982 \r
8983   case CPRcmd:\r
8984     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
8985     closesocket(cp->sock);\r
8986     closesocket(cp->sock2);\r
8987     WSACleanup();\r
8988     break;\r
8989   }\r
8990   free(cp);\r
8991 }\r
8992 \r
8993 void\r
8994 InterruptChildProcess(ProcRef pr)\r
8995 {\r
8996   ChildProc *cp;\r
8997 \r
8998   cp = (ChildProc *) pr;\r
8999   if (cp == NULL) return;\r
9000   switch (cp->kind) {\r
9001   case CPReal:\r
9002     /* The following doesn't work because the chess program\r
9003        doesn't "have the same console" as WinBoard.  Maybe\r
9004        we could arrange for this even though neither WinBoard\r
9005        nor the chess program uses a console for stdio */\r
9006     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9007     break;\r
9008 \r
9009   case CPComm:\r
9010   case CPSock:\r
9011     /* Can't interrupt */\r
9012     break;\r
9013 \r
9014   case CPRcmd:\r
9015     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9016     break;\r
9017   }\r
9018 }\r
9019 \r
9020 \r
9021 int\r
9022 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9023 {\r
9024   char cmdLine[MSG_SIZ];\r
9025 \r
9026   if (port[0] == NULLCHAR) {\r
9027     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9028   } else {\r
9029     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9030   }\r
9031   return StartChildProcess(cmdLine, "", pr);\r
9032 }\r
9033 \r
9034 \r
9035 /* Code to open TCP sockets */\r
9036 \r
9037 int\r
9038 OpenTCP(char *host, char *port, ProcRef *pr)\r
9039 {\r
9040   ChildProc *cp;\r
9041   int err;\r
9042   SOCKET s;\r
9043   struct sockaddr_in sa, mysa;\r
9044   struct hostent FAR *hp;\r
9045   unsigned short uport;\r
9046   WORD wVersionRequested;\r
9047   WSADATA wsaData;\r
9048 \r
9049   /* Initialize socket DLL */\r
9050   wVersionRequested = MAKEWORD(1, 1);\r
9051   err = WSAStartup(wVersionRequested, &wsaData);\r
9052   if (err != 0) return err;\r
9053 \r
9054   /* Make socket */\r
9055   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9056     err = WSAGetLastError();\r
9057     WSACleanup();\r
9058     return err;\r
9059   }\r
9060 \r
9061   /* Bind local address using (mostly) don't-care values.\r
9062    */\r
9063   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9064   mysa.sin_family = AF_INET;\r
9065   mysa.sin_addr.s_addr = INADDR_ANY;\r
9066   uport = (unsigned short) 0;\r
9067   mysa.sin_port = htons(uport);\r
9068   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9069       == SOCKET_ERROR) {\r
9070     err = WSAGetLastError();\r
9071     WSACleanup();\r
9072     return err;\r
9073   }\r
9074 \r
9075   /* Resolve remote host name */\r
9076   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9077   if (!(hp = gethostbyname(host))) {\r
9078     unsigned int b0, b1, b2, b3;\r
9079 \r
9080     err = WSAGetLastError();\r
9081 \r
9082     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9083       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9084       hp->h_addrtype = AF_INET;\r
9085       hp->h_length = 4;\r
9086       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9087       hp->h_addr_list[0] = (char *) malloc(4);\r
9088       hp->h_addr_list[0][0] = (char) b0;\r
9089       hp->h_addr_list[0][1] = (char) b1;\r
9090       hp->h_addr_list[0][2] = (char) b2;\r
9091       hp->h_addr_list[0][3] = (char) b3;\r
9092     } else {\r
9093       WSACleanup();\r
9094       return err;\r
9095     }\r
9096   }\r
9097   sa.sin_family = hp->h_addrtype;\r
9098   uport = (unsigned short) atoi(port);\r
9099   sa.sin_port = htons(uport);\r
9100   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9101 \r
9102   /* Make connection */\r
9103   if (connect(s, (struct sockaddr *) &sa,\r
9104               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9105     err = WSAGetLastError();\r
9106     WSACleanup();\r
9107     return err;\r
9108   }\r
9109 \r
9110   /* Prepare return value */\r
9111   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9112   cp->kind = CPSock;\r
9113   cp->sock = s;\r
9114   *pr = (ProcRef *) cp;\r
9115 \r
9116   return NO_ERROR;\r
9117 }\r
9118 \r
9119 int\r
9120 OpenCommPort(char *name, ProcRef *pr)\r
9121 {\r
9122   HANDLE h;\r
9123   COMMTIMEOUTS ct;\r
9124   ChildProc *cp;\r
9125   char fullname[MSG_SIZ];\r
9126 \r
9127   if (*name != '\\')\r
9128     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9129   else\r
9130     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9131 \r
9132   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9133                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9134   if (h == (HANDLE) -1) {\r
9135     return GetLastError();\r
9136   }\r
9137   hCommPort = h;\r
9138 \r
9139   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9140 \r
9141   /* Accumulate characters until a 100ms pause, then parse */\r
9142   ct.ReadIntervalTimeout = 100;\r
9143   ct.ReadTotalTimeoutMultiplier = 0;\r
9144   ct.ReadTotalTimeoutConstant = 0;\r
9145   ct.WriteTotalTimeoutMultiplier = 0;\r
9146   ct.WriteTotalTimeoutConstant = 0;\r
9147   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9148 \r
9149   /* Prepare return value */\r
9150   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9151   cp->kind = CPComm;\r
9152   cp->hFrom = h;\r
9153   cp->hTo = h;\r
9154   *pr = (ProcRef *) cp;\r
9155 \r
9156   return NO_ERROR;\r
9157 }\r
9158 \r
9159 int\r
9160 OpenLoopback(ProcRef *pr)\r
9161 {\r
9162   DisplayFatalError(_("Not implemented"), 0, 1);\r
9163   return NO_ERROR;\r
9164 }\r
9165 \r
9166 \r
9167 int\r
9168 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9169 {\r
9170   ChildProc *cp;\r
9171   int err;\r
9172   SOCKET s, s2, s3;\r
9173   struct sockaddr_in sa, mysa;\r
9174   struct hostent FAR *hp;\r
9175   unsigned short uport;\r
9176   WORD wVersionRequested;\r
9177   WSADATA wsaData;\r
9178   int fromPort;\r
9179   char stderrPortStr[MSG_SIZ];\r
9180 \r
9181   /* Initialize socket DLL */\r
9182   wVersionRequested = MAKEWORD(1, 1);\r
9183   err = WSAStartup(wVersionRequested, &wsaData);\r
9184   if (err != 0) return err;\r
9185 \r
9186   /* Resolve remote host name */\r
9187   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9188   if (!(hp = gethostbyname(host))) {\r
9189     unsigned int b0, b1, b2, b3;\r
9190 \r
9191     err = WSAGetLastError();\r
9192 \r
9193     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9194       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9195       hp->h_addrtype = AF_INET;\r
9196       hp->h_length = 4;\r
9197       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9198       hp->h_addr_list[0] = (char *) malloc(4);\r
9199       hp->h_addr_list[0][0] = (char) b0;\r
9200       hp->h_addr_list[0][1] = (char) b1;\r
9201       hp->h_addr_list[0][2] = (char) b2;\r
9202       hp->h_addr_list[0][3] = (char) b3;\r
9203     } else {\r
9204       WSACleanup();\r
9205       return err;\r
9206     }\r
9207   }\r
9208   sa.sin_family = hp->h_addrtype;\r
9209   uport = (unsigned short) 514;\r
9210   sa.sin_port = htons(uport);\r
9211   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9212 \r
9213   /* Bind local socket to unused "privileged" port address\r
9214    */\r
9215   s = INVALID_SOCKET;\r
9216   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9217   mysa.sin_family = AF_INET;\r
9218   mysa.sin_addr.s_addr = INADDR_ANY;\r
9219   for (fromPort = 1023;; fromPort--) {\r
9220     if (fromPort < 0) {\r
9221       WSACleanup();\r
9222       return WSAEADDRINUSE;\r
9223     }\r
9224     if (s == INVALID_SOCKET) {\r
9225       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9226         err = WSAGetLastError();\r
9227         WSACleanup();\r
9228         return err;\r
9229       }\r
9230     }\r
9231     uport = (unsigned short) fromPort;\r
9232     mysa.sin_port = htons(uport);\r
9233     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9234         == SOCKET_ERROR) {\r
9235       err = WSAGetLastError();\r
9236       if (err == WSAEADDRINUSE) continue;\r
9237       WSACleanup();\r
9238       return err;\r
9239     }\r
9240     if (connect(s, (struct sockaddr *) &sa,\r
9241       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9242       err = WSAGetLastError();\r
9243       if (err == WSAEADDRINUSE) {\r
9244         closesocket(s);\r
9245         s = -1;\r
9246         continue;\r
9247       }\r
9248       WSACleanup();\r
9249       return err;\r
9250     }\r
9251     break;\r
9252   }\r
9253 \r
9254   /* Bind stderr local socket to unused "privileged" port address\r
9255    */\r
9256   s2 = INVALID_SOCKET;\r
9257   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9258   mysa.sin_family = AF_INET;\r
9259   mysa.sin_addr.s_addr = INADDR_ANY;\r
9260   for (fromPort = 1023;; fromPort--) {\r
9261     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9262     if (fromPort < 0) {\r
9263       (void) closesocket(s);\r
9264       WSACleanup();\r
9265       return WSAEADDRINUSE;\r
9266     }\r
9267     if (s2 == INVALID_SOCKET) {\r
9268       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9269         err = WSAGetLastError();\r
9270         closesocket(s);\r
9271         WSACleanup();\r
9272         return err;\r
9273       }\r
9274     }\r
9275     uport = (unsigned short) fromPort;\r
9276     mysa.sin_port = htons(uport);\r
9277     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9278         == SOCKET_ERROR) {\r
9279       err = WSAGetLastError();\r
9280       if (err == WSAEADDRINUSE) continue;\r
9281       (void) closesocket(s);\r
9282       WSACleanup();\r
9283       return err;\r
9284     }\r
9285     if (listen(s2, 1) == SOCKET_ERROR) {\r
9286       err = WSAGetLastError();\r
9287       if (err == WSAEADDRINUSE) {\r
9288         closesocket(s2);\r
9289         s2 = INVALID_SOCKET;\r
9290         continue;\r
9291       }\r
9292       (void) closesocket(s);\r
9293       (void) closesocket(s2);\r
9294       WSACleanup();\r
9295       return err;\r
9296     }\r
9297     break;\r
9298   }\r
9299   prevStderrPort = fromPort; // remember port used\r
9300   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9301 \r
9302   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9303     err = WSAGetLastError();\r
9304     (void) closesocket(s);\r
9305     (void) closesocket(s2);\r
9306     WSACleanup();\r
9307     return err;\r
9308   }\r
9309 \r
9310   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9311     err = WSAGetLastError();\r
9312     (void) closesocket(s);\r
9313     (void) closesocket(s2);\r
9314     WSACleanup();\r
9315     return err;\r
9316   }\r
9317   if (*user == NULLCHAR) user = UserName();\r
9318   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9319     err = WSAGetLastError();\r
9320     (void) closesocket(s);\r
9321     (void) closesocket(s2);\r
9322     WSACleanup();\r
9323     return err;\r
9324   }\r
9325   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9326     err = WSAGetLastError();\r
9327     (void) closesocket(s);\r
9328     (void) closesocket(s2);\r
9329     WSACleanup();\r
9330     return err;\r
9331   }\r
9332 \r
9333   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9334     err = WSAGetLastError();\r
9335     (void) closesocket(s);\r
9336     (void) closesocket(s2);\r
9337     WSACleanup();\r
9338     return err;\r
9339   }\r
9340   (void) closesocket(s2);  /* Stop listening */\r
9341 \r
9342   /* Prepare return value */\r
9343   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9344   cp->kind = CPRcmd;\r
9345   cp->sock = s;\r
9346   cp->sock2 = s3;\r
9347   *pr = (ProcRef *) cp;\r
9348 \r
9349   return NO_ERROR;\r
9350 }\r
9351 \r
9352 \r
9353 InputSourceRef\r
9354 AddInputSource(ProcRef pr, int lineByLine,\r
9355                InputCallback func, VOIDSTAR closure)\r
9356 {\r
9357   InputSource *is, *is2 = NULL;\r
9358   ChildProc *cp = (ChildProc *) pr;\r
9359 \r
9360   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9361   is->lineByLine = lineByLine;\r
9362   is->func = func;\r
9363   is->closure = closure;\r
9364   is->second = NULL;\r
9365   is->next = is->buf;\r
9366   if (pr == NoProc) {\r
9367     is->kind = CPReal;\r
9368     consoleInputSource = is;\r
9369   } else {\r
9370     is->kind = cp->kind;\r
9371     /* \r
9372         [AS] Try to avoid a race condition if the thread is given control too early:\r
9373         we create all threads suspended so that the is->hThread variable can be\r
9374         safely assigned, then let the threads start with ResumeThread.\r
9375     */\r
9376     switch (cp->kind) {\r
9377     case CPReal:\r
9378       is->hFile = cp->hFrom;\r
9379       cp->hFrom = NULL; /* now owned by InputThread */\r
9380       is->hThread =\r
9381         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9382                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9383       break;\r
9384 \r
9385     case CPComm:\r
9386       is->hFile = cp->hFrom;\r
9387       cp->hFrom = NULL; /* now owned by InputThread */\r
9388       is->hThread =\r
9389         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9390                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9391       break;\r
9392 \r
9393     case CPSock:\r
9394       is->sock = cp->sock;\r
9395       is->hThread =\r
9396         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9397                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9398       break;\r
9399 \r
9400     case CPRcmd:\r
9401       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9402       *is2 = *is;\r
9403       is->sock = cp->sock;\r
9404       is->second = is2;\r
9405       is2->sock = cp->sock2;\r
9406       is2->second = is2;\r
9407       is->hThread =\r
9408         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9409                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9410       is2->hThread =\r
9411         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9412                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9413       break;\r
9414     }\r
9415 \r
9416     if( is->hThread != NULL ) {\r
9417         ResumeThread( is->hThread );\r
9418     }\r
9419 \r
9420     if( is2 != NULL && is2->hThread != NULL ) {\r
9421         ResumeThread( is2->hThread );\r
9422     }\r
9423   }\r
9424 \r
9425   return (InputSourceRef) is;\r
9426 }\r
9427 \r
9428 void\r
9429 RemoveInputSource(InputSourceRef isr)\r
9430 {\r
9431   InputSource *is;\r
9432 \r
9433   is = (InputSource *) isr;\r
9434   is->hThread = NULL;  /* tell thread to stop */\r
9435   CloseHandle(is->hThread);\r
9436   if (is->second != NULL) {\r
9437     is->second->hThread = NULL;\r
9438     CloseHandle(is->second->hThread);\r
9439   }\r
9440 }\r
9441 \r
9442 int no_wrap(char *message, int count)\r
9443 {\r
9444     ConsoleOutput(message, count, FALSE);\r
9445     return count;\r
9446 }\r
9447 \r
9448 int\r
9449 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9450 {\r
9451   DWORD dOutCount;\r
9452   int outCount = SOCKET_ERROR;\r
9453   ChildProc *cp = (ChildProc *) pr;\r
9454   static OVERLAPPED ovl;\r
9455   static int line = 0;\r
9456 \r
9457   if (pr == NoProc)\r
9458   {\r
9459     if (appData.noJoin || !appData.useInternalWrap)\r
9460       return no_wrap(message, count);\r
9461     else\r
9462     {\r
9463       int width = get_term_width();\r
9464       int len = wrap(NULL, message, count, width, &line);\r
9465       char *msg = malloc(len);\r
9466       int dbgchk;\r
9467 \r
9468       if (!msg)\r
9469         return no_wrap(message, count);\r
9470       else\r
9471       {\r
9472         dbgchk = wrap(msg, message, count, width, &line);\r
9473         if (dbgchk != len && appData.debugMode)\r
9474             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9475         ConsoleOutput(msg, len, FALSE);\r
9476         free(msg);\r
9477         return len;\r
9478       }\r
9479     }\r
9480   }\r
9481 \r
9482   if (ovl.hEvent == NULL) {\r
9483     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9484   }\r
9485   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9486 \r
9487   switch (cp->kind) {\r
9488   case CPSock:\r
9489   case CPRcmd:\r
9490     outCount = send(cp->sock, message, count, 0);\r
9491     if (outCount == SOCKET_ERROR) {\r
9492       *outError = WSAGetLastError();\r
9493     } else {\r
9494       *outError = NO_ERROR;\r
9495     }\r
9496     break;\r
9497 \r
9498   case CPReal:\r
9499     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9500                   &dOutCount, NULL)) {\r
9501       *outError = NO_ERROR;\r
9502       outCount = (int) dOutCount;\r
9503     } else {\r
9504       *outError = GetLastError();\r
9505     }\r
9506     break;\r
9507 \r
9508   case CPComm:\r
9509     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9510                             &dOutCount, &ovl);\r
9511     if (*outError == NO_ERROR) {\r
9512       outCount = (int) dOutCount;\r
9513     }\r
9514     break;\r
9515   }\r
9516   return outCount;\r
9517 }\r
9518 \r
9519 int\r
9520 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9521                        long msdelay)\r
9522 {\r
9523   /* Ignore delay, not implemented for WinBoard */\r
9524   return OutputToProcess(pr, message, count, outError);\r
9525 }\r
9526 \r
9527 \r
9528 void\r
9529 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9530                         char *buf, int count, int error)\r
9531 {\r
9532   DisplayFatalError(_("Not implemented"), 0, 1);\r
9533 }\r
9534 \r
9535 /* see wgamelist.c for Game List functions */\r
9536 /* see wedittags.c for Edit Tags functions */\r
9537 \r
9538 \r
9539 VOID\r
9540 ICSInitScript()\r
9541 {\r
9542   FILE *f;\r
9543   char buf[MSG_SIZ];\r
9544   char *dummy;\r
9545 \r
9546   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9547     f = fopen(buf, "r");\r
9548     if (f != NULL) {\r
9549       ProcessICSInitScript(f);\r
9550       fclose(f);\r
9551     }\r
9552   }\r
9553 }\r
9554 \r
9555 \r
9556 VOID\r
9557 StartAnalysisClock()\r
9558 {\r
9559   if (analysisTimerEvent) return;\r
9560   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9561                                         (UINT) 2000, NULL);\r
9562 }\r
9563 \r
9564 VOID\r
9565 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9566 {\r
9567   highlightInfo.sq[0].x = fromX;\r
9568   highlightInfo.sq[0].y = fromY;\r
9569   highlightInfo.sq[1].x = toX;\r
9570   highlightInfo.sq[1].y = toY;\r
9571 }\r
9572 \r
9573 VOID\r
9574 ClearHighlights()\r
9575 {\r
9576   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9577     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9578 }\r
9579 \r
9580 VOID\r
9581 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9582 {\r
9583   premoveHighlightInfo.sq[0].x = fromX;\r
9584   premoveHighlightInfo.sq[0].y = fromY;\r
9585   premoveHighlightInfo.sq[1].x = toX;\r
9586   premoveHighlightInfo.sq[1].y = toY;\r
9587 }\r
9588 \r
9589 VOID\r
9590 ClearPremoveHighlights()\r
9591 {\r
9592   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9593     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9594 }\r
9595 \r
9596 VOID\r
9597 ShutDownFrontEnd()\r
9598 {\r
9599   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9600   DeleteClipboardTempFiles();\r
9601 }\r
9602 \r
9603 void\r
9604 BoardToTop()\r
9605 {\r
9606     if (IsIconic(hwndMain))\r
9607       ShowWindow(hwndMain, SW_RESTORE);\r
9608 \r
9609     SetActiveWindow(hwndMain);\r
9610 }\r
9611 \r
9612 /*\r
9613  * Prototypes for animation support routines\r
9614  */\r
9615 static void ScreenSquare(int column, int row, POINT * pt);\r
9616 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9617      POINT frames[], int * nFrames);\r
9618 \r
9619 \r
9620 #define kFactor 4\r
9621 \r
9622 void\r
9623 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9624 {       // [HGM] atomic: animate blast wave\r
9625         int i;\r
9626 \r
9627         explodeInfo.fromX = fromX;\r
9628         explodeInfo.fromY = fromY;\r
9629         explodeInfo.toX = toX;\r
9630         explodeInfo.toY = toY;\r
9631         for(i=1; i<4*kFactor; i++) {\r
9632             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9633             DrawPosition(FALSE, board);\r
9634             Sleep(appData.animSpeed);\r
9635         }\r
9636         explodeInfo.radius = 0;\r
9637         DrawPosition(TRUE, board);\r
9638 }\r
9639 \r
9640 void\r
9641 AnimateMove(board, fromX, fromY, toX, toY)\r
9642      Board board;\r
9643      int fromX;\r
9644      int fromY;\r
9645      int toX;\r
9646      int toY;\r
9647 {\r
9648   ChessSquare piece;\r
9649   POINT start, finish, mid;\r
9650   POINT frames[kFactor * 2 + 1];\r
9651   int nFrames, n;\r
9652 \r
9653   if (!appData.animate) return;\r
9654   if (doingSizing) return;\r
9655   if (fromY < 0 || fromX < 0) return;\r
9656   piece = board[fromY][fromX];\r
9657   if (piece >= EmptySquare) return;\r
9658 \r
9659   ScreenSquare(fromX, fromY, &start);\r
9660   ScreenSquare(toX, toY, &finish);\r
9661 \r
9662   /* All moves except knight jumps move in straight line */\r
9663   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9664     mid.x = start.x + (finish.x - start.x) / 2;\r
9665     mid.y = start.y + (finish.y - start.y) / 2;\r
9666   } else {\r
9667     /* Knight: make straight movement then diagonal */\r
9668     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9669        mid.x = start.x + (finish.x - start.x) / 2;\r
9670        mid.y = start.y;\r
9671      } else {\r
9672        mid.x = start.x;\r
9673        mid.y = start.y + (finish.y - start.y) / 2;\r
9674      }\r
9675   }\r
9676   \r
9677   /* Don't use as many frames for very short moves */\r
9678   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9679     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9680   else\r
9681     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9682 \r
9683   animInfo.from.x = fromX;\r
9684   animInfo.from.y = fromY;\r
9685   animInfo.to.x = toX;\r
9686   animInfo.to.y = toY;\r
9687   animInfo.lastpos = start;\r
9688   animInfo.piece = piece;\r
9689   for (n = 0; n < nFrames; n++) {\r
9690     animInfo.pos = frames[n];\r
9691     DrawPosition(FALSE, NULL);\r
9692     animInfo.lastpos = animInfo.pos;\r
9693     Sleep(appData.animSpeed);\r
9694   }\r
9695   animInfo.pos = finish;\r
9696   DrawPosition(FALSE, NULL);\r
9697   animInfo.piece = EmptySquare;\r
9698   Explode(board, fromX, fromY, toX, toY);\r
9699 }\r
9700 \r
9701 /*      Convert board position to corner of screen rect and color       */\r
9702 \r
9703 static void\r
9704 ScreenSquare(column, row, pt)\r
9705      int column; int row; POINT * pt;\r
9706 {\r
9707   if (flipView) {\r
9708     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9709     pt->y = lineGap + row * (squareSize + lineGap);\r
9710   } else {\r
9711     pt->x = lineGap + column * (squareSize + lineGap);\r
9712     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9713   }\r
9714 }\r
9715 \r
9716 /*      Generate a series of frame coords from start->mid->finish.\r
9717         The movement rate doubles until the half way point is\r
9718         reached, then halves back down to the final destination,\r
9719         which gives a nice slow in/out effect. The algorithmn\r
9720         may seem to generate too many intermediates for short\r
9721         moves, but remember that the purpose is to attract the\r
9722         viewers attention to the piece about to be moved and\r
9723         then to where it ends up. Too few frames would be less\r
9724         noticeable.                                             */\r
9725 \r
9726 static void\r
9727 Tween(start, mid, finish, factor, frames, nFrames)\r
9728      POINT * start; POINT * mid;\r
9729      POINT * finish; int factor;\r
9730      POINT frames[]; int * nFrames;\r
9731 {\r
9732   int n, fraction = 1, count = 0;\r
9733 \r
9734   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9735   for (n = 0; n < factor; n++)\r
9736     fraction *= 2;\r
9737   for (n = 0; n < factor; n++) {\r
9738     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9739     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9740     count ++;\r
9741     fraction = fraction / 2;\r
9742   }\r
9743   \r
9744   /* Midpoint */\r
9745   frames[count] = *mid;\r
9746   count ++;\r
9747   \r
9748   /* Slow out, stepping 1/2, then 1/4, ... */\r
9749   fraction = 2;\r
9750   for (n = 0; n < factor; n++) {\r
9751     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9752     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9753     count ++;\r
9754     fraction = fraction * 2;\r
9755   }\r
9756   *nFrames = count;\r
9757 }\r
9758 \r
9759 void\r
9760 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9761 {\r
9762     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9763 \r
9764     EvalGraphSet( first, last, current, pvInfoList );\r
9765 }\r