Add one-click move control to WB general-options dialog
[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 static HWND savedHwnd;\r
190 HWND hCommPort = NULL;    /* currently open comm port */\r
191 static HWND hwndPause;    /* pause button */\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,\r
194   blackSquareBrush, /* [HGM] for band between board and holdings */\r
195   explodeBrush,     /* [HGM] atomic */\r
196   markerBrush,      /* [HGM] markers */\r
197   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
198 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
199 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
200 static HPEN gridPen = NULL;\r
201 static HPEN highlightPen = NULL;\r
202 static HPEN premovePen = NULL;\r
203 static NPLOGPALETTE pLogPal;\r
204 static BOOL paletteChanged = FALSE;\r
205 static HICON iconWhite, iconBlack, iconCurrent;\r
206 static int doingSizing = FALSE;\r
207 static int lastSizing = 0;\r
208 static int prevStderrPort;\r
209 static HBITMAP userLogo;\r
210 \r
211 static HBITMAP liteBackTexture = NULL;\r
212 static HBITMAP darkBackTexture = NULL;\r
213 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
214 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
215 static int backTextureSquareSize = 0;\r
216 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
217 \r
218 #if __GNUC__ && !defined(_winmajor)\r
219 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
220 #else\r
221 #if defined(_winmajor)\r
222 #define oldDialog (_winmajor < 4)\r
223 #else\r
224 #define oldDialog 0\r
225 #endif\r
226 #endif\r
227 \r
228 #define INTERNATIONAL\r
229 \r
230 #ifdef INTERNATIONAL\r
231 #  define _(s) T_(s)\r
232 #  define N_(s) s\r
233 #else\r
234 #  define _(s) s\r
235 #  define N_(s) s\r
236 #  define T_(s) s\r
237 #  define Translate(x, y)\r
238 #  define LoadLanguageFile(s)\r
239 #endif\r
240 \r
241 #ifdef INTERNATIONAL\r
242 \r
243 Boolean barbaric; // flag indicating if translation is needed\r
244 \r
245 // list of item numbers used in each dialog (used to alter language at run time)\r
246 \r
247 #define ABOUTBOX -1  /* not sure why these are needed */\r
248 #define ABOUTBOX2 -1\r
249 \r
250 int dialogItems[][40] = {\r
251 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
252 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
253   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
254 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL }, \r
255 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
256   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
257 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
258 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
259   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
260 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
261 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
262   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
263 { ABOUTBOX2, IDC_ChessBoard }, \r
264 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
265   OPT_GameListClose, IDC_GameListDoFilter }, \r
266 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
267 { DLG_Error, IDOK }, \r
268 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
269   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
270 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
271 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
272   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
273   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
274 { DLG_IndexNumber, IDC_Index }, \r
275 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
276 { DLG_TypeInName, IDOK, IDCANCEL }, \r
277 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
278   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
279 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
280   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
281   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
282   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
283   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
284   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
285   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
286 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
287   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
288   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
289   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
290   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
291   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
292   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
293   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
294   GPB_General, GPB_Alarm }, \r
295 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
296   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
297   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
298   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
299   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
300   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
301   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
302   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size }, \r
303 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
304   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
305   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
306   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
307   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
308   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
309   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
310   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
311   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
312 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
313   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
314   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,\r
315   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
316 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
317 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
318   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
319 { DLG_MoveHistory }, \r
320 { DLG_EvalGraph }, \r
321 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
322 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
323 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
324   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
325   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
326   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
327 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
328   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
329   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
330 { 0 }\r
331 };\r
332 \r
333 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
334 static int lastChecked;\r
335 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
336 extern int tinyLayout;\r
337 extern char * menuBarText[][10];\r
338 \r
339 void\r
340 LoadLanguageFile(char *name)\r
341 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
342     FILE *f;\r
343     int i=0, j=0, n=0, k;\r
344     char buf[MSG_SIZ];\r
345 \r
346     if(!name || name[0] == NULLCHAR) return;\r
347       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
348     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
349     if((f = fopen(buf, "r")) == NULL) return;\r
350     while((k = fgetc(f)) != EOF) {\r
351         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
352         languageBuf[i] = k;\r
353         if(k == '\n') {\r
354             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
355                 char *p;\r
356                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
357                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
358                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
359                         english[j] = languageBuf + n + 1; *p = 0;\r
360                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
361 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
362                     }\r
363                 }\r
364             }\r
365             n = i + 1;\r
366         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
367             switch(k) {\r
368               case 'n': k = '\n'; break;\r
369               case 'r': k = '\r'; break;\r
370               case 't': k = '\t'; break;\r
371             }\r
372             languageBuf[--i] = k;\r
373         }\r
374         i++;\r
375     }\r
376     fclose(f);\r
377     barbaric = (j != 0);\r
378     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
379 }\r
380 \r
381 char *\r
382 T_(char *s)\r
383 {   // return the translation of the given string\r
384     // efficiency can be improved a lot...\r
385     int i=0;\r
386 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
387     if(!barbaric) return s;\r
388     if(!s) return ""; // sanity\r
389     while(english[i]) {\r
390         if(!strcmp(s, english[i])) return foreign[i];\r
391         i++;\r
392     }\r
393     return s;\r
394 }\r
395 \r
396 void\r
397 Translate(HWND hDlg, int dialogID)\r
398 {   // translate all text items in the given dialog\r
399     int i=0, j, k;\r
400     char buf[MSG_SIZ], *s;\r
401 //if(appData.debugMode) fprintf(debugFP, "Translate(%d)\n", dialogID);\r
402     if(!barbaric) return;\r
403     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
404     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
405     GetWindowText( hDlg, buf, MSG_SIZ );\r
406     s = T_(buf);\r
407 //if(appData.debugMode) fprintf(debugFP, "WindowText '%s' -> '%s'\n", buf, s);\r
408     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
409     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
410         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
411         if(strlen(buf) == 0) continue;\r
412         s = T_(buf);\r
413         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
414     }\r
415 }\r
416 \r
417 void\r
418 TranslateMenus(int addLanguage)\r
419 {\r
420     int i, j;\r
421     WIN32_FIND_DATA fileData;\r
422     HANDLE hFind;\r
423 #define IDM_English 1895\r
424     if(1) {\r
425         HMENU mainMenu = GetMenu(hwndMain);\r
426         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
427           HMENU subMenu = GetSubMenu(mainMenu, i);\r
428           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
429                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
430           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
431             char buf[MSG_SIZ];\r
432             UINT k = GetMenuItemID(subMenu, j);\r
433               if(menuText[i][j])
434                 safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) ); else {\r
435                 GetMenuString(subMenu, j, buf, MSG_SIZ, MF_BYPOSITION);\r
436                 menuText[i][j] = strdup(buf); // remember original on first change\r
437             }\r
438             if(buf[0] == NULLCHAR) continue;\r
439 //fprintf(debugFP, "menu(%d,%d) = %s (%08x, %08x) %d\n", i, j, buf, mainMenu, subMenu, k);\r
440             ModifyMenu(subMenu, j, MF_STRING|MF_BYPOSITION\r
441                                    |CheckMenuItem(subMenu, j, MF_BYPOSITION)\r
442                                    |EnableMenuItem(subMenu, j, MF_BYPOSITION), k, T_(buf));\r
443           }\r
444         }\r
445         DrawMenuBar(hwndMain);\r
446     }\r
447 \r
448     if(!addLanguage) return;\r
449     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
450         HMENU mainMenu = GetMenu(hwndMain);\r
451         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
452         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
453         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
454         i = 0; lastChecked = IDM_English;\r
455         do {\r
456             char *p, *q = fileData.cFileName;\r
457             int checkFlag = MF_UNCHECKED;\r
458             languageFile[i] = strdup(q);\r
459             if(barbaric && !strcmp(oldLanguage, q)) {\r
460                 checkFlag = MF_CHECKED;\r
461                 lastChecked = IDM_English + i + 1;\r
462                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
463             }\r
464             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
465             p = strstr(fileData.cFileName, ".lng");\r
466             if(p) *p = 0;\r
467             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
468         } while(FindNextFile(hFind, &fileData));\r
469         FindClose(hFind);\r
470     }\r
471 }\r
472 \r
473 #endif\r
474 \r
475 typedef struct {\r
476   char *name;\r
477   int squareSize;\r
478   int lineGap;\r
479   int smallLayout;\r
480   int tinyLayout;\r
481   int cliWidth, cliHeight;\r
482 } SizeInfo;\r
483 \r
484 SizeInfo sizeInfo[] = \r
485 {\r
486   { "tiny",     21, 0, 1, 1, 0, 0 },\r
487   { "teeny",    25, 1, 1, 1, 0, 0 },\r
488   { "dinky",    29, 1, 1, 1, 0, 0 },\r
489   { "petite",   33, 1, 1, 1, 0, 0 },\r
490   { "slim",     37, 2, 1, 0, 0, 0 },\r
491   { "small",    40, 2, 1, 0, 0, 0 },\r
492   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
493   { "middling", 49, 2, 0, 0, 0, 0 },\r
494   { "average",  54, 2, 0, 0, 0, 0 },\r
495   { "moderate", 58, 3, 0, 0, 0, 0 },\r
496   { "medium",   64, 3, 0, 0, 0, 0 },\r
497   { "bulky",    72, 3, 0, 0, 0, 0 },\r
498   { "large",    80, 3, 0, 0, 0, 0 },\r
499   { "big",      87, 3, 0, 0, 0, 0 },\r
500   { "huge",     95, 3, 0, 0, 0, 0 },\r
501   { "giant",    108, 3, 0, 0, 0, 0 },\r
502   { "colossal", 116, 4, 0, 0, 0, 0 },\r
503   { "titanic",  129, 4, 0, 0, 0, 0 },\r
504   { NULL, 0, 0, 0, 0, 0, 0 }\r
505 };\r
506 \r
507 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
508 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
509 {\r
510   { 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
511   { 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
512   { 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
513   { 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
514   { 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
515   { 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
516   { 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
517   { 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
518   { 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
519   { 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
520   { 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
521   { 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
522   { 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
523   { 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
524   { 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
525   { 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
526   { 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
527   { 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
528 };\r
529 \r
530 MyFont *font[NUM_SIZES][NUM_FONTS];\r
531 \r
532 typedef struct {\r
533   char *label;\r
534   int id;\r
535   HWND hwnd;\r
536   WNDPROC wndproc;\r
537 } MyButtonDesc;\r
538 \r
539 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
540 #define N_BUTTONS 5\r
541 \r
542 MyButtonDesc buttonDesc[N_BUTTONS] =\r
543 {\r
544   {"<<", IDM_ToStart, NULL, NULL},\r
545   {"<", IDM_Backward, NULL, NULL},\r
546   {"P", IDM_Pause, NULL, NULL},\r
547   {">", IDM_Forward, NULL, NULL},\r
548   {">>", IDM_ToEnd, NULL, NULL},\r
549 };\r
550 \r
551 int tinyLayout = 0, smallLayout = 0;\r
552 #define MENU_BAR_ITEMS 9\r
553 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
554   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
555   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
556 };\r
557 \r
558 \r
559 MySound sounds[(int)NSoundClasses];\r
560 MyTextAttribs textAttribs[(int)NColorClasses];\r
561 \r
562 MyColorizeAttribs colorizeAttribs[] = {\r
563   { (COLORREF)0, 0, N_("Shout Text") },\r
564   { (COLORREF)0, 0, N_("SShout/CShout") },\r
565   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
566   { (COLORREF)0, 0, N_("Channel Text") },\r
567   { (COLORREF)0, 0, N_("Kibitz Text") },\r
568   { (COLORREF)0, 0, N_("Tell Text") },\r
569   { (COLORREF)0, 0, N_("Challenge Text") },\r
570   { (COLORREF)0, 0, N_("Request Text") },\r
571   { (COLORREF)0, 0, N_("Seek Text") },\r
572   { (COLORREF)0, 0, N_("Normal Text") },\r
573   { (COLORREF)0, 0, N_("None") }\r
574 };\r
575 \r
576 \r
577 \r
578 static char *commentTitle;\r
579 static char *commentText;\r
580 static int commentIndex;\r
581 static Boolean editComment = FALSE;\r
582 \r
583 \r
584 char errorTitle[MSG_SIZ];\r
585 char errorMessage[2*MSG_SIZ];\r
586 HWND errorDialog = NULL;\r
587 BOOLEAN moveErrorMessageUp = FALSE;\r
588 BOOLEAN consoleEcho = TRUE;\r
589 CHARFORMAT consoleCF;\r
590 COLORREF consoleBackgroundColor;\r
591 \r
592 char *programVersion;\r
593 \r
594 #define CPReal 1\r
595 #define CPComm 2\r
596 #define CPSock 3\r
597 #define CPRcmd 4\r
598 typedef int CPKind;\r
599 \r
600 typedef struct {\r
601   CPKind kind;\r
602   HANDLE hProcess;\r
603   DWORD pid;\r
604   HANDLE hTo;\r
605   HANDLE hFrom;\r
606   SOCKET sock;\r
607   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
608 } ChildProc;\r
609 \r
610 #define INPUT_SOURCE_BUF_SIZE 4096\r
611 \r
612 typedef struct _InputSource {\r
613   CPKind kind;\r
614   HANDLE hFile;\r
615   SOCKET sock;\r
616   int lineByLine;\r
617   HANDLE hThread;\r
618   DWORD id;\r
619   char buf[INPUT_SOURCE_BUF_SIZE];\r
620   char *next;\r
621   DWORD count;\r
622   int error;\r
623   InputCallback func;\r
624   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
625   VOIDSTAR closure;\r
626 } InputSource;\r
627 \r
628 InputSource *consoleInputSource;\r
629 \r
630 DCB dcb;\r
631 \r
632 /* forward */\r
633 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
634 VOID ConsoleCreate();\r
635 LRESULT CALLBACK\r
636   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
637 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
638 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
639 VOID ParseCommSettings(char *arg, DCB *dcb);\r
640 LRESULT CALLBACK\r
641   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
642 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
643 void ParseIcsTextMenu(char *icsTextMenuString);\r
644 VOID PopUpMoveDialog(char firstchar);\r
645 VOID PopUpNameDialog(char firstchar);\r
646 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
647 \r
648 /* [AS] */\r
649 int NewGameFRC();\r
650 int GameListOptions();\r
651 \r
652 int dummy; // [HGM] for obsolete args\r
653 \r
654 HWND hwndMain = NULL;        /* root window*/\r
655 HWND hwndConsole = NULL;\r
656 HWND commentDialog = NULL;\r
657 HWND moveHistoryDialog = NULL;\r
658 HWND evalGraphDialog = NULL;\r
659 HWND engineOutputDialog = NULL;\r
660 HWND gameListDialog = NULL;\r
661 HWND editTagsDialog = NULL;\r
662 \r
663 int commentUp = FALSE;\r
664 \r
665 WindowPlacement wpMain;\r
666 WindowPlacement wpConsole;\r
667 WindowPlacement wpComment;\r
668 WindowPlacement wpMoveHistory;\r
669 WindowPlacement wpEvalGraph;\r
670 WindowPlacement wpEngineOutput;\r
671 WindowPlacement wpGameList;\r
672 WindowPlacement wpTags;\r
673 \r
674 VOID EngineOptionsPopup(); // [HGM] settings\r
675 \r
676 VOID GothicPopUp(char *title, VariantClass variant);\r
677 /*\r
678  * Setting "frozen" should disable all user input other than deleting\r
679  * the window.  We do this while engines are initializing themselves.\r
680  */\r
681 static int frozen = 0;\r
682 static int oldMenuItemState[MENU_BAR_ITEMS];\r
683 void FreezeUI()\r
684 {\r
685   HMENU hmenu;\r
686   int i;\r
687 \r
688   if (frozen) return;\r
689   frozen = 1;\r
690   hmenu = GetMenu(hwndMain);\r
691   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
692     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
693   }\r
694   DrawMenuBar(hwndMain);\r
695 }\r
696 \r
697 /* Undo a FreezeUI */\r
698 void ThawUI()\r
699 {\r
700   HMENU hmenu;\r
701   int i;\r
702 \r
703   if (!frozen) return;\r
704   frozen = 0;\r
705   hmenu = GetMenu(hwndMain);\r
706   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
707     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
708   }\r
709   DrawMenuBar(hwndMain);\r
710 }\r
711 \r
712 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
713 \r
714 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
715 #ifdef JAWS\r
716 #include "jaws.c"\r
717 #else\r
718 #define JAWS_INIT\r
719 #define JAWS_ARGS\r
720 #define JAWS_ALT_INTERCEPT\r
721 #define JAWS_KB_NAVIGATION\r
722 #define JAWS_MENU_ITEMS\r
723 #define JAWS_SILENCE\r
724 #define JAWS_REPLAY\r
725 #define JAWS_ACCEL\r
726 #define JAWS_COPYRIGHT\r
727 #define JAWS_DELETE(X) X\r
728 #define SAYMACHINEMOVE()\r
729 #define SAY(X)\r
730 #endif\r
731 \r
732 /*---------------------------------------------------------------------------*\\r
733  *\r
734  * WinMain\r
735  *\r
736 \*---------------------------------------------------------------------------*/\r
737 \r
738 int APIENTRY\r
739 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
740         LPSTR lpCmdLine, int nCmdShow)\r
741 {\r
742   MSG msg;\r
743   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
744 //  INITCOMMONCONTROLSEX ex;\r
745 \r
746   debugFP = stderr;\r
747 \r
748   LoadLibrary("RICHED32.DLL");\r
749   consoleCF.cbSize = sizeof(CHARFORMAT);\r
750 \r
751   if (!InitApplication(hInstance)) {\r
752     return (FALSE);\r
753   }\r
754   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
755     return (FALSE);\r
756   }\r
757 \r
758   JAWS_INIT\r
759 \r
760 //  InitCommonControlsEx(&ex);\r
761   InitCommonControls();\r
762 \r
763   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
764   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
765   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
766 \r
767   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
768 \r
769   while (GetMessage(&msg, /* message structure */\r
770                     NULL, /* handle of window receiving the message */\r
771                     0,    /* lowest message to examine */\r
772                     0))   /* highest message to examine */\r
773     {\r
774 \r
775       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
776         // [HGM] navigate: switch between all windows with tab\r
777         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
778         int i, currentElement = 0;\r
779 \r
780         // first determine what element of the chain we come from (if any)\r
781         if(appData.icsActive) {\r
782             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
783             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
784         }\r
785         if(engineOutputDialog && EngineOutputIsUp()) {\r
786             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
787             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
788         }\r
789         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
790             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
791         }\r
792         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
793         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
794         if(msg.hwnd == e1)                 currentElement = 2; else\r
795         if(msg.hwnd == e2)                 currentElement = 3; else\r
796         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
797         if(msg.hwnd == mh)                currentElement = 4; else\r
798         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
799         if(msg.hwnd == hText)  currentElement = 5; else\r
800         if(msg.hwnd == hInput) currentElement = 6; else\r
801         for (i = 0; i < N_BUTTONS; i++) {\r
802             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
803         }\r
804 \r
805         // determine where to go to\r
806         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
807           do {\r
808             currentElement = (currentElement + direction) % 7;\r
809             switch(currentElement) {\r
810                 case 0:\r
811                   h = hwndMain; break; // passing this case always makes the loop exit\r
812                 case 1:\r
813                   h = buttonDesc[0].hwnd; break; // could be NULL\r
814                 case 2:\r
815                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
816                   h = e1; break;\r
817                 case 3:\r
818                   if(!EngineOutputIsUp()) continue;\r
819                   h = e2; break;\r
820                 case 4:\r
821                   if(!MoveHistoryIsUp()) continue;\r
822                   h = mh; break;\r
823 //              case 6: // input to eval graph does not seem to get here!\r
824 //                if(!EvalGraphIsUp()) continue;\r
825 //                h = evalGraphDialog; break;\r
826                 case 5:\r
827                   if(!appData.icsActive) continue;\r
828                   SAY("display");\r
829                   h = hText; break;\r
830                 case 6:\r
831                   if(!appData.icsActive) continue;\r
832                   SAY("input");\r
833                   h = hInput; break;\r
834             }\r
835           } while(h == 0);\r
836 \r
837           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
838           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
839           SetFocus(h);\r
840 \r
841           continue; // this message now has been processed\r
842         }\r
843       }\r
844 \r
845       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
846           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
847           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
848           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
849           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
850           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
851           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
852           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
853           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
854           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
855         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
856         for(i=0; i<MAX_CHAT; i++) \r
857             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
858                 done = 1; break;\r
859         }\r
860         if(done) continue; // [HGM] chat: end patch\r
861         TranslateMessage(&msg); /* Translates virtual key codes */\r
862         DispatchMessage(&msg);  /* Dispatches message to window */\r
863       }\r
864     }\r
865 \r
866 \r
867   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
868 }\r
869 \r
870 /*---------------------------------------------------------------------------*\\r
871  *\r
872  * Initialization functions\r
873  *\r
874 \*---------------------------------------------------------------------------*/\r
875 \r
876 void\r
877 SetUserLogo()\r
878 {   // update user logo if necessary\r
879     static char oldUserName[MSG_SIZ], *curName;\r
880 \r
881     if(appData.autoLogo) {\r
882           curName = UserName();\r
883           if(strcmp(curName, oldUserName)) {\r
884             snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
885                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
886                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
887                 if(userLogo == NULL)\r
888                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
889           }\r
890     }\r
891 }\r
892 \r
893 BOOL\r
894 InitApplication(HINSTANCE hInstance)\r
895 {\r
896   WNDCLASS wc;\r
897 \r
898   /* Fill in window class structure with parameters that describe the */\r
899   /* main window. */\r
900 \r
901   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
902   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
903   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
904   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
905   wc.hInstance     = hInstance;         /* Owner of this class */\r
906   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
907   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
908   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
909   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
910   wc.lpszClassName = szAppName;                 /* Name to register as */\r
911 \r
912   /* Register the window class and return success/failure code. */\r
913   if (!RegisterClass(&wc)) return FALSE;\r
914 \r
915   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
916   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
917   wc.cbClsExtra    = 0;\r
918   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
919   wc.hInstance     = hInstance;\r
920   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
921   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
922   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
923   wc.lpszMenuName  = NULL;\r
924   wc.lpszClassName = szConsoleName;\r
925 \r
926   if (!RegisterClass(&wc)) return FALSE;\r
927   return TRUE;\r
928 }\r
929 \r
930 \r
931 /* Set by InitInstance, used by EnsureOnScreen */\r
932 int screenHeight, screenWidth;\r
933 \r
934 void\r
935 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
936 {\r
937 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
938   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
939   if (*x > screenWidth - 32) *x = 0;\r
940   if (*y > screenHeight - 32) *y = 0;\r
941   if (*x < minX) *x = minX;\r
942   if (*y < minY) *y = minY;\r
943 }\r
944 \r
945 BOOL\r
946 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
947 {\r
948   HWND hwnd; /* Main window handle. */\r
949   int ibs;\r
950   WINDOWPLACEMENT wp;\r
951   char *filepart;\r
952 \r
953   hInst = hInstance;    /* Store instance handle in our global variable */\r
954   programName = szAppName;\r
955 \r
956   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
957     *filepart = NULLCHAR;\r
958   } else {\r
959     GetCurrentDirectory(MSG_SIZ, installDir);\r
960   }\r
961   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
962   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
963   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
964   /* xboard, and older WinBoards, controlled the move sound with the\r
965      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
966      always turn the option on (so that the backend will call us),\r
967      then let the user turn the sound off by setting it to silence if\r
968      desired.  To accommodate old winboard.ini files saved by old\r
969      versions of WinBoard, we also turn off the sound if the option\r
970      was initially set to false. [HGM] taken out of InitAppData */\r
971   if (!appData.ringBellAfterMoves) {\r
972     sounds[(int)SoundMove].name = strdup("");\r
973     appData.ringBellAfterMoves = TRUE;\r
974   }\r
975   if (appData.debugMode) {\r
976     debugFP = fopen(appData.nameOfDebugFile, "w");\r
977     setbuf(debugFP, NULL);\r
978   }\r
979 \r
980   LoadLanguageFile(appData.language);\r
981 \r
982   InitBackEnd1();\r
983 \r
984 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
985 //  InitEngineUCI( installDir, &second );\r
986 \r
987   /* Create a main window for this application instance. */\r
988   hwnd = CreateWindow(szAppName, szTitle,\r
989                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
990                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
991                       NULL, NULL, hInstance, NULL);\r
992   hwndMain = hwnd;\r
993 \r
994   /* If window could not be created, return "failure" */\r
995   if (!hwnd) {\r
996     return (FALSE);\r
997   }\r
998 \r
999   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1000   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
1001       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1002 \r
1003       if (first.programLogo == NULL && appData.debugMode) {\r
1004           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
1005       }\r
1006   } else if(appData.autoLogo) {\r
1007       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
1008         char buf[MSG_SIZ];\r
1009           snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.firstDirectory);\r
1010         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
1011       }\r
1012   }\r
1013 \r
1014   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
1015       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1016 \r
1017       if (second.programLogo == NULL && appData.debugMode) {\r
1018           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
1019       }\r
1020   } else if(appData.autoLogo) {\r
1021       char buf[MSG_SIZ];\r
1022       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
1023         snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);\r
1024         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1025       } else\r
1026       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
1027         snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);\r
1028         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
1029       }\r
1030   }\r
1031 \r
1032   SetUserLogo();\r
1033 \r
1034   iconWhite = LoadIcon(hInstance, "icon_white");\r
1035   iconBlack = LoadIcon(hInstance, "icon_black");\r
1036   iconCurrent = iconWhite;\r
1037   InitDrawingColors();\r
1038   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1039   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1040   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1041     /* Compute window size for each board size, and use the largest\r
1042        size that fits on this screen as the default. */\r
1043     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1044     if (boardSize == (BoardSize)-1 &&\r
1045         winH <= screenHeight\r
1046            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1047         && winW <= screenWidth) {\r
1048       boardSize = (BoardSize)ibs;\r
1049     }\r
1050   }\r
1051 \r
1052   InitDrawingSizes(boardSize, 0);\r
1053   TranslateMenus(1);\r
1054   InitMenuChecks();\r
1055   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1056 \r
1057   /* [AS] Load textures if specified */\r
1058   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1059   \r
1060   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1061       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1062       liteBackTextureMode = appData.liteBackTextureMode;\r
1063 \r
1064       if (liteBackTexture == NULL && appData.debugMode) {\r
1065           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1066       }\r
1067   }\r
1068   \r
1069   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1070       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1071       darkBackTextureMode = appData.darkBackTextureMode;\r
1072 \r
1073       if (darkBackTexture == NULL && appData.debugMode) {\r
1074           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1075       }\r
1076   }\r
1077 \r
1078   mysrandom( (unsigned) time(NULL) );\r
1079 \r
1080   /* [AS] Restore layout */\r
1081   if( wpMoveHistory.visible ) {\r
1082       MoveHistoryPopUp();\r
1083   }\r
1084 \r
1085   if( wpEvalGraph.visible ) {\r
1086       EvalGraphPopUp();\r
1087   }\r
1088 \r
1089   if( wpEngineOutput.visible ) {\r
1090       EngineOutputPopUp();\r
1091   }\r
1092 \r
1093   /* Make the window visible; update its client area; and return "success" */\r
1094   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1095   wp.length = sizeof(WINDOWPLACEMENT);\r
1096   wp.flags = 0;\r
1097   wp.showCmd = nCmdShow;\r
1098   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1099   wp.rcNormalPosition.left = wpMain.x;\r
1100   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1101   wp.rcNormalPosition.top = wpMain.y;\r
1102   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1103   SetWindowPlacement(hwndMain, &wp);\r
1104 \r
1105   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1106 \r
1107   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1108                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1109 \r
1110   if (hwndConsole) {\r
1111 #if AOT_CONSOLE\r
1112     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1113                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1114 #endif\r
1115     ShowWindow(hwndConsole, nCmdShow);\r
1116     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
1117       char buf[MSG_SIZ], *p = buf, *q;\r
1118         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
1119       do {\r
1120         q = strchr(p, ';');\r
1121         if(q) *q++ = 0;\r
1122         if(*p) ChatPopUp(p);\r
1123       } while(p=q);\r
1124     }\r
1125     SetActiveWindow(hwndConsole);\r
1126   }\r
1127   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1128   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1129 \r
1130   return TRUE;\r
1131 \r
1132 }\r
1133 \r
1134 VOID\r
1135 InitMenuChecks()\r
1136 {\r
1137   HMENU hmenu = GetMenu(hwndMain);\r
1138 \r
1139   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1140                         MF_BYCOMMAND|((appData.icsActive &&\r
1141                                        *appData.icsCommPort != NULLCHAR) ?\r
1142                                       MF_ENABLED : MF_GRAYED));\r
1143   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1144                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1145                                      MF_CHECKED : MF_UNCHECKED));\r
1146 }\r
1147 \r
1148 //---------------------------------------------------------------------------------------------------------\r
1149 \r
1150 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1151 #define XBOARD FALSE\r
1152 \r
1153 #define OPTCHAR "/"\r
1154 #define SEPCHAR "="\r
1155 \r
1156 #include "args.h"\r
1157 \r
1158 // front-end part of option handling\r
1159 \r
1160 VOID\r
1161 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1162 {\r
1163   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1164   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1165   DeleteDC(hdc);\r
1166   lf->lfWidth = 0;\r
1167   lf->lfEscapement = 0;\r
1168   lf->lfOrientation = 0;\r
1169   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1170   lf->lfItalic = mfp->italic;\r
1171   lf->lfUnderline = mfp->underline;\r
1172   lf->lfStrikeOut = mfp->strikeout;\r
1173   lf->lfCharSet = mfp->charset;\r
1174   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1175   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1176   lf->lfQuality = DEFAULT_QUALITY;\r
1177   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1178     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1179 }\r
1180 \r
1181 void\r
1182 CreateFontInMF(MyFont *mf)\r
1183\r
1184   LFfromMFP(&mf->lf, &mf->mfp);\r
1185   if (mf->hf) DeleteObject(mf->hf);\r
1186   mf->hf = CreateFontIndirect(&mf->lf);\r
1187 }\r
1188 \r
1189 // [HGM] This platform-dependent table provides the location for storing the color info\r
1190 void *\r
1191 colorVariable[] = {\r
1192   &whitePieceColor, \r
1193   &blackPieceColor, \r
1194   &lightSquareColor,\r
1195   &darkSquareColor, \r
1196   &highlightSquareColor,\r
1197   &premoveHighlightColor,\r
1198   NULL,\r
1199   &consoleBackgroundColor,\r
1200   &appData.fontForeColorWhite,\r
1201   &appData.fontBackColorWhite,\r
1202   &appData.fontForeColorBlack,\r
1203   &appData.fontBackColorBlack,\r
1204   &appData.evalHistColorWhite,\r
1205   &appData.evalHistColorBlack,\r
1206   &appData.highlightArrowColor,\r
1207 };\r
1208 \r
1209 /* Command line font name parser.  NULL name means do nothing.\r
1210    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1211    For backward compatibility, syntax without the colon is also\r
1212    accepted, but font names with digits in them won't work in that case.\r
1213 */\r
1214 VOID\r
1215 ParseFontName(char *name, MyFontParams *mfp)\r
1216 {\r
1217   char *p, *q;\r
1218   if (name == NULL) return;\r
1219   p = name;\r
1220   q = strchr(p, ':');\r
1221   if (q) {\r
1222     if (q - p >= sizeof(mfp->faceName))\r
1223       ExitArgError(_("Font name too long:"), name);\r
1224     memcpy(mfp->faceName, p, q - p);\r
1225     mfp->faceName[q - p] = NULLCHAR;\r
1226     p = q + 1;\r
1227   } else {\r
1228     q = mfp->faceName;\r
1229     while (*p && !isdigit(*p)) {\r
1230       *q++ = *p++;\r
1231       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1232         ExitArgError(_("Font name too long:"), name);\r
1233     }\r
1234     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1235     *q = NULLCHAR;\r
1236   }\r
1237   if (!*p) ExitArgError(_("Font point size missing:"), name);\r
1238   mfp->pointSize = (float) atof(p);\r
1239   mfp->bold = (strchr(p, 'b') != NULL);\r
1240   mfp->italic = (strchr(p, 'i') != NULL);\r
1241   mfp->underline = (strchr(p, 'u') != NULL);\r
1242   mfp->strikeout = (strchr(p, 's') != NULL);\r
1243   mfp->charset = DEFAULT_CHARSET;\r
1244   q = strchr(p, 'c');\r
1245   if (q)\r
1246     mfp->charset = (BYTE) atoi(q+1);\r
1247 }\r
1248 \r
1249 void\r
1250 ParseFont(char *name, int number)\r
1251 { // wrapper to shield back-end from 'font'\r
1252   ParseFontName(name, &font[boardSize][number]->mfp);\r
1253 }\r
1254 \r
1255 void\r
1256 SetFontDefaults()\r
1257 { // in WB  we have a 2D array of fonts; this initializes their description\r
1258   int i, j;\r
1259   /* Point font array elements to structures and\r
1260      parse default font names */\r
1261   for (i=0; i<NUM_FONTS; i++) {\r
1262     for (j=0; j<NUM_SIZES; j++) {\r
1263       font[j][i] = &fontRec[j][i];\r
1264       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1265     }\r
1266   }\r
1267 }\r
1268 \r
1269 void\r
1270 CreateFonts()\r
1271 { // here we create the actual fonts from the selected descriptions\r
1272   int i, j;\r
1273   for (i=0; i<NUM_FONTS; i++) {\r
1274     for (j=0; j<NUM_SIZES; j++) {\r
1275       CreateFontInMF(font[j][i]);\r
1276     }\r
1277   }\r
1278 }\r
1279 /* Color name parser.\r
1280    X version accepts X color names, but this one\r
1281    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1282 COLORREF\r
1283 ParseColorName(char *name)\r
1284 {\r
1285   int red, green, blue, count;\r
1286   char buf[MSG_SIZ];\r
1287 \r
1288   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1289   if (count != 3) {\r
1290     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1291       &red, &green, &blue);\r
1292   }\r
1293   if (count != 3) {\r
1294     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1295     DisplayError(buf, 0);\r
1296     return RGB(0, 0, 0);\r
1297   }\r
1298   return PALETTERGB(red, green, blue);\r
1299 }\r
1300 \r
1301 void\r
1302 ParseColor(int n, char *name)\r
1303 { // for WinBoard the color is an int, which needs to be derived from the string\r
1304   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1305 }\r
1306 \r
1307 void\r
1308 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1309 {\r
1310   char *e = argValue;\r
1311   int eff = 0;\r
1312 \r
1313   while (*e) {\r
1314     if (*e == 'b')      eff |= CFE_BOLD;\r
1315     else if (*e == 'i') eff |= CFE_ITALIC;\r
1316     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1317     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1318     else if (*e == '#' || isdigit(*e)) break;\r
1319     e++;\r
1320   }\r
1321   *effects = eff;\r
1322   *color   = ParseColorName(e);\r
1323 }\r
1324 \r
1325 void\r
1326 ParseTextAttribs(ColorClass cc, char *s)\r
1327 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1328     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1329     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1330 }\r
1331 \r
1332 void\r
1333 ParseBoardSize(void *addr, char *name)\r
1334 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1335   BoardSize bs = SizeTiny;\r
1336   while (sizeInfo[bs].name != NULL) {\r
1337     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1338         *(BoardSize *)addr = bs;\r
1339         return;\r
1340     }\r
1341     bs++;\r
1342   }\r
1343   ExitArgError(_("Unrecognized board size value"), name);\r
1344 }\r
1345 \r
1346 void\r
1347 LoadAllSounds()\r
1348 { // [HGM] import name from appData first\r
1349   ColorClass cc;\r
1350   SoundClass sc;\r
1351   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1352     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1353     textAttribs[cc].sound.data = NULL;\r
1354     MyLoadSound(&textAttribs[cc].sound);\r
1355   }\r
1356   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1357     textAttribs[cc].sound.name = strdup("");\r
1358     textAttribs[cc].sound.data = NULL;\r
1359   }\r
1360   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1361     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1362     sounds[sc].data = NULL;\r
1363     MyLoadSound(&sounds[sc]);\r
1364   }\r
1365 }\r
1366 \r
1367 void\r
1368 SetCommPortDefaults()\r
1369 {\r
1370    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1371   dcb.DCBlength = sizeof(DCB);\r
1372   dcb.BaudRate = 9600;\r
1373   dcb.fBinary = TRUE;\r
1374   dcb.fParity = FALSE;\r
1375   dcb.fOutxCtsFlow = FALSE;\r
1376   dcb.fOutxDsrFlow = FALSE;\r
1377   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1378   dcb.fDsrSensitivity = FALSE;\r
1379   dcb.fTXContinueOnXoff = TRUE;\r
1380   dcb.fOutX = FALSE;\r
1381   dcb.fInX = FALSE;\r
1382   dcb.fNull = FALSE;\r
1383   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1384   dcb.fAbortOnError = FALSE;\r
1385   dcb.ByteSize = 7;\r
1386   dcb.Parity = SPACEPARITY;\r
1387   dcb.StopBits = ONESTOPBIT;\r
1388 }\r
1389 \r
1390 // [HGM] args: these three cases taken out to stay in front-end\r
1391 void\r
1392 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1393 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1394         // while the curent board size determines the element. This system should be ported to XBoard.\r
1395         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1396         int bs;\r
1397         for (bs=0; bs<NUM_SIZES; bs++) {\r
1398           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1399           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1400           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1401             ad->argName, mfp->faceName, mfp->pointSize,\r
1402             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1403             mfp->bold ? "b" : "",\r
1404             mfp->italic ? "i" : "",\r
1405             mfp->underline ? "u" : "",\r
1406             mfp->strikeout ? "s" : "",\r
1407             (int)mfp->charset);\r
1408         }\r
1409       }\r
1410 \r
1411 void\r
1412 ExportSounds()\r
1413 { // [HGM] copy the names from the internal WB variables to appData\r
1414   ColorClass cc;\r
1415   SoundClass sc;\r
1416   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1417     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1418   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1419     (&appData.soundMove)[sc] = sounds[sc].name;\r
1420 }\r
1421 \r
1422 void\r
1423 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1424 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1425         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1426         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1427           (ta->effects & CFE_BOLD) ? "b" : "",\r
1428           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1429           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1430           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1431           (ta->effects) ? " " : "",\r
1432           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1433       }\r
1434 \r
1435 void\r
1436 SaveColor(FILE *f, ArgDescriptor *ad)\r
1437 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1438         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1439         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1440           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1441 }\r
1442 \r
1443 void\r
1444 SaveBoardSize(FILE *f, char *name, void *addr)\r
1445 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1446   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1447 }\r
1448 \r
1449 void\r
1450 ParseCommPortSettings(char *s)\r
1451 { // wrapper to keep dcb from back-end\r
1452   ParseCommSettings(s, &dcb);\r
1453 }\r
1454 \r
1455 void\r
1456 GetWindowCoords()\r
1457 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1458   GetActualPlacement(hwndMain, &wpMain);\r
1459   GetActualPlacement(hwndConsole, &wpConsole);\r
1460   GetActualPlacement(commentDialog, &wpComment);\r
1461   GetActualPlacement(editTagsDialog, &wpTags);\r
1462   GetActualPlacement(gameListDialog, &wpGameList);\r
1463   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1464   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1465   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1466 }\r
1467 \r
1468 void\r
1469 PrintCommPortSettings(FILE *f, char *name)\r
1470 { // wrapper to shield back-end from DCB\r
1471       PrintCommSettings(f, name, &dcb);\r
1472 }\r
1473 \r
1474 int\r
1475 MySearchPath(char *installDir, char *name, char *fullname)\r
1476 {\r
1477   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1478   if(name[0]== '%') {\r
1479     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1480     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1481       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1482       *strchr(buf, '%') = 0;\r
1483       strcat(fullname, getenv(buf));\r
1484       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1485     }\r
1486     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1487     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1488     return (int) strlen(fullname);\r
1489   }\r
1490   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1491 }\r
1492 \r
1493 int\r
1494 MyGetFullPathName(char *name, char *fullname)\r
1495 {\r
1496   char *dummy;\r
1497   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1498 }\r
1499 \r
1500 int\r
1501 MainWindowUp()\r
1502 { // [HGM] args: allows testing if main window is realized from back-end\r
1503   return hwndMain != NULL;\r
1504 }\r
1505 \r
1506 void\r
1507 PopUpStartupDialog()\r
1508 {\r
1509     FARPROC lpProc;\r
1510     \r
1511     LoadLanguageFile(appData.language);\r
1512     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1513     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1514     FreeProcInstance(lpProc);\r
1515 }\r
1516 \r
1517 /*---------------------------------------------------------------------------*\\r
1518  *\r
1519  * GDI board drawing routines\r
1520  *\r
1521 \*---------------------------------------------------------------------------*/\r
1522 \r
1523 /* [AS] Draw square using background texture */\r
1524 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1525 {\r
1526     XFORM   x;\r
1527 \r
1528     if( mode == 0 ) {\r
1529         return; /* Should never happen! */\r
1530     }\r
1531 \r
1532     SetGraphicsMode( dst, GM_ADVANCED );\r
1533 \r
1534     switch( mode ) {\r
1535     case 1:\r
1536         /* Identity */\r
1537         break;\r
1538     case 2:\r
1539         /* X reflection */\r
1540         x.eM11 = -1.0;\r
1541         x.eM12 = 0;\r
1542         x.eM21 = 0;\r
1543         x.eM22 = 1.0;\r
1544         x.eDx = (FLOAT) dw + dx - 1;\r
1545         x.eDy = 0;\r
1546         dx = 0;\r
1547         SetWorldTransform( dst, &x );\r
1548         break;\r
1549     case 3:\r
1550         /* Y reflection */\r
1551         x.eM11 = 1.0;\r
1552         x.eM12 = 0;\r
1553         x.eM21 = 0;\r
1554         x.eM22 = -1.0;\r
1555         x.eDx = 0;\r
1556         x.eDy = (FLOAT) dh + dy - 1;\r
1557         dy = 0;\r
1558         SetWorldTransform( dst, &x );\r
1559         break;\r
1560     case 4:\r
1561         /* X/Y flip */\r
1562         x.eM11 = 0;\r
1563         x.eM12 = 1.0;\r
1564         x.eM21 = 1.0;\r
1565         x.eM22 = 0;\r
1566         x.eDx = (FLOAT) dx;\r
1567         x.eDy = (FLOAT) dy;\r
1568         dx = 0;\r
1569         dy = 0;\r
1570         SetWorldTransform( dst, &x );\r
1571         break;\r
1572     }\r
1573 \r
1574     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1575 \r
1576     x.eM11 = 1.0;\r
1577     x.eM12 = 0;\r
1578     x.eM21 = 0;\r
1579     x.eM22 = 1.0;\r
1580     x.eDx = 0;\r
1581     x.eDy = 0;\r
1582     SetWorldTransform( dst, &x );\r
1583 \r
1584     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1585 }\r
1586 \r
1587 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1588 enum {\r
1589     PM_WP = (int) WhitePawn, \r
1590     PM_WN = (int) WhiteKnight, \r
1591     PM_WB = (int) WhiteBishop, \r
1592     PM_WR = (int) WhiteRook, \r
1593     PM_WQ = (int) WhiteQueen, \r
1594     PM_WF = (int) WhiteFerz, \r
1595     PM_WW = (int) WhiteWazir, \r
1596     PM_WE = (int) WhiteAlfil, \r
1597     PM_WM = (int) WhiteMan, \r
1598     PM_WO = (int) WhiteCannon, \r
1599     PM_WU = (int) WhiteUnicorn, \r
1600     PM_WH = (int) WhiteNightrider, \r
1601     PM_WA = (int) WhiteAngel, \r
1602     PM_WC = (int) WhiteMarshall, \r
1603     PM_WAB = (int) WhiteCardinal, \r
1604     PM_WD = (int) WhiteDragon, \r
1605     PM_WL = (int) WhiteLance, \r
1606     PM_WS = (int) WhiteCobra, \r
1607     PM_WV = (int) WhiteFalcon, \r
1608     PM_WSG = (int) WhiteSilver, \r
1609     PM_WG = (int) WhiteGrasshopper, \r
1610     PM_WK = (int) WhiteKing,\r
1611     PM_BP = (int) BlackPawn, \r
1612     PM_BN = (int) BlackKnight, \r
1613     PM_BB = (int) BlackBishop, \r
1614     PM_BR = (int) BlackRook, \r
1615     PM_BQ = (int) BlackQueen, \r
1616     PM_BF = (int) BlackFerz, \r
1617     PM_BW = (int) BlackWazir, \r
1618     PM_BE = (int) BlackAlfil, \r
1619     PM_BM = (int) BlackMan,\r
1620     PM_BO = (int) BlackCannon, \r
1621     PM_BU = (int) BlackUnicorn, \r
1622     PM_BH = (int) BlackNightrider, \r
1623     PM_BA = (int) BlackAngel, \r
1624     PM_BC = (int) BlackMarshall, \r
1625     PM_BG = (int) BlackGrasshopper, \r
1626     PM_BAB = (int) BlackCardinal,\r
1627     PM_BD = (int) BlackDragon,\r
1628     PM_BL = (int) BlackLance,\r
1629     PM_BS = (int) BlackCobra,\r
1630     PM_BV = (int) BlackFalcon,\r
1631     PM_BSG = (int) BlackSilver,\r
1632     PM_BK = (int) BlackKing\r
1633 };\r
1634 \r
1635 static HFONT hPieceFont = NULL;\r
1636 static HBITMAP hPieceMask[(int) EmptySquare];\r
1637 static HBITMAP hPieceFace[(int) EmptySquare];\r
1638 static int fontBitmapSquareSize = 0;\r
1639 static char pieceToFontChar[(int) EmptySquare] =\r
1640                               { 'p', 'n', 'b', 'r', 'q', \r
1641                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1642                       'k', 'o', 'm', 'v', 't', 'w', \r
1643                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1644                                                               'l' };\r
1645 \r
1646 extern BOOL SetCharTable( char *table, const char * map );\r
1647 /* [HGM] moved to backend.c */\r
1648 \r
1649 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1650 {\r
1651     HBRUSH hbrush;\r
1652     BYTE r1 = GetRValue( color );\r
1653     BYTE g1 = GetGValue( color );\r
1654     BYTE b1 = GetBValue( color );\r
1655     BYTE r2 = r1 / 2;\r
1656     BYTE g2 = g1 / 2;\r
1657     BYTE b2 = b1 / 2;\r
1658     RECT rc;\r
1659 \r
1660     /* Create a uniform background first */\r
1661     hbrush = CreateSolidBrush( color );\r
1662     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1663     FillRect( hdc, &rc, hbrush );\r
1664     DeleteObject( hbrush );\r
1665     \r
1666     if( mode == 1 ) {\r
1667         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1668         int steps = squareSize / 2;\r
1669         int i;\r
1670 \r
1671         for( i=0; i<steps; i++ ) {\r
1672             BYTE r = r1 - (r1-r2) * i / steps;\r
1673             BYTE g = g1 - (g1-g2) * i / steps;\r
1674             BYTE b = b1 - (b1-b2) * i / steps;\r
1675 \r
1676             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1677             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1678             FillRect( hdc, &rc, hbrush );\r
1679             DeleteObject(hbrush);\r
1680         }\r
1681     }\r
1682     else if( mode == 2 ) {\r
1683         /* Diagonal gradient, good more or less for every piece */\r
1684         POINT triangle[3];\r
1685         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1686         HBRUSH hbrush_old;\r
1687         int steps = squareSize;\r
1688         int i;\r
1689 \r
1690         triangle[0].x = squareSize - steps;\r
1691         triangle[0].y = squareSize;\r
1692         triangle[1].x = squareSize;\r
1693         triangle[1].y = squareSize;\r
1694         triangle[2].x = squareSize;\r
1695         triangle[2].y = squareSize - steps;\r
1696 \r
1697         for( i=0; i<steps; i++ ) {\r
1698             BYTE r = r1 - (r1-r2) * i / steps;\r
1699             BYTE g = g1 - (g1-g2) * i / steps;\r
1700             BYTE b = b1 - (b1-b2) * i / steps;\r
1701 \r
1702             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1703             hbrush_old = SelectObject( hdc, hbrush );\r
1704             Polygon( hdc, triangle, 3 );\r
1705             SelectObject( hdc, hbrush_old );\r
1706             DeleteObject(hbrush);\r
1707             triangle[0].x++;\r
1708             triangle[2].y++;\r
1709         }\r
1710 \r
1711         SelectObject( hdc, hpen );\r
1712     }\r
1713 }\r
1714 \r
1715 /*\r
1716     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1717     seems to work ok. The main problem here is to find the "inside" of a chess\r
1718     piece: follow the steps as explained below.\r
1719 */\r
1720 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1721 {\r
1722     HBITMAP hbm;\r
1723     HBITMAP hbm_old;\r
1724     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1725     RECT rc;\r
1726     SIZE sz;\r
1727     POINT pt;\r
1728     int backColor = whitePieceColor; \r
1729     int foreColor = blackPieceColor;\r
1730     \r
1731     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1732         backColor = appData.fontBackColorWhite;\r
1733         foreColor = appData.fontForeColorWhite;\r
1734     }\r
1735     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1736         backColor = appData.fontBackColorBlack;\r
1737         foreColor = appData.fontForeColorBlack;\r
1738     }\r
1739 \r
1740     /* Mask */\r
1741     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1742 \r
1743     hbm_old = SelectObject( hdc, hbm );\r
1744 \r
1745     rc.left = 0;\r
1746     rc.top = 0;\r
1747     rc.right = squareSize;\r
1748     rc.bottom = squareSize;\r
1749 \r
1750     /* Step 1: background is now black */\r
1751     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1752 \r
1753     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1754 \r
1755     pt.x = (squareSize - sz.cx) / 2;\r
1756     pt.y = (squareSize - sz.cy) / 2;\r
1757 \r
1758     SetBkMode( hdc, TRANSPARENT );\r
1759     SetTextColor( hdc, chroma );\r
1760     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1761     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1762 \r
1763     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1764     /* Step 3: the area outside the piece is filled with white */\r
1765 //    FloodFill( hdc, 0, 0, chroma );\r
1766     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1767     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1768     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1769     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1770     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1771     /* \r
1772         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1773         but if the start point is not inside the piece we're lost!\r
1774         There should be a better way to do this... if we could create a region or path\r
1775         from the fill operation we would be fine for example.\r
1776     */\r
1777 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1778     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1779 \r
1780     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1781         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1782         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1783 \r
1784         SelectObject( dc2, bm2 );\r
1785         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1786         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1787         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1788         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1789         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1790 \r
1791         DeleteDC( dc2 );\r
1792         DeleteObject( bm2 );\r
1793     }\r
1794 \r
1795     SetTextColor( hdc, 0 );\r
1796     /* \r
1797         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1798         draw the piece again in black for safety.\r
1799     */\r
1800     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1801 \r
1802     SelectObject( hdc, hbm_old );\r
1803 \r
1804     if( hPieceMask[index] != NULL ) {\r
1805         DeleteObject( hPieceMask[index] );\r
1806     }\r
1807 \r
1808     hPieceMask[index] = hbm;\r
1809 \r
1810     /* Face */\r
1811     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1812 \r
1813     SelectObject( hdc, hbm );\r
1814 \r
1815     {\r
1816         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1817         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1818         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1819 \r
1820         SelectObject( dc1, hPieceMask[index] );\r
1821         SelectObject( dc2, bm2 );\r
1822         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1823         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1824         \r
1825         /* \r
1826             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1827             the piece background and deletes (makes transparent) the rest.\r
1828             Thanks to that mask, we are free to paint the background with the greates\r
1829             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1830             We use this, to make gradients and give the pieces a "roundish" look.\r
1831         */\r
1832         SetPieceBackground( hdc, backColor, 2 );\r
1833         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1834 \r
1835         DeleteDC( dc2 );\r
1836         DeleteDC( dc1 );\r
1837         DeleteObject( bm2 );\r
1838     }\r
1839 \r
1840     SetTextColor( hdc, foreColor );\r
1841     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1842 \r
1843     SelectObject( hdc, hbm_old );\r
1844 \r
1845     if( hPieceFace[index] != NULL ) {\r
1846         DeleteObject( hPieceFace[index] );\r
1847     }\r
1848 \r
1849     hPieceFace[index] = hbm;\r
1850 }\r
1851 \r
1852 static int TranslatePieceToFontPiece( int piece )\r
1853 {\r
1854     switch( piece ) {\r
1855     case BlackPawn:\r
1856         return PM_BP;\r
1857     case BlackKnight:\r
1858         return PM_BN;\r
1859     case BlackBishop:\r
1860         return PM_BB;\r
1861     case BlackRook:\r
1862         return PM_BR;\r
1863     case BlackQueen:\r
1864         return PM_BQ;\r
1865     case BlackKing:\r
1866         return PM_BK;\r
1867     case WhitePawn:\r
1868         return PM_WP;\r
1869     case WhiteKnight:\r
1870         return PM_WN;\r
1871     case WhiteBishop:\r
1872         return PM_WB;\r
1873     case WhiteRook:\r
1874         return PM_WR;\r
1875     case WhiteQueen:\r
1876         return PM_WQ;\r
1877     case WhiteKing:\r
1878         return PM_WK;\r
1879 \r
1880     case BlackAngel:\r
1881         return PM_BA;\r
1882     case BlackMarshall:\r
1883         return PM_BC;\r
1884     case BlackFerz:\r
1885         return PM_BF;\r
1886     case BlackNightrider:\r
1887         return PM_BH;\r
1888     case BlackAlfil:\r
1889         return PM_BE;\r
1890     case BlackWazir:\r
1891         return PM_BW;\r
1892     case BlackUnicorn:\r
1893         return PM_BU;\r
1894     case BlackCannon:\r
1895         return PM_BO;\r
1896     case BlackGrasshopper:\r
1897         return PM_BG;\r
1898     case BlackMan:\r
1899         return PM_BM;\r
1900     case BlackSilver:\r
1901         return PM_BSG;\r
1902     case BlackLance:\r
1903         return PM_BL;\r
1904     case BlackFalcon:\r
1905         return PM_BV;\r
1906     case BlackCobra:\r
1907         return PM_BS;\r
1908     case BlackCardinal:\r
1909         return PM_BAB;\r
1910     case BlackDragon:\r
1911         return PM_BD;\r
1912 \r
1913     case WhiteAngel:\r
1914         return PM_WA;\r
1915     case WhiteMarshall:\r
1916         return PM_WC;\r
1917     case WhiteFerz:\r
1918         return PM_WF;\r
1919     case WhiteNightrider:\r
1920         return PM_WH;\r
1921     case WhiteAlfil:\r
1922         return PM_WE;\r
1923     case WhiteWazir:\r
1924         return PM_WW;\r
1925     case WhiteUnicorn:\r
1926         return PM_WU;\r
1927     case WhiteCannon:\r
1928         return PM_WO;\r
1929     case WhiteGrasshopper:\r
1930         return PM_WG;\r
1931     case WhiteMan:\r
1932         return PM_WM;\r
1933     case WhiteSilver:\r
1934         return PM_WSG;\r
1935     case WhiteLance:\r
1936         return PM_WL;\r
1937     case WhiteFalcon:\r
1938         return PM_WV;\r
1939     case WhiteCobra:\r
1940         return PM_WS;\r
1941     case WhiteCardinal:\r
1942         return PM_WAB;\r
1943     case WhiteDragon:\r
1944         return PM_WD;\r
1945     }\r
1946 \r
1947     return 0;\r
1948 }\r
1949 \r
1950 void CreatePiecesFromFont()\r
1951 {\r
1952     LOGFONT lf;\r
1953     HDC hdc_window = NULL;\r
1954     HDC hdc = NULL;\r
1955     HFONT hfont_old;\r
1956     int fontHeight;\r
1957     int i;\r
1958 \r
1959     if( fontBitmapSquareSize < 0 ) {\r
1960         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1961         return;\r
1962     }\r
1963 \r
1964     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1965         fontBitmapSquareSize = -1;\r
1966         return;\r
1967     }\r
1968 \r
1969     if( fontBitmapSquareSize != squareSize ) {\r
1970         hdc_window = GetDC( hwndMain );\r
1971         hdc = CreateCompatibleDC( hdc_window );\r
1972 \r
1973         if( hPieceFont != NULL ) {\r
1974             DeleteObject( hPieceFont );\r
1975         }\r
1976         else {\r
1977             for( i=0; i<=(int)BlackKing; i++ ) {\r
1978                 hPieceMask[i] = NULL;\r
1979                 hPieceFace[i] = NULL;\r
1980             }\r
1981         }\r
1982 \r
1983         fontHeight = 75;\r
1984 \r
1985         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
1986             fontHeight = appData.fontPieceSize;\r
1987         }\r
1988 \r
1989         fontHeight = (fontHeight * squareSize) / 100;\r
1990 \r
1991         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
1992         lf.lfWidth = 0;\r
1993         lf.lfEscapement = 0;\r
1994         lf.lfOrientation = 0;\r
1995         lf.lfWeight = FW_NORMAL;\r
1996         lf.lfItalic = 0;\r
1997         lf.lfUnderline = 0;\r
1998         lf.lfStrikeOut = 0;\r
1999         lf.lfCharSet = DEFAULT_CHARSET;\r
2000         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2001         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2002         lf.lfQuality = PROOF_QUALITY;\r
2003         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2004         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2005         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2006 \r
2007         hPieceFont = CreateFontIndirect( &lf );\r
2008 \r
2009         if( hPieceFont == NULL ) {\r
2010             fontBitmapSquareSize = -2;\r
2011         }\r
2012         else {\r
2013             /* Setup font-to-piece character table */\r
2014             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2015                 /* No (or wrong) global settings, try to detect the font */\r
2016                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2017                     /* Alpha */\r
2018                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2019                 }\r
2020                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2021                     /* DiagramTT* family */\r
2022                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2023                 }\r
2024                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2025                     /* Fairy symbols */\r
2026                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2027                 }\r
2028                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2029                     /* Good Companion (Some characters get warped as literal :-( */\r
2030                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2031                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2032                     SetCharTable(pieceToFontChar, s);\r
2033                 }\r
2034                 else {\r
2035                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2036                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2037                 }\r
2038             }\r
2039 \r
2040             /* Create bitmaps */\r
2041             hfont_old = SelectObject( hdc, hPieceFont );\r
2042             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2043                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2044                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2045 \r
2046             SelectObject( hdc, hfont_old );\r
2047 \r
2048             fontBitmapSquareSize = squareSize;\r
2049         }\r
2050     }\r
2051 \r
2052     if( hdc != NULL ) {\r
2053         DeleteDC( hdc );\r
2054     }\r
2055 \r
2056     if( hdc_window != NULL ) {\r
2057         ReleaseDC( hwndMain, hdc_window );\r
2058     }\r
2059 }\r
2060 \r
2061 HBITMAP\r
2062 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2063 {\r
2064   char name[128];\r
2065 \r
2066     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2067   if (gameInfo.event &&\r
2068       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2069       strcmp(name, "k80s") == 0) {\r
2070     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2071   }\r
2072   return LoadBitmap(hinst, name);\r
2073 }\r
2074 \r
2075 \r
2076 /* Insert a color into the program's logical palette\r
2077    structure.  This code assumes the given color is\r
2078    the result of the RGB or PALETTERGB macro, and it\r
2079    knows how those macros work (which is documented).\r
2080 */\r
2081 VOID\r
2082 InsertInPalette(COLORREF color)\r
2083 {\r
2084   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2085 \r
2086   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2087     DisplayFatalError(_("Too many colors"), 0, 1);\r
2088     pLogPal->palNumEntries--;\r
2089     return;\r
2090   }\r
2091 \r
2092   pe->peFlags = (char) 0;\r
2093   pe->peRed = (char) (0xFF & color);\r
2094   pe->peGreen = (char) (0xFF & (color >> 8));\r
2095   pe->peBlue = (char) (0xFF & (color >> 16));\r
2096   return;\r
2097 }\r
2098 \r
2099 \r
2100 VOID\r
2101 InitDrawingColors()\r
2102 {\r
2103   if (pLogPal == NULL) {\r
2104     /* Allocate enough memory for a logical palette with\r
2105      * PALETTESIZE entries and set the size and version fields\r
2106      * of the logical palette structure.\r
2107      */\r
2108     pLogPal = (NPLOGPALETTE)\r
2109       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2110                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2111     pLogPal->palVersion    = 0x300;\r
2112   }\r
2113   pLogPal->palNumEntries = 0;\r
2114 \r
2115   InsertInPalette(lightSquareColor);\r
2116   InsertInPalette(darkSquareColor);\r
2117   InsertInPalette(whitePieceColor);\r
2118   InsertInPalette(blackPieceColor);\r
2119   InsertInPalette(highlightSquareColor);\r
2120   InsertInPalette(premoveHighlightColor);\r
2121 \r
2122   /*  create a logical color palette according the information\r
2123    *  in the LOGPALETTE structure.\r
2124    */\r
2125   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2126 \r
2127   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2128   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2129   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2130   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2131   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2132   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2133   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2134   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2135   /* [AS] Force rendering of the font-based pieces */\r
2136   if( fontBitmapSquareSize > 0 ) {\r
2137     fontBitmapSquareSize = 0;\r
2138   }\r
2139 }\r
2140 \r
2141 \r
2142 int\r
2143 BoardWidth(int boardSize, int n)\r
2144 { /* [HGM] argument n added to allow different width and height */\r
2145   int lineGap = sizeInfo[boardSize].lineGap;\r
2146 \r
2147   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2148       lineGap = appData.overrideLineGap;\r
2149   }\r
2150 \r
2151   return (n + 1) * lineGap +\r
2152           n * sizeInfo[boardSize].squareSize;\r
2153 }\r
2154 \r
2155 /* Respond to board resize by dragging edge */\r
2156 VOID\r
2157 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2158 {\r
2159   BoardSize newSize = NUM_SIZES - 1;\r
2160   static int recurse = 0;\r
2161   if (IsIconic(hwndMain)) return;\r
2162   if (recurse > 0) return;\r
2163   recurse++;\r
2164   while (newSize > 0) {\r
2165         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2166         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2167            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2168     newSize--;\r
2169   } \r
2170   boardSize = newSize;\r
2171   InitDrawingSizes(boardSize, flags);\r
2172   recurse--;\r
2173 }\r
2174 \r
2175 \r
2176 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2177 \r
2178 VOID\r
2179 InitDrawingSizes(BoardSize boardSize, int flags)\r
2180 {\r
2181   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2182   ChessSquare piece;\r
2183   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2184   HDC hdc;\r
2185   SIZE clockSize, messageSize;\r
2186   HFONT oldFont;\r
2187   char buf[MSG_SIZ];\r
2188   char *str;\r
2189   HMENU hmenu = GetMenu(hwndMain);\r
2190   RECT crect, wrect, oldRect;\r
2191   int offby;\r
2192   LOGBRUSH logbrush;\r
2193 \r
2194   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2195   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2196 \r
2197   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2198   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2199 \r
2200   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2201   oldRect.top = wpMain.y;\r
2202   oldRect.right = wpMain.x + wpMain.width;\r
2203   oldRect.bottom = wpMain.y + wpMain.height;\r
2204 \r
2205   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2206   smallLayout = sizeInfo[boardSize].smallLayout;\r
2207   squareSize = sizeInfo[boardSize].squareSize;\r
2208   lineGap = sizeInfo[boardSize].lineGap;\r
2209   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2210 \r
2211   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2212       lineGap = appData.overrideLineGap;\r
2213   }\r
2214 \r
2215   if (tinyLayout != oldTinyLayout) {\r
2216     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
2217     if (tinyLayout) {\r
2218       style &= ~WS_SYSMENU;\r
2219       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2220                  "&Minimize\tCtrl+F4");\r
2221     } else {\r
2222       style |= WS_SYSMENU;\r
2223       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2224     }\r
2225     SetWindowLong(hwndMain, GWL_STYLE, style);\r
2226 \r
2227     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2228       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2229         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2230     }\r
2231     DrawMenuBar(hwndMain);\r
2232   }\r
2233 \r
2234   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2235   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2236 \r
2237   /* Get text area sizes */\r
2238   hdc = GetDC(hwndMain);\r
2239   if (appData.clockMode) {\r
2240     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2241   } else {\r
2242     snprintf(buf, MSG_SIZ, _("White"));\r
2243   }\r
2244   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2245   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2246   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2247   str = _("We only care about the height here");\r
2248   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2249   SelectObject(hdc, oldFont);\r
2250   ReleaseDC(hwndMain, hdc);\r
2251 \r
2252   /* Compute where everything goes */\r
2253   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2254         /* [HGM] logo: if either logo is on, reserve space for it */\r
2255         logoHeight =  2*clockSize.cy;\r
2256         leftLogoRect.left   = OUTER_MARGIN;\r
2257         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2258         leftLogoRect.top    = OUTER_MARGIN;\r
2259         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2260 \r
2261         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2262         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2263         rightLogoRect.top    = OUTER_MARGIN;\r
2264         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2265 \r
2266 \r
2267     whiteRect.left = leftLogoRect.right;\r
2268     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2269     whiteRect.top = OUTER_MARGIN;\r
2270     whiteRect.bottom = whiteRect.top + logoHeight;\r
2271 \r
2272     blackRect.right = rightLogoRect.left;\r
2273     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2274     blackRect.top = whiteRect.top;\r
2275     blackRect.bottom = whiteRect.bottom;\r
2276   } else {\r
2277     whiteRect.left = OUTER_MARGIN;\r
2278     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2279     whiteRect.top = OUTER_MARGIN;\r
2280     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2281 \r
2282     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2283     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2284     blackRect.top = whiteRect.top;\r
2285     blackRect.bottom = whiteRect.bottom;\r
2286 \r
2287     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2288   }\r
2289 \r
2290   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2291   if (appData.showButtonBar) {\r
2292     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2293       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2294   } else {\r
2295     messageRect.right = OUTER_MARGIN + boardWidth;\r
2296   }\r
2297   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2298   messageRect.bottom = messageRect.top + messageSize.cy;\r
2299 \r
2300   boardRect.left = OUTER_MARGIN;\r
2301   boardRect.right = boardRect.left + boardWidth;\r
2302   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2303   boardRect.bottom = boardRect.top + boardHeight;\r
2304 \r
2305   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2306   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2307   oldBoardSize = boardSize;\r
2308   oldTinyLayout = tinyLayout;\r
2309   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2310   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2311     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2312   winW *= 1 + twoBoards;\r
2313   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2314   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2315   wpMain.height = winH; //       without disturbing window attachments\r
2316   GetWindowRect(hwndMain, &wrect);\r
2317   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2318                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2319 \r
2320   // [HGM] placement: let attached windows follow size change.\r
2321   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2322   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2323   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2324   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2325   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2326 \r
2327   /* compensate if menu bar wrapped */\r
2328   GetClientRect(hwndMain, &crect);\r
2329   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2330   wpMain.height += offby;\r
2331   switch (flags) {\r
2332   case WMSZ_TOPLEFT:\r
2333     SetWindowPos(hwndMain, NULL, \r
2334                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2335                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2336     break;\r
2337 \r
2338   case WMSZ_TOPRIGHT:\r
2339   case WMSZ_TOP:\r
2340     SetWindowPos(hwndMain, NULL, \r
2341                  wrect.left, wrect.bottom - wpMain.height, \r
2342                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2343     break;\r
2344 \r
2345   case WMSZ_BOTTOMLEFT:\r
2346   case WMSZ_LEFT:\r
2347     SetWindowPos(hwndMain, NULL, \r
2348                  wrect.right - wpMain.width, wrect.top, \r
2349                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2350     break;\r
2351 \r
2352   case WMSZ_BOTTOMRIGHT:\r
2353   case WMSZ_BOTTOM:\r
2354   case WMSZ_RIGHT:\r
2355   default:\r
2356     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2357                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2358     break;\r
2359   }\r
2360 \r
2361   hwndPause = NULL;\r
2362   for (i = 0; i < N_BUTTONS; i++) {\r
2363     if (buttonDesc[i].hwnd != NULL) {\r
2364       DestroyWindow(buttonDesc[i].hwnd);\r
2365       buttonDesc[i].hwnd = NULL;\r
2366     }\r
2367     if (appData.showButtonBar) {\r
2368       buttonDesc[i].hwnd =\r
2369         CreateWindow("BUTTON", buttonDesc[i].label,\r
2370                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2371                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2372                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2373                      (HMENU) buttonDesc[i].id,\r
2374                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
2375       if (tinyLayout) {\r
2376         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2377                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2378                     MAKELPARAM(FALSE, 0));\r
2379       }\r
2380       if (buttonDesc[i].id == IDM_Pause)\r
2381         hwndPause = buttonDesc[i].hwnd;\r
2382       buttonDesc[i].wndproc = (WNDPROC)\r
2383         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
2384     }\r
2385   }\r
2386   if (gridPen != NULL) DeleteObject(gridPen);\r
2387   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2388   if (premovePen != NULL) DeleteObject(premovePen);\r
2389   if (lineGap != 0) {\r
2390     logbrush.lbStyle = BS_SOLID;\r
2391     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2392     gridPen =\r
2393       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2394                    lineGap, &logbrush, 0, NULL);\r
2395     logbrush.lbColor = highlightSquareColor;\r
2396     highlightPen =\r
2397       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2398                    lineGap, &logbrush, 0, NULL);\r
2399 \r
2400     logbrush.lbColor = premoveHighlightColor; \r
2401     premovePen =\r
2402       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2403                    lineGap, &logbrush, 0, NULL);\r
2404 \r
2405     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2406     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2407       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2408       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2409         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2410       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2411         BOARD_WIDTH * (squareSize + lineGap);\r
2412       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2413     }\r
2414     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2415       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2416       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2417         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2418         lineGap / 2 + (i * (squareSize + lineGap));\r
2419       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2420         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2421       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2422     }\r
2423   }\r
2424 \r
2425   /* [HGM] Licensing requirement */\r
2426 #ifdef GOTHIC\r
2427   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2428 #endif\r
2429 #ifdef FALCON\r
2430   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2431 #endif\r
2432   GothicPopUp( "", VariantNormal);\r
2433 \r
2434 \r
2435 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2436 \r
2437   /* Load piece bitmaps for this board size */\r
2438   for (i=0; i<=2; i++) {\r
2439     for (piece = WhitePawn;\r
2440          (int) piece < (int) BlackPawn;\r
2441          piece = (ChessSquare) ((int) piece + 1)) {\r
2442       if (pieceBitmap[i][piece] != NULL)\r
2443         DeleteObject(pieceBitmap[i][piece]);\r
2444     }\r
2445   }\r
2446 \r
2447   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2448   // Orthodox Chess pieces\r
2449   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2450   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2451   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2452   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2453   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2454   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2455   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2456   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2457   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2458   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2459   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2460   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2461   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2462   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2463   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2464   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2465     // in Shogi, Hijack the unused Queen for Lance\r
2466     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2467     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2468     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2469   } else {\r
2470     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2471     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2472     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2473   }\r
2474 \r
2475   if(squareSize <= 72 && squareSize >= 33) { \r
2476     /* A & C are available in most sizes now */\r
2477     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2478       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2479       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2480       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2481       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2482       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2483       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2484       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2485       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2486       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2487       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2488       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2489       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2490     } else { // Smirf-like\r
2491       if(gameInfo.variant == VariantSChess) {\r
2492         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2493         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2494         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2495       } else {\r
2496         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2497         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2498         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2499       }\r
2500     }\r
2501     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2502       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2503       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2504       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2505     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2506       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2507       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2508       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2509     } else { // WinBoard standard\r
2510       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2511       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2512       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2513     }\r
2514   }\r
2515 \r
2516 \r
2517   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2518     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2519     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2520     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2521     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2522     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2523     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2524     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2525     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2526     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2527     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2528     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2529     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2530     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2531     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2532     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2533     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2534     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2535     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2536     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2537     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2538     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2539     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2540     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2541     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2542     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2543     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2544     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2545     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2546     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2547     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2548 \r
2549     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2550       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2551       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2552       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2553       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2554       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2555       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2556       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2557       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2558       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2559       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2560       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2561       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2562     } else {\r
2563       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2564       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2565       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2566       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2567       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2568       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2569       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2570       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2571       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2572       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2573       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2574       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2575     }\r
2576 \r
2577   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2578     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2579     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2580     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2581     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2582     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2583     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2584     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2585     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2586     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2587     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2588     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2589     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2590     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2591     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2592   }\r
2593 \r
2594 \r
2595   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2596   /* special Shogi support in this size */\r
2597   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2598       for (piece = WhitePawn;\r
2599            (int) piece < (int) BlackPawn;\r
2600            piece = (ChessSquare) ((int) piece + 1)) {\r
2601         if (pieceBitmap[i][piece] != NULL)\r
2602           DeleteObject(pieceBitmap[i][piece]);\r
2603       }\r
2604     }\r
2605   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2606   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2607   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2608   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2609   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2610   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2611   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2612   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2613   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2614   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2615   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2616   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2617   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2618   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2619   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2620   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2621   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2622   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2623   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2624   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2625   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2626   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2627   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2628   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2629   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2630   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2631   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2632   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2633   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2634   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2635   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2636   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2637   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2638   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2639   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2640   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2641   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2642   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2643   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2644   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2645   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2646   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2647   minorSize = 0;\r
2648   }\r
2649 }\r
2650 \r
2651 HBITMAP\r
2652 PieceBitmap(ChessSquare p, int kind)\r
2653 {\r
2654   if ((int) p >= (int) BlackPawn)\r
2655     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2656 \r
2657   return pieceBitmap[kind][(int) p];\r
2658 }\r
2659 \r
2660 /***************************************************************/\r
2661 \r
2662 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2663 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2664 /*\r
2665 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2666 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2667 */\r
2668 \r
2669 VOID\r
2670 SquareToPos(int row, int column, int * x, int * y)\r
2671 {\r
2672   if (flipView) {\r
2673     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2674     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2675   } else {\r
2676     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2677     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2678   }\r
2679 }\r
2680 \r
2681 VOID\r
2682 DrawCoordsOnDC(HDC hdc)\r
2683 {\r
2684   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
2685   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
2686   char str[2] = { NULLCHAR, NULLCHAR };\r
2687   int oldMode, oldAlign, x, y, start, i;\r
2688   HFONT oldFont;\r
2689   HBRUSH oldBrush;\r
2690 \r
2691   if (!appData.showCoords)\r
2692     return;\r
2693 \r
2694   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2695 \r
2696   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2697   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2698   oldAlign = GetTextAlign(hdc);\r
2699   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2700 \r
2701   y = boardRect.top + lineGap;\r
2702   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2703 \r
2704   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2705   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2706     str[0] = files[start + i];\r
2707     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2708     y += squareSize + lineGap;\r
2709   }\r
2710 \r
2711   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2712 \r
2713   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2714   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2715     str[0] = ranks[start + i];\r
2716     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2717     x += squareSize + lineGap;\r
2718   }    \r
2719 \r
2720   SelectObject(hdc, oldBrush);\r
2721   SetBkMode(hdc, oldMode);\r
2722   SetTextAlign(hdc, oldAlign);\r
2723   SelectObject(hdc, oldFont);\r
2724 }\r
2725 \r
2726 VOID\r
2727 DrawGridOnDC(HDC hdc)\r
2728 {\r
2729   HPEN oldPen;\r
2730  \r
2731   if (lineGap != 0) {\r
2732     oldPen = SelectObject(hdc, gridPen);\r
2733     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2734     SelectObject(hdc, oldPen);\r
2735   }\r
2736 }\r
2737 \r
2738 #define HIGHLIGHT_PEN 0\r
2739 #define PREMOVE_PEN   1\r
2740 \r
2741 VOID\r
2742 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2743 {\r
2744   int x1, y1;\r
2745   HPEN oldPen, hPen;\r
2746   if (lineGap == 0) return;\r
2747   if (flipView) {\r
2748     x1 = boardRect.left +\r
2749       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2750     y1 = boardRect.top +\r
2751       lineGap/2 + y * (squareSize + lineGap);\r
2752   } else {\r
2753     x1 = boardRect.left +\r
2754       lineGap/2 + x * (squareSize + lineGap);\r
2755     y1 = boardRect.top +\r
2756       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2757   }\r
2758   hPen = pen ? premovePen : highlightPen;\r
2759   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2760   MoveToEx(hdc, x1, y1, NULL);\r
2761   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2762   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2763   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2764   LineTo(hdc, x1, y1);\r
2765   SelectObject(hdc, oldPen);\r
2766 }\r
2767 \r
2768 VOID\r
2769 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2770 {\r
2771   int i;\r
2772   for (i=0; i<2; i++) {\r
2773     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2774       DrawHighlightOnDC(hdc, TRUE,\r
2775                         h->sq[i].x, h->sq[i].y,\r
2776                         pen);\r
2777   }\r
2778 }\r
2779 \r
2780 /* Note: sqcolor is used only in monoMode */\r
2781 /* Note that this code is largely duplicated in woptions.c,\r
2782    function DrawSampleSquare, so that needs to be updated too */\r
2783 VOID\r
2784 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2785 {\r
2786   HBITMAP oldBitmap;\r
2787   HBRUSH oldBrush;\r
2788   int tmpSize;\r
2789 \r
2790   if (appData.blindfold) return;\r
2791 \r
2792   /* [AS] Use font-based pieces if needed */\r
2793   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2794     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2795     CreatePiecesFromFont();\r
2796 \r
2797     if( fontBitmapSquareSize == squareSize ) {\r
2798         int index = TranslatePieceToFontPiece(piece);\r
2799 \r
2800         SelectObject( tmphdc, hPieceMask[ index ] );\r
2801 \r
2802       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2803         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2804       else\r
2805         BitBlt( hdc,\r
2806             x, y,\r
2807             squareSize, squareSize,\r
2808             tmphdc,\r
2809             0, 0,\r
2810             SRCAND );\r
2811 \r
2812         SelectObject( tmphdc, hPieceFace[ index ] );\r
2813 \r
2814       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2815         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2816       else\r
2817         BitBlt( hdc,\r
2818             x, y,\r
2819             squareSize, squareSize,\r
2820             tmphdc,\r
2821             0, 0,\r
2822             SRCPAINT );\r
2823 \r
2824         return;\r
2825     }\r
2826   }\r
2827 \r
2828   if (appData.monoMode) {\r
2829     SelectObject(tmphdc, PieceBitmap(piece, \r
2830       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2831     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2832            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2833   } else {\r
2834     tmpSize = squareSize;\r
2835     if(minorSize &&\r
2836         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2837          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2838       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2839       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2840       x += (squareSize - minorSize)>>1;\r
2841       y += squareSize - minorSize - 2;\r
2842       tmpSize = minorSize;\r
2843     }\r
2844     if (color || appData.allWhite ) {\r
2845       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2846       if( color )\r
2847               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2848       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2849       if(appData.upsideDown && color==flipView)\r
2850         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2851       else\r
2852         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2853       /* Use black for outline of white pieces */\r
2854       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2855       if(appData.upsideDown && color==flipView)\r
2856         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2857       else\r
2858         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2859     } else {\r
2860       /* Use square color for details of black pieces */\r
2861       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2862       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2863       if(appData.upsideDown && !flipView)\r
2864         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2865       else\r
2866         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2867     }\r
2868     SelectObject(hdc, oldBrush);\r
2869     SelectObject(tmphdc, oldBitmap);\r
2870   }\r
2871 }\r
2872 \r
2873 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2874 int GetBackTextureMode( int algo )\r
2875 {\r
2876     int result = BACK_TEXTURE_MODE_DISABLED;\r
2877 \r
2878     switch( algo ) \r
2879     {\r
2880         case BACK_TEXTURE_MODE_PLAIN:\r
2881             result = 1; /* Always use identity map */\r
2882             break;\r
2883         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2884             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2885             break;\r
2886     }\r
2887 \r
2888     return result;\r
2889 }\r
2890 \r
2891 /* \r
2892     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2893     to handle redraws cleanly (as random numbers would always be different).\r
2894 */\r
2895 VOID RebuildTextureSquareInfo()\r
2896 {\r
2897     BITMAP bi;\r
2898     int lite_w = 0;\r
2899     int lite_h = 0;\r
2900     int dark_w = 0;\r
2901     int dark_h = 0;\r
2902     int row;\r
2903     int col;\r
2904 \r
2905     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2906 \r
2907     if( liteBackTexture != NULL ) {\r
2908         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2909             lite_w = bi.bmWidth;\r
2910             lite_h = bi.bmHeight;\r
2911         }\r
2912     }\r
2913 \r
2914     if( darkBackTexture != NULL ) {\r
2915         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2916             dark_w = bi.bmWidth;\r
2917             dark_h = bi.bmHeight;\r
2918         }\r
2919     }\r
2920 \r
2921     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2922         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2923             if( (col + row) & 1 ) {\r
2924                 /* Lite square */\r
2925                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2926                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2927                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2928                   else\r
2929                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2930                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2931                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2932                   else\r
2933                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2934                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2935                 }\r
2936             }\r
2937             else {\r
2938                 /* Dark square */\r
2939                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2940                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2941                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2942                   else\r
2943                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2944                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2945                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2946                   else\r
2947                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2948                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2949                 }\r
2950             }\r
2951         }\r
2952     }\r
2953 }\r
2954 \r
2955 /* [AS] Arrow highlighting support */\r
2956 \r
2957 static double A_WIDTH = 5; /* Width of arrow body */\r
2958 \r
2959 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2960 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2961 \r
2962 static double Sqr( double x )\r
2963 {\r
2964     return x*x;\r
2965 }\r
2966 \r
2967 static int Round( double x )\r
2968 {\r
2969     return (int) (x + 0.5);\r
2970 }\r
2971 \r
2972 /* Draw an arrow between two points using current settings */\r
2973 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2974 {\r
2975     POINT arrow[7];\r
2976     double dx, dy, j, k, x, y;\r
2977 \r
2978     if( d_x == s_x ) {\r
2979         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2980 \r
2981         arrow[0].x = s_x + A_WIDTH + 0.5;\r
2982         arrow[0].y = s_y;\r
2983 \r
2984         arrow[1].x = s_x + A_WIDTH + 0.5;\r
2985         arrow[1].y = d_y - h;\r
2986 \r
2987         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
2988         arrow[2].y = d_y - h;\r
2989 \r
2990         arrow[3].x = d_x;\r
2991         arrow[3].y = d_y;\r
2992 \r
2993         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
2994         arrow[5].y = d_y - h;\r
2995 \r
2996         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
2997         arrow[4].y = d_y - h;\r
2998 \r
2999         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3000         arrow[6].y = s_y;\r
3001     }\r
3002     else if( d_y == s_y ) {\r
3003         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3004 \r
3005         arrow[0].x = s_x;\r
3006         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3007 \r
3008         arrow[1].x = d_x - w;\r
3009         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3010 \r
3011         arrow[2].x = d_x - w;\r
3012         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3013 \r
3014         arrow[3].x = d_x;\r
3015         arrow[3].y = d_y;\r
3016 \r
3017         arrow[5].x = d_x - w;\r
3018         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3019 \r
3020         arrow[4].x = d_x - w;\r
3021         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3022 \r
3023         arrow[6].x = s_x;\r
3024         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3025     }\r
3026     else {\r
3027         /* [AS] Needed a lot of paper for this! :-) */\r
3028         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3029         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3030   \r
3031         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3032 \r
3033         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3034 \r
3035         x = s_x;\r
3036         y = s_y;\r
3037 \r
3038         arrow[0].x = Round(x - j);\r
3039         arrow[0].y = Round(y + j*dx);\r
3040 \r
3041         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3042         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3043 \r
3044         if( d_x > s_x ) {\r
3045             x = (double) d_x - k;\r
3046             y = (double) d_y - k*dy;\r
3047         }\r
3048         else {\r
3049             x = (double) d_x + k;\r
3050             y = (double) d_y + k*dy;\r
3051         }\r
3052 \r
3053         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3054 \r
3055         arrow[6].x = Round(x - j);\r
3056         arrow[6].y = Round(y + j*dx);\r
3057 \r
3058         arrow[2].x = Round(arrow[6].x + 2*j);\r
3059         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3060 \r
3061         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3062         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3063 \r
3064         arrow[4].x = d_x;\r
3065         arrow[4].y = d_y;\r
3066 \r
3067         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3068         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3069     }\r
3070 \r
3071     Polygon( hdc, arrow, 7 );\r
3072 }\r
3073 \r
3074 /* [AS] Draw an arrow between two squares */\r
3075 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3076 {\r
3077     int s_x, s_y, d_x, d_y;\r
3078     HPEN hpen;\r
3079     HPEN holdpen;\r
3080     HBRUSH hbrush;\r
3081     HBRUSH holdbrush;\r
3082     LOGBRUSH stLB;\r
3083 \r
3084     if( s_col == d_col && s_row == d_row ) {\r
3085         return;\r
3086     }\r
3087 \r
3088     /* Get source and destination points */\r
3089     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3090     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3091 \r
3092     if( d_y > s_y ) {\r
3093         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3094     }\r
3095     else if( d_y < s_y ) {\r
3096         d_y += squareSize / 2 + squareSize / 4;\r
3097     }\r
3098     else {\r
3099         d_y += squareSize / 2;\r
3100     }\r
3101 \r
3102     if( d_x > s_x ) {\r
3103         d_x += squareSize / 2 - squareSize / 4;\r
3104     }\r
3105     else if( d_x < s_x ) {\r
3106         d_x += squareSize / 2 + squareSize / 4;\r
3107     }\r
3108     else {\r
3109         d_x += squareSize / 2;\r
3110     }\r
3111 \r
3112     s_x += squareSize / 2;\r
3113     s_y += squareSize / 2;\r
3114 \r
3115     /* Adjust width */\r
3116     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3117 \r
3118     /* Draw */\r
3119     stLB.lbStyle = BS_SOLID;\r
3120     stLB.lbColor = appData.highlightArrowColor;\r
3121     stLB.lbHatch = 0;\r
3122 \r
3123     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3124     holdpen = SelectObject( hdc, hpen );\r
3125     hbrush = CreateBrushIndirect( &stLB );\r
3126     holdbrush = SelectObject( hdc, hbrush );\r
3127 \r
3128     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3129 \r
3130     SelectObject( hdc, holdpen );\r
3131     SelectObject( hdc, holdbrush );\r
3132     DeleteObject( hpen );\r
3133     DeleteObject( hbrush );\r
3134 }\r
3135 \r
3136 BOOL HasHighlightInfo()\r
3137 {\r
3138     BOOL result = FALSE;\r
3139 \r
3140     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3141         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3142     {\r
3143         result = TRUE;\r
3144     }\r
3145 \r
3146     return result;\r
3147 }\r
3148 \r
3149 BOOL IsDrawArrowEnabled()\r
3150 {\r
3151     BOOL result = FALSE;\r
3152 \r
3153     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3154         result = TRUE;\r
3155     }\r
3156 \r
3157     return result;\r
3158 }\r
3159 \r
3160 VOID DrawArrowHighlight( HDC hdc )\r
3161 {\r
3162     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3163         DrawArrowBetweenSquares( hdc,\r
3164             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3165             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3166     }\r
3167 }\r
3168 \r
3169 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3170 {\r
3171     HRGN result = NULL;\r
3172 \r
3173     if( HasHighlightInfo() ) {\r
3174         int x1, y1, x2, y2;\r
3175         int sx, sy, dx, dy;\r
3176 \r
3177         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3178         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3179 \r
3180         sx = MIN( x1, x2 );\r
3181         sy = MIN( y1, y2 );\r
3182         dx = MAX( x1, x2 ) + squareSize;\r
3183         dy = MAX( y1, y2 ) + squareSize;\r
3184 \r
3185         result = CreateRectRgn( sx, sy, dx, dy );\r
3186     }\r
3187 \r
3188     return result;\r
3189 }\r
3190 \r
3191 /*\r
3192     Warning: this function modifies the behavior of several other functions. \r
3193     \r
3194     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3195     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3196     repaint is scattered all over the place, which is not good for features such as\r
3197     "arrow highlighting" that require a full repaint of the board.\r
3198 \r
3199     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3200     user interaction, when speed is not so important) but especially to avoid errors\r
3201     in the displayed graphics.\r
3202 \r
3203     In such patched places, I always try refer to this function so there is a single\r
3204     place to maintain knowledge.\r
3205     \r
3206     To restore the original behavior, just return FALSE unconditionally.\r
3207 */\r
3208 BOOL IsFullRepaintPreferrable()\r
3209 {\r
3210     BOOL result = FALSE;\r
3211 \r
3212     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3213         /* Arrow may appear on the board */\r
3214         result = TRUE;\r
3215     }\r
3216 \r
3217     return result;\r
3218 }\r
3219 \r
3220 /* \r
3221     This function is called by DrawPosition to know whether a full repaint must\r
3222     be forced or not.\r
3223 \r
3224     Only DrawPosition may directly call this function, which makes use of \r
3225     some state information. Other function should call DrawPosition specifying \r
3226     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3227 */\r
3228 BOOL DrawPositionNeedsFullRepaint()\r
3229 {\r
3230     BOOL result = FALSE;\r
3231 \r
3232     /* \r
3233         Probably a slightly better policy would be to trigger a full repaint\r
3234         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3235         but animation is fast enough that it's difficult to notice.\r
3236     */\r
3237     if( animInfo.piece == EmptySquare ) {\r
3238         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3239             result = TRUE;\r
3240         }\r
3241     }\r
3242 \r
3243     return result;\r
3244 }\r
3245 \r
3246 VOID\r
3247 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3248 {\r
3249   int row, column, x, y, square_color, piece_color;\r
3250   ChessSquare piece;\r
3251   HBRUSH oldBrush;\r
3252   HDC texture_hdc = NULL;\r
3253 \r
3254   /* [AS] Initialize background textures if needed */\r
3255   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3256       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3257       if( backTextureSquareSize != squareSize \r
3258        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3259           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3260           backTextureSquareSize = squareSize;\r
3261           RebuildTextureSquareInfo();\r
3262       }\r
3263 \r
3264       texture_hdc = CreateCompatibleDC( hdc );\r
3265   }\r
3266 \r
3267   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3268     for (column = 0; column < BOARD_WIDTH; column++) {\r
3269   \r
3270       SquareToPos(row, column, &x, &y);\r
3271 \r
3272       piece = board[row][column];\r
3273 \r
3274       square_color = ((column + row) % 2) == 1;\r
3275       if( gameInfo.variant == VariantXiangqi ) {\r
3276           square_color = !InPalace(row, column);\r
3277           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3278           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3279       }\r
3280       piece_color = (int) piece < (int) BlackPawn;\r
3281 \r
3282 \r
3283       /* [HGM] holdings file: light square or black */\r
3284       if(column == BOARD_LEFT-2) {\r
3285             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3286                 square_color = 1;\r
3287             else {\r
3288                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3289                 continue;\r
3290             }\r
3291       } else\r
3292       if(column == BOARD_RGHT + 1 ) {\r
3293             if( row < gameInfo.holdingsSize )\r
3294                 square_color = 1;\r
3295             else {\r
3296                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3297                 continue;\r
3298             }\r
3299       }\r
3300       if(column == BOARD_LEFT-1 ) /* left align */\r
3301             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3302       else if( column == BOARD_RGHT) /* right align */\r
3303             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3304       else\r
3305       if (appData.monoMode) {\r
3306         if (piece == EmptySquare) {\r
3307           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3308                  square_color ? WHITENESS : BLACKNESS);\r
3309         } else {\r
3310           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3311         }\r
3312       } \r
3313       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3314           /* [AS] Draw the square using a texture bitmap */\r
3315           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3316           int r = row, c = column; // [HGM] do not flip board in flipView\r
3317           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3318 \r
3319           DrawTile( x, y, \r
3320               squareSize, squareSize, \r
3321               hdc, \r
3322               texture_hdc,\r
3323               backTextureSquareInfo[r][c].mode,\r
3324               backTextureSquareInfo[r][c].x,\r
3325               backTextureSquareInfo[r][c].y );\r
3326 \r
3327           SelectObject( texture_hdc, hbm );\r
3328 \r
3329           if (piece != EmptySquare) {\r
3330               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3331           }\r
3332       }\r
3333       else {\r
3334         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3335 \r
3336         oldBrush = SelectObject(hdc, brush );\r
3337         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3338         SelectObject(hdc, oldBrush);\r
3339         if (piece != EmptySquare)\r
3340           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3341       }\r
3342     }\r
3343   }\r
3344 \r
3345   if( texture_hdc != NULL ) {\r
3346     DeleteDC( texture_hdc );\r
3347   }\r
3348 }\r
3349 \r
3350 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3351 void fputDW(FILE *f, int x)\r
3352 {\r
3353         fputc(x     & 255, f);\r
3354         fputc(x>>8  & 255, f);\r
3355         fputc(x>>16 & 255, f);\r
3356         fputc(x>>24 & 255, f);\r
3357 }\r
3358 \r
3359 #define MAX_CLIPS 200   /* more than enough */\r
3360 \r
3361 VOID\r
3362 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3363 {\r
3364 //  HBITMAP bufferBitmap;\r
3365   BITMAP bi;\r
3366 //  RECT Rect;\r
3367   HDC tmphdc;\r
3368   HBITMAP hbm;\r
3369   int w = 100, h = 50;\r
3370 \r
3371   if(logo == NULL) return;\r
3372 //  GetClientRect(hwndMain, &Rect);\r
3373 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3374 //                                      Rect.bottom-Rect.top+1);\r
3375   tmphdc = CreateCompatibleDC(hdc);\r
3376   hbm = SelectObject(tmphdc, logo);\r
3377   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3378             w = bi.bmWidth;\r
3379             h = bi.bmHeight;\r
3380   }\r
3381   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3382                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3383   SelectObject(tmphdc, hbm);\r
3384   DeleteDC(tmphdc);\r
3385 }\r
3386 \r
3387 static HDC hdcSeek;\r
3388 \r
3389 // [HGM] seekgraph\r
3390 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3391 {\r
3392     POINT stPt;\r
3393     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3394     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3395     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3396     SelectObject( hdcSeek, hp );\r
3397 }\r
3398 \r
3399 // front-end wrapper for drawing functions to do rectangles\r
3400 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3401 {\r
3402     HPEN hp;\r
3403     RECT rc;\r
3404 \r
3405     if (hdcSeek == NULL) {\r
3406     hdcSeek = GetDC(hwndMain);\r
3407       if (!appData.monoMode) {\r
3408         SelectPalette(hdcSeek, hPal, FALSE);\r
3409         RealizePalette(hdcSeek);\r
3410       }\r
3411     }\r
3412     hp = SelectObject( hdcSeek, gridPen );\r
3413     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3414     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3415     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3416     SelectObject( hdcSeek, hp );\r
3417 }\r
3418 \r
3419 // front-end wrapper for putting text in graph\r
3420 void DrawSeekText(char *buf, int x, int y)\r
3421 {\r
3422         SIZE stSize;\r
3423         SetBkMode( hdcSeek, TRANSPARENT );\r
3424         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3425         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3426 }\r
3427 \r
3428 void DrawSeekDot(int x, int y, int color)\r
3429 {\r
3430         int square = color & 0x80;\r
3431         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3432                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3433         color &= 0x7F;\r
3434         if(square)\r
3435             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3436                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3437         else\r
3438             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3439                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3440             SelectObject(hdcSeek, oldBrush);\r
3441 }\r
3442 \r
3443 VOID\r
3444 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3445 {\r
3446   static Board lastReq[2], lastDrawn[2];\r
3447   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3448   static int lastDrawnFlipView = 0;\r
3449   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3450   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3451   HDC tmphdc;\r
3452   HDC hdcmem;\r
3453   HBITMAP bufferBitmap;\r
3454   HBITMAP oldBitmap;\r
3455   RECT Rect;\r
3456   HRGN clips[MAX_CLIPS];\r
3457   ChessSquare dragged_piece = EmptySquare;\r
3458   int nr = twoBoards*partnerUp;\r
3459 \r
3460   /* I'm undecided on this - this function figures out whether a full\r
3461    * repaint is necessary on its own, so there's no real reason to have the\r
3462    * caller tell it that.  I think this can safely be set to FALSE - but\r
3463    * if we trust the callers not to request full repaints unnessesarily, then\r
3464    * we could skip some clipping work.  In other words, only request a full\r
3465    * redraw when the majority of pieces have changed positions (ie. flip, \r
3466    * gamestart and similar)  --Hawk\r
3467    */\r
3468   Boolean fullrepaint = repaint;\r
3469 \r
3470   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3471 \r
3472   if( DrawPositionNeedsFullRepaint() ) {\r
3473       fullrepaint = TRUE;\r
3474   }\r
3475 \r
3476   if (board == NULL) {\r
3477     if (!lastReqValid[nr]) {\r
3478       return;\r
3479     }\r
3480     board = lastReq[nr];\r
3481   } else {\r
3482     CopyBoard(lastReq[nr], board);\r
3483     lastReqValid[nr] = 1;\r
3484   }\r
3485 \r
3486   if (doingSizing) {\r
3487     return;\r
3488   }\r
3489 \r
3490   if (IsIconic(hwndMain)) {\r
3491     return;\r
3492   }\r
3493 \r
3494   if (hdc == NULL) {\r
3495     hdc = GetDC(hwndMain);\r
3496     if (!appData.monoMode) {\r
3497       SelectPalette(hdc, hPal, FALSE);\r
3498       RealizePalette(hdc);\r
3499     }\r
3500     releaseDC = TRUE;\r
3501   } else {\r
3502     releaseDC = FALSE;\r
3503   }\r
3504 \r
3505   /* Create some work-DCs */\r
3506   hdcmem = CreateCompatibleDC(hdc);\r
3507   tmphdc = CreateCompatibleDC(hdc);\r
3508 \r
3509   /* If dragging is in progress, we temporarely remove the piece */\r
3510   /* [HGM] or temporarily decrease count if stacked              */\r
3511   /*       !! Moved to before board compare !!                   */\r
3512   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3513     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3514     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3515             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3516         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3517     } else \r
3518     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3519             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3520         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3521     } else \r
3522         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3523   }\r
3524 \r
3525   /* Figure out which squares need updating by comparing the \r
3526    * newest board with the last drawn board and checking if\r
3527    * flipping has changed.\r
3528    */\r
3529   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3530     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3531       for (column = 0; column < BOARD_WIDTH; column++) {\r
3532         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3533           SquareToPos(row, column, &x, &y);\r
3534           clips[num_clips++] =\r
3535             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3536         }\r
3537       }\r
3538     }\r
3539    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3540     for (i=0; i<2; i++) {\r
3541       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3542           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3543         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3544             lastDrawnHighlight.sq[i].y >= 0) {\r
3545           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3546                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3547           clips[num_clips++] =\r
3548             CreateRectRgn(x - lineGap, y - lineGap, \r
3549                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3550         }\r
3551         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3552           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3553           clips[num_clips++] =\r
3554             CreateRectRgn(x - lineGap, y - lineGap, \r
3555                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3556         }\r
3557       }\r
3558     }\r
3559     for (i=0; i<2; i++) {\r
3560       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3561           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3562         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3563             lastDrawnPremove.sq[i].y >= 0) {\r
3564           SquareToPos(lastDrawnPremove.sq[i].y,\r
3565                       lastDrawnPremove.sq[i].x, &x, &y);\r
3566           clips[num_clips++] =\r
3567             CreateRectRgn(x - lineGap, y - lineGap, \r
3568                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3569         }\r
3570         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3571             premoveHighlightInfo.sq[i].y >= 0) {\r
3572           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3573                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3574           clips[num_clips++] =\r
3575             CreateRectRgn(x - lineGap, y - lineGap, \r
3576                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3577         }\r
3578       }\r
3579     }\r
3580    } else { // nr == 1\r
3581         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3582         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3583         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3584         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3585       for (i=0; i<2; i++) {\r
3586         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3587             partnerHighlightInfo.sq[i].y >= 0) {\r
3588           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3589                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3590           clips[num_clips++] =\r
3591             CreateRectRgn(x - lineGap, y - lineGap, \r
3592                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3593         }\r
3594         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3595             oldPartnerHighlight.sq[i].y >= 0) {\r
3596           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3597                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3598           clips[num_clips++] =\r
3599             CreateRectRgn(x - lineGap, y - lineGap, \r
3600                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3601         }\r
3602       }\r
3603    }\r
3604   } else {\r
3605     fullrepaint = TRUE;\r
3606   }\r
3607 \r
3608   /* Create a buffer bitmap - this is the actual bitmap\r
3609    * being written to.  When all the work is done, we can\r
3610    * copy it to the real DC (the screen).  This avoids\r
3611    * the problems with flickering.\r
3612    */\r
3613   GetClientRect(hwndMain, &Rect);\r
3614   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3615                                         Rect.bottom-Rect.top+1);\r
3616   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3617   if (!appData.monoMode) {\r
3618     SelectPalette(hdcmem, hPal, FALSE);\r
3619   }\r
3620 \r
3621   /* Create clips for dragging */\r
3622   if (!fullrepaint) {\r
3623     if (dragInfo.from.x >= 0) {\r
3624       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3625       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3626     }\r
3627     if (dragInfo.start.x >= 0) {\r
3628       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3629       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3630     }\r
3631     if (dragInfo.pos.x >= 0) {\r
3632       x = dragInfo.pos.x - squareSize / 2;\r
3633       y = dragInfo.pos.y - squareSize / 2;\r
3634       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3635     }\r
3636     if (dragInfo.lastpos.x >= 0) {\r
3637       x = dragInfo.lastpos.x - squareSize / 2;\r
3638       y = dragInfo.lastpos.y - squareSize / 2;\r
3639       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3640     }\r
3641   }\r
3642 \r
3643   /* Are we animating a move?  \r
3644    * If so, \r
3645    *   - remove the piece from the board (temporarely)\r
3646    *   - calculate the clipping region\r
3647    */\r
3648   if (!fullrepaint) {\r
3649     if (animInfo.piece != EmptySquare) {\r
3650       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3651       x = boardRect.left + animInfo.lastpos.x;\r
3652       y = boardRect.top + animInfo.lastpos.y;\r
3653       x2 = boardRect.left + animInfo.pos.x;\r
3654       y2 = boardRect.top + animInfo.pos.y;\r
3655       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3656       /* Slight kludge.  The real problem is that after AnimateMove is\r
3657          done, the position on the screen does not match lastDrawn.\r
3658          This currently causes trouble only on e.p. captures in\r
3659          atomic, where the piece moves to an empty square and then\r
3660          explodes.  The old and new positions both had an empty square\r
3661          at the destination, but animation has drawn a piece there and\r
3662          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3663       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3664     }\r
3665   }\r
3666 \r
3667   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3668   if (num_clips == 0)\r
3669     fullrepaint = TRUE;\r
3670 \r
3671   /* Set clipping on the memory DC */\r
3672   if (!fullrepaint) {\r
3673     SelectClipRgn(hdcmem, clips[0]);\r
3674     for (x = 1; x < num_clips; x++) {\r
3675       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3676         abort();  // this should never ever happen!\r
3677     }\r
3678   }\r
3679 \r
3680   /* Do all the drawing to the memory DC */\r
3681   if(explodeInfo.radius) { // [HGM] atomic\r
3682         HBRUSH oldBrush;\r
3683         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3684         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3685         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3686         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3687         x += squareSize/2;\r
3688         y += squareSize/2;\r
3689         if(!fullrepaint) {\r
3690           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3691           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3692         }\r
3693         DrawGridOnDC(hdcmem);\r
3694         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3695         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3696         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3697         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3698         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3699         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3700         SelectObject(hdcmem, oldBrush);\r
3701   } else {\r
3702     DrawGridOnDC(hdcmem);\r
3703     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3704         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3705         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3706     } else {\r
3707         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3708         oldPartnerHighlight = partnerHighlightInfo;\r
3709     }\r
3710     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3711   }\r
3712   if(nr == 0) // [HGM] dual: markers only on left board\r
3713   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3714     for (column = 0; column < BOARD_WIDTH; column++) {\r
3715         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3716             HBRUSH oldBrush = SelectObject(hdcmem, \r
3717                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3718             SquareToPos(row, column, &x, &y);\r
3719             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3720                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3721             SelectObject(hdcmem, oldBrush);\r
3722         }\r
3723     }\r
3724   }\r
3725   if(logoHeight) {\r
3726         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3727         if(appData.autoLogo) {\r
3728           \r
3729           switch(gameMode) { // pick logos based on game mode\r
3730             case IcsObserving:\r
3731                 whiteLogo = second.programLogo; // ICS logo\r
3732                 blackLogo = second.programLogo;\r
3733             default:\r
3734                 break;\r
3735             case IcsPlayingWhite:\r
3736                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3737                 blackLogo = second.programLogo; // ICS logo\r
3738                 break;\r
3739             case IcsPlayingBlack:\r
3740                 whiteLogo = second.programLogo; // ICS logo\r
3741                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3742                 break;\r
3743             case TwoMachinesPlay:\r
3744                 if(first.twoMachinesColor[0] == 'b') {\r
3745                     whiteLogo = second.programLogo;\r
3746                     blackLogo = first.programLogo;\r
3747                 }\r
3748                 break;\r
3749             case MachinePlaysWhite:\r
3750                 blackLogo = userLogo;\r
3751                 break;\r
3752             case MachinePlaysBlack:\r
3753                 whiteLogo = userLogo;\r
3754                 blackLogo = first.programLogo;\r
3755           }\r
3756         }\r
3757         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3758         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3759   }\r
3760 \r
3761   if( appData.highlightMoveWithArrow ) {\r
3762     DrawArrowHighlight(hdcmem);\r
3763   }\r
3764 \r
3765   DrawCoordsOnDC(hdcmem);\r
3766 \r
3767   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3768                  /* to make sure lastDrawn contains what is actually drawn */\r
3769 \r
3770   /* Put the dragged piece back into place and draw it (out of place!) */\r
3771     if (dragged_piece != EmptySquare) {\r
3772     /* [HGM] or restack */\r
3773     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3774                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3775     else\r
3776     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3777                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3778     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3779     x = dragInfo.pos.x - squareSize / 2;\r
3780     y = dragInfo.pos.y - squareSize / 2;\r
3781     DrawPieceOnDC(hdcmem, dragged_piece,\r
3782                   ((int) dragged_piece < (int) BlackPawn), \r
3783                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3784   }   \r
3785   \r
3786   /* Put the animated piece back into place and draw it */\r
3787   if (animInfo.piece != EmptySquare) {\r
3788     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3789     x = boardRect.left + animInfo.pos.x;\r
3790     y = boardRect.top + animInfo.pos.y;\r
3791     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3792                   ((int) animInfo.piece < (int) BlackPawn),\r
3793                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3794   }\r
3795 \r
3796   /* Release the bufferBitmap by selecting in the old bitmap \r
3797    * and delete the memory DC\r
3798    */\r
3799   SelectObject(hdcmem, oldBitmap);\r
3800   DeleteDC(hdcmem);\r
3801 \r
3802   /* Set clipping on the target DC */\r
3803   if (!fullrepaint) {\r
3804     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3805         RECT rect;\r
3806         GetRgnBox(clips[x], &rect);\r
3807         DeleteObject(clips[x]);\r
3808         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3809                           rect.right + wpMain.width/2, rect.bottom);\r
3810     }\r
3811     SelectClipRgn(hdc, clips[0]);\r
3812     for (x = 1; x < num_clips; x++) {\r
3813       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3814         abort();   // this should never ever happen!\r
3815     } \r
3816   }\r
3817 \r
3818   /* Copy the new bitmap onto the screen in one go.\r
3819    * This way we avoid any flickering\r
3820    */\r
3821   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3822   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3823          boardRect.right - boardRect.left,\r
3824          boardRect.bottom - boardRect.top,\r
3825          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3826   if(saveDiagFlag) { \r
3827     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
3828     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3829 \r
3830     GetObject(bufferBitmap, sizeof(b), &b);\r
3831     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
3832         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3833         bih.biWidth = b.bmWidth;\r
3834         bih.biHeight = b.bmHeight;\r
3835         bih.biPlanes = 1;\r
3836         bih.biBitCount = b.bmBitsPixel;\r
3837         bih.biCompression = 0;\r
3838         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3839         bih.biXPelsPerMeter = 0;\r
3840         bih.biYPelsPerMeter = 0;\r
3841         bih.biClrUsed = 0;\r
3842         bih.biClrImportant = 0;\r
3843 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3844 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3845         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3846 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3847 \r
3848         wb = b.bmWidthBytes;\r
3849         // count colors\r
3850         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3851                 int k = ((int*) pData)[i];\r
3852                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3853                 if(j >= 16) break;\r
3854                 color[j] = k;\r
3855                 if(j >= nrColors) nrColors = j+1;\r
3856         }\r
3857         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3858                 INT p = 0;\r
3859                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3860                     for(w=0; w<(wb>>2); w+=2) {\r
3861                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3862                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3863                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3864                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3865                         pData[p++] = m | j<<4;\r
3866                     }\r
3867                     while(p&3) pData[p++] = 0;\r
3868                 }\r
3869                 fac = 3;\r
3870                 wb = ((wb+31)>>5)<<2;\r
3871         }\r
3872         // write BITMAPFILEHEADER\r
3873         fprintf(diagFile, "BM");\r
3874         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3875         fputDW(diagFile, 0);\r
3876         fputDW(diagFile, 0x36 + (fac?64:0));\r
3877         // write BITMAPINFOHEADER\r
3878         fputDW(diagFile, 40);\r
3879         fputDW(diagFile, b.bmWidth);\r
3880         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3881         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3882         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3883         fputDW(diagFile, 0);\r
3884         fputDW(diagFile, 0);\r
3885         fputDW(diagFile, 0);\r
3886         fputDW(diagFile, 0);\r
3887         fputDW(diagFile, 0);\r
3888         fputDW(diagFile, 0);\r
3889         // write color table\r
3890         if(fac)\r
3891         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3892         // write bitmap data\r
3893         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3894                 fputc(pData[i], diagFile);\r
3895      }\r
3896   }\r
3897 \r
3898   SelectObject(tmphdc, oldBitmap);\r
3899 \r
3900   /* Massive cleanup */\r
3901   for (x = 0; x < num_clips; x++)\r
3902     DeleteObject(clips[x]);\r
3903 \r
3904   DeleteDC(tmphdc);\r
3905   DeleteObject(bufferBitmap);\r
3906 \r
3907   if (releaseDC) \r
3908     ReleaseDC(hwndMain, hdc);\r
3909   \r
3910   if (lastDrawnFlipView != flipView && nr == 0) {\r
3911     if (flipView)\r
3912       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3913     else\r
3914       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3915   }\r
3916 \r
3917 /*  CopyBoard(lastDrawn, board);*/\r
3918   lastDrawnHighlight = highlightInfo;\r
3919   lastDrawnPremove   = premoveHighlightInfo;\r
3920   lastDrawnFlipView = flipView;\r
3921   lastDrawnValid[nr] = 1;\r
3922 }\r
3923 \r
3924 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3925 int\r
3926 SaveDiagram(f)\r
3927      FILE *f;\r
3928 {\r
3929     saveDiagFlag = 1; diagFile = f;\r
3930     HDCDrawPosition(NULL, TRUE, NULL);\r
3931 \r
3932     saveDiagFlag = 0;\r
3933 \r
3934 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
3935     \r
3936     fclose(f);\r
3937     return TRUE;\r
3938 }\r
3939 \r
3940 \r
3941 /*---------------------------------------------------------------------------*\\r
3942 | CLIENT PAINT PROCEDURE\r
3943 |   This is the main event-handler for the WM_PAINT message.\r
3944 |\r
3945 \*---------------------------------------------------------------------------*/\r
3946 VOID\r
3947 PaintProc(HWND hwnd)\r
3948 {\r
3949   HDC         hdc;\r
3950   PAINTSTRUCT ps;\r
3951   HFONT       oldFont;\r
3952 \r
3953   if((hdc = BeginPaint(hwnd, &ps))) {\r
3954     if (IsIconic(hwnd)) {\r
3955       DrawIcon(hdc, 2, 2, iconCurrent);\r
3956     } else {\r
3957       if (!appData.monoMode) {\r
3958         SelectPalette(hdc, hPal, FALSE);\r
3959         RealizePalette(hdc);\r
3960       }\r
3961       HDCDrawPosition(hdc, 1, NULL);\r
3962       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3963         flipView = !flipView; partnerUp = !partnerUp;\r
3964         HDCDrawPosition(hdc, 1, NULL);\r
3965         flipView = !flipView; partnerUp = !partnerUp;\r
3966       }\r
3967       oldFont =\r
3968         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3969       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3970                  ETO_CLIPPED|ETO_OPAQUE,\r
3971                  &messageRect, messageText, strlen(messageText), NULL);\r
3972       SelectObject(hdc, oldFont);\r
3973       DisplayBothClocks();\r
3974     }\r
3975     EndPaint(hwnd,&ps);\r
3976   }\r
3977 \r
3978   return;\r
3979 }\r
3980 \r
3981 \r
3982 /*\r
3983  * If the user selects on a border boundary, return -1; if off the board,\r
3984  *   return -2.  Otherwise map the event coordinate to the square.\r
3985  * The offset boardRect.left or boardRect.top must already have been\r
3986  *   subtracted from x.\r
3987  */\r
3988 int EventToSquare(x, limit)\r
3989      int x, limit;\r
3990 {\r
3991   if (x <= 0)\r
3992     return -2;\r
3993   if (x < lineGap)\r
3994     return -1;\r
3995   x -= lineGap;\r
3996   if ((x % (squareSize + lineGap)) >= squareSize)\r
3997     return -1;\r
3998   x /= (squareSize + lineGap);\r
3999     if (x >= limit)\r
4000     return -2;\r
4001   return x;\r
4002 }\r
4003 \r
4004 typedef struct {\r
4005   char piece;\r
4006   int command;\r
4007   char* name;\r
4008 } DropEnable;\r
4009 \r
4010 DropEnable dropEnables[] = {\r
4011   { 'P', DP_Pawn, N_("Pawn") },\r
4012   { 'N', DP_Knight, N_("Knight") },\r
4013   { 'B', DP_Bishop, N_("Bishop") },\r
4014   { 'R', DP_Rook, N_("Rook") },\r
4015   { 'Q', DP_Queen, N_("Queen") },\r
4016 };\r
4017 \r
4018 VOID\r
4019 SetupDropMenu(HMENU hmenu)\r
4020 {\r
4021   int i, count, enable;\r
4022   char *p;\r
4023   extern char white_holding[], black_holding[];\r
4024   char item[MSG_SIZ];\r
4025 \r
4026   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4027     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4028                dropEnables[i].piece);\r
4029     count = 0;\r
4030     while (p && *p++ == dropEnables[i].piece) count++;\r
4031       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4032     enable = count > 0 || !appData.testLegality\r
4033       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4034                       && !appData.icsActive);\r
4035     ModifyMenu(hmenu, dropEnables[i].command,\r
4036                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4037                dropEnables[i].command, item);\r
4038   }\r
4039 }\r
4040 \r
4041 void DragPieceBegin(int x, int y)\r
4042 {\r
4043       dragInfo.lastpos.x = boardRect.left + x;\r
4044       dragInfo.lastpos.y = boardRect.top + y;\r
4045       dragInfo.from.x = fromX;\r
4046       dragInfo.from.y = fromY;\r
4047       dragInfo.start = dragInfo.from;\r
4048       SetCapture(hwndMain);\r
4049 }\r
4050 \r
4051 void DragPieceEnd(int x, int y)\r
4052 {\r
4053     ReleaseCapture();\r
4054     dragInfo.start.x = dragInfo.start.y = -1;\r
4055     dragInfo.from = dragInfo.start;\r
4056     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4057 }\r
4058 \r
4059 /* Event handler for mouse messages */\r
4060 VOID\r
4061 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4062 {\r
4063   int x, y, menuNr;\r
4064   POINT pt;\r
4065   static int recursive = 0;\r
4066   HMENU hmenu;\r
4067   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4068 \r
4069   if (recursive) {\r
4070     if (message == WM_MBUTTONUP) {\r
4071       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4072          to the middle button: we simulate pressing the left button too!\r
4073          */\r
4074       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4075       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4076     }\r
4077     return;\r
4078   }\r
4079   recursive++;\r
4080   \r
4081   pt.x = LOWORD(lParam);\r
4082   pt.y = HIWORD(lParam);\r
4083   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4084   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4085   if (!flipView && y >= 0) {\r
4086     y = BOARD_HEIGHT - 1 - y;\r
4087   }\r
4088   if (flipView && x >= 0) {\r
4089     x = BOARD_WIDTH - 1 - x;\r
4090   }\r
4091 \r
4092   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4093 \r
4094   switch (message) {\r
4095   case WM_LBUTTONDOWN:\r
4096       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4097         if (gameMode == EditPosition) {\r
4098           SetWhiteToPlayEvent();\r
4099         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4100           AdjustClock(flipClock, -1);\r
4101         } else if (gameMode == IcsPlayingBlack ||\r
4102                    gameMode == MachinePlaysWhite) {\r
4103           CallFlagEvent();\r
4104         }\r
4105       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4106         if (gameMode == EditPosition) {\r
4107           SetBlackToPlayEvent();\r
4108         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4109           AdjustClock(!flipClock, -1);\r
4110         } else if (gameMode == IcsPlayingWhite ||\r
4111                    gameMode == MachinePlaysBlack) {\r
4112           CallFlagEvent();\r
4113         }\r
4114       }\r
4115       dragInfo.start.x = dragInfo.start.y = -1;\r
4116       dragInfo.from = dragInfo.start;\r
4117     if(fromX == -1 && frozen) { // not sure where this is for\r
4118                 fromX = fromY = -1; \r
4119       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4120       break;\r
4121     }\r
4122       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4123       DrawPosition(TRUE, NULL);\r
4124     break;\r
4125 \r
4126   case WM_LBUTTONUP:\r
4127       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4128       DrawPosition(TRUE, NULL);\r
4129     break;\r
4130 \r
4131   case WM_MOUSEMOVE:\r
4132     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4133     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4134     if ((appData.animateDragging || appData.highlightDragging)\r
4135         && (wParam & MK_LBUTTON)\r
4136         && dragInfo.from.x >= 0) \r
4137     {\r
4138       BOOL full_repaint = FALSE;\r
4139 \r
4140       if (appData.animateDragging) {\r
4141         dragInfo.pos = pt;\r
4142       }\r
4143       if (appData.highlightDragging) {\r
4144         SetHighlights(fromX, fromY, x, y);\r
4145         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4146             full_repaint = TRUE;\r
4147         }\r
4148       }\r
4149       \r
4150       DrawPosition( full_repaint, NULL);\r
4151       \r
4152       dragInfo.lastpos = dragInfo.pos;\r
4153     }\r
4154     break;\r
4155 \r
4156   case WM_MOUSEWHEEL: // [DM]\r
4157     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4158        /* Mouse Wheel is being rolled forward\r
4159         * Play moves forward\r
4160         */\r
4161        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4162                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4163        /* Mouse Wheel is being rolled backward\r
4164         * Play moves backward\r
4165         */\r
4166        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4167                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4168     }\r
4169     break;\r
4170 \r
4171   case WM_MBUTTONUP:\r
4172   case WM_RBUTTONUP:\r
4173     ReleaseCapture();\r
4174     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4175     break;\r
4176  \r
4177   case WM_MBUTTONDOWN:\r
4178   case WM_RBUTTONDOWN:\r
4179     ErrorPopDown();\r
4180     ReleaseCapture();\r
4181     fromX = fromY = -1;\r
4182     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4183     dragInfo.start.x = dragInfo.start.y = -1;\r
4184     dragInfo.from = dragInfo.start;\r
4185     dragInfo.lastpos = dragInfo.pos;\r
4186     if (appData.highlightDragging) {\r
4187       ClearHighlights();\r
4188     }\r
4189     if(y == -2) {\r
4190       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4191       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4192           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4193       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4194           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4195       }\r
4196       break;\r
4197     }\r
4198     DrawPosition(TRUE, NULL);\r
4199 \r
4200     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4201     switch (menuNr) {\r
4202     case 0:\r
4203       if (message == WM_MBUTTONDOWN) {\r
4204         buttonCount = 3;  /* even if system didn't think so */\r
4205         if (wParam & MK_SHIFT) \r
4206           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4207         else\r
4208           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4209       } else { /* message == WM_RBUTTONDOWN */\r
4210         /* Just have one menu, on the right button.  Windows users don't\r
4211            think to try the middle one, and sometimes other software steals\r
4212            it, or it doesn't really exist. */\r
4213         if(gameInfo.variant != VariantShogi)\r
4214             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4215         else\r
4216             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4217       }\r
4218       break;\r
4219     case 2:\r
4220       SetCapture(hwndMain);
4221       break;\r
4222     case 1:\r
4223       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4224       SetupDropMenu(hmenu);\r
4225       MenuPopup(hwnd, pt, hmenu, -1);\r
4226     default:\r
4227       break;\r
4228     }\r
4229     break;\r
4230   }\r
4231 \r
4232   recursive--;\r
4233 }\r
4234 \r
4235 /* Preprocess messages for buttons in main window */\r
4236 LRESULT CALLBACK\r
4237 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4238 {\r
4239   int id = GetWindowLong(hwnd, GWL_ID);\r
4240   int i, dir;\r
4241 \r
4242   for (i=0; i<N_BUTTONS; i++) {\r
4243     if (buttonDesc[i].id == id) break;\r
4244   }\r
4245   if (i == N_BUTTONS) return 0;\r
4246   switch (message) {\r
4247   case WM_KEYDOWN:\r
4248     switch (wParam) {\r
4249     case VK_LEFT:\r
4250     case VK_RIGHT:\r
4251       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4252       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4253       return TRUE;\r
4254     }\r
4255     break;\r
4256   case WM_CHAR:\r
4257     switch (wParam) {\r
4258     case '\r':\r
4259       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4260       return TRUE;\r
4261     default:\r
4262       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4263         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4264         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4265         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4266         SetFocus(h);\r
4267         SendMessage(h, WM_CHAR, wParam, lParam);\r
4268         return TRUE;\r
4269       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4270         PopUpMoveDialog((char)wParam);\r
4271       }\r
4272       break;\r
4273     }\r
4274     break;\r
4275   }\r
4276   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4277 }\r
4278 \r
4279 /* Process messages for Promotion dialog box */\r
4280 LRESULT CALLBACK\r
4281 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4282 {\r
4283   char promoChar;\r
4284 \r
4285   switch (message) {\r
4286   case WM_INITDIALOG: /* message: initialize dialog box */\r
4287     /* Center the dialog over the application window */\r
4288     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4289     Translate(hDlg, DLG_PromotionKing);\r
4290     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4291       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4292        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4293                SW_SHOW : SW_HIDE);\r
4294     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4295     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4296        ((PieceToChar(WhiteAngel) >= 'A' &&\r
4297          PieceToChar(WhiteAngel) != '~') ||\r
4298         (PieceToChar(BlackAngel) >= 'A' &&\r
4299          PieceToChar(BlackAngel) != '~')   ) ?\r
4300                SW_SHOW : SW_HIDE);\r
4301     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4302        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
4303          PieceToChar(WhiteMarshall) != '~') ||\r
4304         (PieceToChar(BlackMarshall) >= 'A' &&\r
4305          PieceToChar(BlackMarshall) != '~')   ) ?\r
4306                SW_SHOW : SW_HIDE);\r
4307     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4308     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4309        gameInfo.variant != VariantShogi ?\r
4310                SW_SHOW : SW_HIDE);\r
4311     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4312        gameInfo.variant != VariantShogi ?\r
4313                SW_SHOW : SW_HIDE);\r
4314     if(gameInfo.variant == VariantShogi) {\r
4315         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4316         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4317         SetWindowText(hDlg, "Promote?");\r
4318     }\r
4319     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4320        gameInfo.variant == VariantSuper ?\r
4321                SW_SHOW : SW_HIDE);\r
4322     return TRUE;\r
4323 \r
4324   case WM_COMMAND: /* message: received a command */\r
4325     switch (LOWORD(wParam)) {\r
4326     case IDCANCEL:\r
4327       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4328       ClearHighlights();\r
4329       DrawPosition(FALSE, NULL);\r
4330       return TRUE;\r
4331     case PB_King:\r
4332       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4333       break;\r
4334     case PB_Queen:\r
4335       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
4336       break;\r
4337     case PB_Rook:\r
4338       promoChar = PieceToChar(BlackRook);\r
4339       break;\r
4340     case PB_Bishop:\r
4341       promoChar = PieceToChar(BlackBishop);\r
4342       break;\r
4343     case PB_Chancellor:\r
4344       promoChar = PieceToChar(BlackMarshall);\r
4345       break;\r
4346     case PB_Archbishop:\r
4347       promoChar = PieceToChar(BlackAngel);\r
4348       break;\r
4349     case PB_Knight:\r
4350       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
4351       break;\r
4352     default:\r
4353       return FALSE;\r
4354     }\r
4355     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4356     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
4357        only show the popup when we are already sure the move is valid or\r
4358        legal. We pass a faulty move type, but the kludge is that FinishMove\r
4359        will figure out it is a promotion from the promoChar. */\r
4360     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4361     fromX = fromY = -1;\r
4362     if (!appData.highlightLastMove) {\r
4363       ClearHighlights();\r
4364       DrawPosition(FALSE, NULL);\r
4365     }\r
4366     return TRUE;\r
4367   }\r
4368   return FALSE;\r
4369 }\r
4370 \r
4371 /* Pop up promotion dialog */\r
4372 VOID\r
4373 PromotionPopup(HWND hwnd)\r
4374 {\r
4375   FARPROC lpProc;\r
4376 \r
4377   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4378   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4379     hwnd, (DLGPROC)lpProc);\r
4380   FreeProcInstance(lpProc);\r
4381 }\r
4382 \r
4383 void\r
4384 PromotionPopUp()\r
4385 {\r
4386   DrawPosition(TRUE, NULL);\r
4387   PromotionPopup(hwndMain);\r
4388 }\r
4389 \r
4390 /* Toggle ShowThinking */\r
4391 VOID\r
4392 ToggleShowThinking()\r
4393 {\r
4394   appData.showThinking = !appData.showThinking;\r
4395   ShowThinkingEvent();\r
4396 }\r
4397 \r
4398 VOID\r
4399 LoadGameDialog(HWND hwnd, char* title)\r
4400 {\r
4401   UINT number = 0;\r
4402   FILE *f;\r
4403   char fileTitle[MSG_SIZ];\r
4404   f = OpenFileDialog(hwnd, "rb", "",\r
4405                      appData.oldSaveStyle ? "gam" : "pgn",\r
4406                      GAME_FILT,\r
4407                      title, &number, fileTitle, NULL);\r
4408   if (f != NULL) {\r
4409     cmailMsgLoaded = FALSE;\r
4410     if (number == 0) {\r
4411       int error = GameListBuild(f);\r
4412       if (error) {\r
4413         DisplayError(_("Cannot build game list"), error);\r
4414       } else if (!ListEmpty(&gameList) &&\r
4415                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4416         GameListPopUp(f, fileTitle);\r
4417         return;\r
4418       }\r
4419       GameListDestroy();\r
4420       number = 1;\r
4421     }\r
4422     LoadGame(f, number, fileTitle, FALSE);\r
4423   }\r
4424 }\r
4425 \r
4426 int get_term_width()\r
4427 {\r
4428     HDC hdc;\r
4429     TEXTMETRIC tm;\r
4430     RECT rc;\r
4431     HFONT hfont, hold_font;\r
4432     LOGFONT lf;\r
4433     HWND hText;\r
4434 \r
4435     if (hwndConsole)\r
4436         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4437     else\r
4438         return 79;\r
4439 \r
4440     // get the text metrics\r
4441     hdc = GetDC(hText);\r
4442     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4443     if (consoleCF.dwEffects & CFE_BOLD)\r
4444         lf.lfWeight = FW_BOLD;\r
4445     if (consoleCF.dwEffects & CFE_ITALIC)\r
4446         lf.lfItalic = TRUE;\r
4447     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4448         lf.lfStrikeOut = TRUE;\r
4449     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4450         lf.lfUnderline = TRUE;\r
4451     hfont = CreateFontIndirect(&lf);\r
4452     hold_font = SelectObject(hdc, hfont);\r
4453     GetTextMetrics(hdc, &tm);\r
4454     SelectObject(hdc, hold_font);\r
4455     DeleteObject(hfont);\r
4456     ReleaseDC(hText, hdc);\r
4457 \r
4458     // get the rectangle\r
4459     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4460 \r
4461     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4462 }\r
4463 \r
4464 void UpdateICSWidth(HWND hText)\r
4465 {\r
4466     LONG old_width, new_width;\r
4467 \r
4468     new_width = get_term_width(hText, FALSE);\r
4469     old_width = GetWindowLong(hText, GWL_USERDATA);\r
4470     if (new_width != old_width)\r
4471     {\r
4472         ics_update_width(new_width);\r
4473         SetWindowLong(hText, GWL_USERDATA, new_width);\r
4474     }\r
4475 }\r
4476 \r
4477 VOID\r
4478 ChangedConsoleFont()\r
4479 {\r
4480   CHARFORMAT cfmt;\r
4481   CHARRANGE tmpsel, sel;\r
4482   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4483   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4484   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4485   PARAFORMAT paraf;\r
4486 \r
4487   cfmt.cbSize = sizeof(CHARFORMAT);\r
4488   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4489     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4490                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4491   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4492    * size.  This was undocumented in the version of MSVC++ that I had\r
4493    * when I wrote the code, but is apparently documented now.\r
4494    */\r
4495   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4496   cfmt.bCharSet = f->lf.lfCharSet;\r
4497   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4498   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4499   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4500   /* Why are the following seemingly needed too? */\r
4501   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4502   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4503   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4504   tmpsel.cpMin = 0;\r
4505   tmpsel.cpMax = -1; /*999999?*/\r
4506   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4507   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4508   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4509    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4510    */\r
4511   paraf.cbSize = sizeof(paraf);\r
4512   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4513   paraf.dxStartIndent = 0;\r
4514   paraf.dxOffset = WRAP_INDENT;\r
4515   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4516   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4517   UpdateICSWidth(hText);\r
4518 }\r
4519 \r
4520 /*---------------------------------------------------------------------------*\\r
4521  *\r
4522  * Window Proc for main window\r
4523  *\r
4524 \*---------------------------------------------------------------------------*/\r
4525 \r
4526 /* Process messages for main window, etc. */\r
4527 LRESULT CALLBACK\r
4528 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4529 {\r
4530   FARPROC lpProc;\r
4531   int wmId, wmEvent;\r
4532   char *defName;\r
4533   FILE *f;\r
4534   UINT number;\r
4535   char fileTitle[MSG_SIZ];\r
4536   char buf[MSG_SIZ];\r
4537   static SnapData sd;\r
4538 \r
4539   switch (message) {\r
4540 \r
4541   case WM_PAINT: /* message: repaint portion of window */\r
4542     PaintProc(hwnd);\r
4543     break;\r
4544 \r
4545   case WM_ERASEBKGND:\r
4546     if (IsIconic(hwnd)) {\r
4547       /* Cheat; change the message */\r
4548       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4549     } else {\r
4550       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4551     }\r
4552     break;\r
4553 \r
4554   case WM_LBUTTONDOWN:\r
4555   case WM_MBUTTONDOWN:\r
4556   case WM_RBUTTONDOWN:\r
4557   case WM_LBUTTONUP:\r
4558   case WM_MBUTTONUP:\r
4559   case WM_RBUTTONUP:\r
4560   case WM_MOUSEMOVE:\r
4561   case WM_MOUSEWHEEL:\r
4562     MouseEvent(hwnd, message, wParam, lParam);\r
4563     break;\r
4564 \r
4565   JAWS_KB_NAVIGATION\r
4566 \r
4567   case WM_CHAR:\r
4568     \r
4569     JAWS_ALT_INTERCEPT\r
4570 \r
4571     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4572         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4573         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4574         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4575         SetFocus(h);\r
4576         SendMessage(h, message, wParam, lParam);\r
4577     } else if(lParam != KF_REPEAT) {\r
4578         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4579                 PopUpMoveDialog((char)wParam);\r
4580         } else if((char)wParam == 003) CopyGameToClipboard();\r
4581          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4582     }\r
4583 \r
4584     break;\r
4585 \r
4586   case WM_PALETTECHANGED:\r
4587     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4588       int nnew;\r
4589       HDC hdc = GetDC(hwndMain);\r
4590       SelectPalette(hdc, hPal, TRUE);\r
4591       nnew = RealizePalette(hdc);\r
4592       if (nnew > 0) {\r
4593         paletteChanged = TRUE;\r
4594         InvalidateRect(hwnd, &boardRect, FALSE);\r
4595       }\r
4596       ReleaseDC(hwnd, hdc);\r
4597     }\r
4598     break;\r
4599 \r
4600   case WM_QUERYNEWPALETTE:\r
4601     if (!appData.monoMode /*&& paletteChanged*/) {\r
4602       int nnew;\r
4603       HDC hdc = GetDC(hwndMain);\r
4604       paletteChanged = FALSE;\r
4605       SelectPalette(hdc, hPal, FALSE);\r
4606       nnew = RealizePalette(hdc);\r
4607       if (nnew > 0) {\r
4608         InvalidateRect(hwnd, &boardRect, FALSE);\r
4609       }\r
4610       ReleaseDC(hwnd, hdc);\r
4611       return TRUE;\r
4612     }\r
4613     return FALSE;\r
4614 \r
4615   case WM_COMMAND: /* message: command from application menu */\r
4616     wmId    = LOWORD(wParam);\r
4617     wmEvent = HIWORD(wParam);\r
4618 \r
4619     switch (wmId) {\r
4620     case IDM_NewGame:\r
4621       ResetGameEvent();\r
4622       SAY("new game enter a move to play against the computer with white");\r
4623       break;\r
4624 \r
4625     case IDM_NewGameFRC:\r
4626       if( NewGameFRC() == 0 ) {\r
4627         ResetGameEvent();\r
4628       }\r
4629       break;\r
4630 \r
4631     case IDM_NewVariant:\r
4632       NewVariantPopup(hwnd);\r
4633       break;\r
4634 \r
4635     case IDM_LoadGame:\r
4636       LoadGameDialog(hwnd, _("Load Game from File"));\r
4637       break;\r
4638 \r
4639     case IDM_LoadNextGame:\r
4640       ReloadGame(1);\r
4641       break;\r
4642 \r
4643     case IDM_LoadPrevGame:\r
4644       ReloadGame(-1);\r
4645       break;\r
4646 \r
4647     case IDM_ReloadGame:\r
4648       ReloadGame(0);\r
4649       break;\r
4650 \r
4651     case IDM_LoadPosition:\r
4652       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4653         Reset(FALSE, TRUE);\r
4654       }\r
4655       number = 1;\r
4656       f = OpenFileDialog(hwnd, "rb", "",\r
4657                          appData.oldSaveStyle ? "pos" : "fen",\r
4658                          POSITION_FILT,\r
4659                          _("Load Position from File"), &number, fileTitle, NULL);\r
4660       if (f != NULL) {\r
4661         LoadPosition(f, number, fileTitle);\r
4662       }\r
4663       break;\r
4664 \r
4665     case IDM_LoadNextPosition:\r
4666       ReloadPosition(1);\r
4667       break;\r
4668 \r
4669     case IDM_LoadPrevPosition:\r
4670       ReloadPosition(-1);\r
4671       break;\r
4672 \r
4673     case IDM_ReloadPosition:\r
4674       ReloadPosition(0);\r
4675       break;\r
4676 \r
4677     case IDM_SaveGame:\r
4678       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4679       f = OpenFileDialog(hwnd, "a", defName,\r
4680                          appData.oldSaveStyle ? "gam" : "pgn",\r
4681                          GAME_FILT,\r
4682                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4683       if (f != NULL) {\r
4684         SaveGame(f, 0, "");\r
4685       }\r
4686       break;\r
4687 \r
4688     case IDM_SavePosition:\r
4689       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4690       f = OpenFileDialog(hwnd, "a", defName,\r
4691                          appData.oldSaveStyle ? "pos" : "fen",\r
4692                          POSITION_FILT,\r
4693                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4694       if (f != NULL) {\r
4695         SavePosition(f, 0, "");\r
4696       }\r
4697       break;\r
4698 \r
4699     case IDM_SaveDiagram:\r
4700       defName = "diagram";\r
4701       f = OpenFileDialog(hwnd, "wb", defName,\r
4702                          "bmp",\r
4703                          DIAGRAM_FILT,\r
4704                          "Save Diagram to File", NULL, fileTitle, NULL);\r
4705       if (f != NULL) {\r
4706         SaveDiagram(f);\r
4707       }\r
4708       break;\r
4709 \r
4710     case IDM_CopyGame:\r
4711       CopyGameToClipboard();\r
4712       break;\r
4713 \r
4714     case IDM_PasteGame:\r
4715       PasteGameFromClipboard();\r
4716       break;\r
4717 \r
4718     case IDM_CopyGameListToClipboard:\r
4719       CopyGameListToClipboard();\r
4720       break;\r
4721 \r
4722     /* [AS] Autodetect FEN or PGN data */\r
4723     case IDM_PasteAny:\r
4724       PasteGameOrFENFromClipboard();\r
4725       break;\r
4726 \r
4727     /* [AS] Move history */\r
4728     case IDM_ShowMoveHistory:\r
4729         if( MoveHistoryIsUp() ) {\r
4730             MoveHistoryPopDown();\r
4731         }\r
4732         else {\r
4733             MoveHistoryPopUp();\r
4734         }\r
4735         break;\r
4736 \r
4737     /* [AS] Eval graph */\r
4738     case IDM_ShowEvalGraph:\r
4739         if( EvalGraphIsUp() ) {\r
4740             EvalGraphPopDown();\r
4741         }\r
4742         else {\r
4743             EvalGraphPopUp();\r
4744             SetFocus(hwndMain);\r
4745         }\r
4746         break;\r
4747 \r
4748     /* [AS] Engine output */\r
4749     case IDM_ShowEngineOutput:\r
4750         if( EngineOutputIsUp() ) {\r
4751             EngineOutputPopDown();\r
4752         }\r
4753         else {\r
4754             EngineOutputPopUp();\r
4755         }\r
4756         break;\r
4757 \r
4758     /* [AS] User adjudication */\r
4759     case IDM_UserAdjudication_White:\r
4760         UserAdjudicationEvent( +1 );\r
4761         break;\r
4762 \r
4763     case IDM_UserAdjudication_Black:\r
4764         UserAdjudicationEvent( -1 );\r
4765         break;\r
4766 \r
4767     case IDM_UserAdjudication_Draw:\r
4768         UserAdjudicationEvent( 0 );\r
4769         break;\r
4770 \r
4771     /* [AS] Game list options dialog */\r
4772     case IDM_GameListOptions:\r
4773       GameListOptions();\r
4774       break;\r
4775 \r
4776     case IDM_NewChat:\r
4777       ChatPopUp(NULL);\r
4778       break;\r
4779 \r
4780     case IDM_CopyPosition:\r
4781       CopyFENToClipboard();\r
4782       break;\r
4783 \r
4784     case IDM_PastePosition:\r
4785       PasteFENFromClipboard();\r
4786       break;\r
4787 \r
4788     case IDM_MailMove:\r
4789       MailMoveEvent();\r
4790       break;\r
4791 \r
4792     case IDM_ReloadCMailMsg:\r
4793       Reset(TRUE, TRUE);\r
4794       ReloadCmailMsgEvent(FALSE);\r
4795       break;\r
4796 \r
4797     case IDM_Minimize:\r
4798       ShowWindow(hwnd, SW_MINIMIZE);\r
4799       break;\r
4800 \r
4801     case IDM_Exit:\r
4802       ExitEvent(0);\r
4803       break;\r
4804 \r
4805     case IDM_MachineWhite:\r
4806       MachineWhiteEvent();\r
4807       /*\r
4808        * refresh the tags dialog only if it's visible\r
4809        */\r
4810       if (gameMode == MachinePlaysWhite && 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 white");\r
4817       break;\r
4818 \r
4819     case IDM_MachineBlack:\r
4820       MachineBlackEvent();\r
4821       /*\r
4822        * refresh the tags dialog only if it's visible\r
4823        */\r
4824       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4825           char *tags;\r
4826           tags = PGNTags(&gameInfo);\r
4827           TagsPopUp(tags, CmailMsg());\r
4828           free(tags);\r
4829       }\r
4830       SAY("computer starts playing black");\r
4831       break;\r
4832 \r
4833     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4834       if(gameMode != BeginningOfGame) { // allow menu item to remain enabled for better mode highligting\r
4835         DisplayError(_("You can only start a match from the initial position."), 0); break;\r
4836       }\r
4837       matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)\r
4838       appData.matchGames = appData.defaultMatchGames;\r
4839       matchGame = 1;\r
4840 \r
4841     case IDM_TwoMachines:\r
4842       TwoMachinesEvent();\r
4843       /*\r
4844        * refresh the tags dialog only if it's visible\r
4845        */\r
4846       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4847           char *tags;\r
4848           tags = PGNTags(&gameInfo);\r
4849           TagsPopUp(tags, CmailMsg());\r
4850           free(tags);\r
4851       }\r
4852       SAY("computer starts playing both sides");\r
4853       break;\r
4854 \r
4855     case IDM_AnalysisMode:\r
4856       if (!first.analysisSupport) {\r
4857         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4858         DisplayError(buf, 0);\r
4859       } else {\r
4860         SAY("analyzing current position");\r
4861         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4862         if (appData.icsActive) {\r
4863                if (gameMode != IcsObserving) {\r
4864                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4865                        DisplayError(buf, 0);\r
4866                        /* secure check */\r
4867                        if (appData.icsEngineAnalyze) {\r
4868                                if (appData.debugMode) \r
4869                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4870                                ExitAnalyzeMode();\r
4871                                ModeHighlight();\r
4872                                break;\r
4873                        }\r
4874                        break;\r
4875                } else {\r
4876                        /* if enable, user want disable icsEngineAnalyze */\r
4877                        if (appData.icsEngineAnalyze) {\r
4878                                ExitAnalyzeMode();\r
4879                                ModeHighlight();\r
4880                                break;\r
4881                        }\r
4882                        appData.icsEngineAnalyze = TRUE;\r
4883                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4884                }\r
4885         } \r
4886         if (!appData.showThinking) ToggleShowThinking();\r
4887         AnalyzeModeEvent();\r
4888       }\r
4889       break;\r
4890 \r
4891     case IDM_AnalyzeFile:\r
4892       if (!first.analysisSupport) {\r
4893         char buf[MSG_SIZ];\r
4894           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4895         DisplayError(buf, 0);\r
4896       } else {\r
4897         if (!appData.showThinking) ToggleShowThinking();\r
4898         AnalyzeFileEvent();\r
4899         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4900         AnalysisPeriodicEvent(1);\r
4901       }\r
4902       break;\r
4903 \r
4904     case IDM_IcsClient:\r
4905       IcsClientEvent();\r
4906       break;\r
4907 \r
4908     case IDM_EditGame:\r
4909     case IDM_EditGame2:\r
4910       EditGameEvent();\r
4911       SAY("edit game");\r
4912       break;\r
4913 \r
4914     case IDM_EditPosition:\r
4915     case IDM_EditPosition2:\r
4916       EditPositionEvent();\r
4917       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4918       break;\r
4919 \r
4920     case IDM_Training:\r
4921       TrainingEvent();\r
4922       break;\r
4923 \r
4924     case IDM_ShowGameList:\r
4925       ShowGameListProc();\r
4926       break;\r
4927 \r
4928     case IDM_EditProgs1:\r
4929       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4930       break;\r
4931 \r
4932     case IDM_EditProgs2:\r
4933       EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);\r
4934       break;\r
4935 \r
4936     case IDM_EditServers:\r
4937       EditTagsPopUp(icsNames, &icsNames);\r
4938       break;\r
4939 \r
4940     case IDM_EditTags:\r
4941     case IDM_Tags:\r
4942       EditTagsProc();\r
4943       break;\r
4944 \r
4945     case IDM_EditComment:\r
4946     case IDM_Comment:\r
4947       if (commentUp && editComment) {\r
4948         CommentPopDown();\r
4949       } else {\r
4950         EditCommentEvent();\r
4951       }\r
4952       break;\r
4953 \r
4954     case IDM_Pause:\r
4955       PauseEvent();\r
4956       break;\r
4957 \r
4958     case IDM_Accept:\r
4959       AcceptEvent();\r
4960       break;\r
4961 \r
4962     case IDM_Decline:\r
4963       DeclineEvent();\r
4964       break;\r
4965 \r
4966     case IDM_Rematch:\r
4967       RematchEvent();\r
4968       break;\r
4969 \r
4970     case IDM_CallFlag:\r
4971       CallFlagEvent();\r
4972       break;\r
4973 \r
4974     case IDM_Draw:\r
4975       DrawEvent();\r
4976       break;\r
4977 \r
4978     case IDM_Adjourn:\r
4979       AdjournEvent();\r
4980       break;\r
4981 \r
4982     case IDM_Abort:\r
4983       AbortEvent();\r
4984       break;\r
4985 \r
4986     case IDM_Resign:\r
4987       ResignEvent();\r
4988       break;\r
4989 \r
4990     case IDM_StopObserving:\r
4991       StopObservingEvent();\r
4992       break;\r
4993 \r
4994     case IDM_StopExamining:\r
4995       StopExaminingEvent();\r
4996       break;\r
4997 \r
4998     case IDM_Upload:\r
4999       UploadGameEvent();\r
5000       break;\r
5001 \r
5002     case IDM_TypeInMove:\r
5003       PopUpMoveDialog('\000');\r
5004       break;\r
5005 \r
5006     case IDM_TypeInName:\r
5007       PopUpNameDialog('\000');\r
5008       break;\r
5009 \r
5010     case IDM_Backward:\r
5011       BackwardEvent();\r
5012       SetFocus(hwndMain);\r
5013       break;\r
5014 \r
5015     JAWS_MENU_ITEMS\r
5016 \r
5017     case IDM_Forward:\r
5018       ForwardEvent();\r
5019       SetFocus(hwndMain);\r
5020       break;\r
5021 \r
5022     case IDM_ToStart:\r
5023       ToStartEvent();\r
5024       SetFocus(hwndMain);\r
5025       break;\r
5026 \r
5027     case IDM_ToEnd:\r
5028       ToEndEvent();\r
5029       SetFocus(hwndMain);\r
5030       break;\r
5031 \r
5032     case IDM_Revert:\r
5033       RevertEvent(FALSE);\r
5034       break;\r
5035 \r
5036     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5037       RevertEvent(TRUE);\r
5038       break;\r
5039 \r
5040     case IDM_TruncateGame:\r
5041       TruncateGameEvent();\r
5042       break;\r
5043 \r
5044     case IDM_MoveNow:\r
5045       MoveNowEvent();\r
5046       break;\r
5047 \r
5048     case IDM_RetractMove:\r
5049       RetractMoveEvent();\r
5050       break;\r
5051 \r
5052     case IDM_FlipView:\r
5053       flipView = !flipView;\r
5054       DrawPosition(FALSE, NULL);\r
5055       break;\r
5056 \r
5057     case IDM_FlipClock:\r
5058       flipClock = !flipClock;\r
5059       DisplayBothClocks();\r
5060       DrawPosition(FALSE, NULL);\r
5061       break;\r
5062 \r
5063     case IDM_MuteSounds:\r
5064       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5065       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5066                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5067       break;\r
5068 \r
5069     case IDM_GeneralOptions:\r
5070       GeneralOptionsPopup(hwnd);\r
5071       DrawPosition(TRUE, NULL);\r
5072       break;\r
5073 \r
5074     case IDM_BoardOptions:\r
5075       BoardOptionsPopup(hwnd);\r
5076       break;\r
5077 \r
5078     case IDM_EnginePlayOptions:\r
5079       EnginePlayOptionsPopup(hwnd);\r
5080       break;\r
5081 \r
5082     case IDM_Engine1Options:\r
5083       EngineOptionsPopup(hwnd, &first);\r
5084       break;\r
5085 \r
5086     case IDM_Engine2Options:\r
5087       savedHwnd = hwnd;\r
5088       if(WaitForSecond(SettingsMenuIfReady)) break;\r
5089       EngineOptionsPopup(hwnd, &second);\r
5090       break;\r
5091 \r
5092     case IDM_OptionsUCI:\r
5093       UciOptionsPopup(hwnd);\r
5094       break;\r
5095 \r
5096     case IDM_IcsOptions:\r
5097       IcsOptionsPopup(hwnd);\r
5098       break;\r
5099 \r
5100     case IDM_Fonts:\r
5101       FontsOptionsPopup(hwnd);\r
5102       break;\r
5103 \r
5104     case IDM_Sounds:\r
5105       SoundOptionsPopup(hwnd);\r
5106       break;\r
5107 \r
5108     case IDM_CommPort:\r
5109       CommPortOptionsPopup(hwnd);\r
5110       break;\r
5111 \r
5112     case IDM_LoadOptions:\r
5113       LoadOptionsPopup(hwnd);\r
5114       break;\r
5115 \r
5116     case IDM_SaveOptions:\r
5117       SaveOptionsPopup(hwnd);\r
5118       break;\r
5119 \r
5120     case IDM_TimeControl:\r
5121       TimeControlOptionsPopup(hwnd);\r
5122       break;\r
5123 \r
5124     case IDM_SaveSettings:\r
5125       SaveSettings(settingsFileName);\r
5126       break;\r
5127 \r
5128     case IDM_SaveSettingsOnExit:\r
5129       saveSettingsOnExit = !saveSettingsOnExit;\r
5130       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5131                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5132                                          MF_CHECKED : MF_UNCHECKED));\r
5133       break;\r
5134 \r
5135     case IDM_Hint:\r
5136       HintEvent();\r
5137       break;\r
5138 \r
5139     case IDM_Book:\r
5140       BookEvent();\r
5141       break;\r
5142 \r
5143     case IDM_AboutGame:\r
5144       AboutGameEvent();\r
5145       break;\r
5146 \r
5147     case IDM_Debug:\r
5148       appData.debugMode = !appData.debugMode;\r
5149       if (appData.debugMode) {\r
5150         char dir[MSG_SIZ];\r
5151         GetCurrentDirectory(MSG_SIZ, dir);\r
5152         SetCurrentDirectory(installDir);\r
5153         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5154         SetCurrentDirectory(dir);\r
5155         setbuf(debugFP, NULL);\r
5156       } else {\r
5157         fclose(debugFP);\r
5158         debugFP = NULL;\r
5159       }\r
5160       break;\r
5161 \r
5162     case IDM_HELPCONTENTS:\r
5163       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5164           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5165           MessageBox (GetFocus(),\r
5166                     _("Unable to activate help"),\r
5167                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5168       }\r
5169       break;\r
5170 \r
5171     case IDM_HELPSEARCH:\r
5172         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5173             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5174         MessageBox (GetFocus(),\r
5175                     _("Unable to activate help"),\r
5176                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5177       }\r
5178       break;\r
5179 \r
5180     case IDM_HELPHELP:\r
5181       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5182         MessageBox (GetFocus(),\r
5183                     _("Unable to activate help"),\r
5184                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5185       }\r
5186       break;\r
5187 \r
5188     case IDM_ABOUT:\r
5189       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5190       DialogBox(hInst, \r
5191         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5192         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5193       FreeProcInstance(lpProc);\r
5194       break;\r
5195 \r
5196     case IDM_DirectCommand1:\r
5197       AskQuestionEvent(_("Direct Command"),\r
5198                        _("Send to chess program:"), "", "1");\r
5199       break;\r
5200     case IDM_DirectCommand2:\r
5201       AskQuestionEvent(_("Direct Command"),\r
5202                        _("Send to second chess program:"), "", "2");\r
5203       break;\r
5204 \r
5205     case EP_WhitePawn:\r
5206       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5207       fromX = fromY = -1;\r
5208       break;\r
5209 \r
5210     case EP_WhiteKnight:\r
5211       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5212       fromX = fromY = -1;\r
5213       break;\r
5214 \r
5215     case EP_WhiteBishop:\r
5216       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5217       fromX = fromY = -1;\r
5218       break;\r
5219 \r
5220     case EP_WhiteRook:\r
5221       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5222       fromX = fromY = -1;\r
5223       break;\r
5224 \r
5225     case EP_WhiteQueen:\r
5226       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5227       fromX = fromY = -1;\r
5228       break;\r
5229 \r
5230     case EP_WhiteFerz:\r
5231       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5232       fromX = fromY = -1;\r
5233       break;\r
5234 \r
5235     case EP_WhiteWazir:\r
5236       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5237       fromX = fromY = -1;\r
5238       break;\r
5239 \r
5240     case EP_WhiteAlfil:\r
5241       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5242       fromX = fromY = -1;\r
5243       break;\r
5244 \r
5245     case EP_WhiteCannon:\r
5246       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5247       fromX = fromY = -1;\r
5248       break;\r
5249 \r
5250     case EP_WhiteCardinal:\r
5251       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5252       fromX = fromY = -1;\r
5253       break;\r
5254 \r
5255     case EP_WhiteMarshall:\r
5256       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5257       fromX = fromY = -1;\r
5258       break;\r
5259 \r
5260     case EP_WhiteKing:\r
5261       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5262       fromX = fromY = -1;\r
5263       break;\r
5264 \r
5265     case EP_BlackPawn:\r
5266       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5267       fromX = fromY = -1;\r
5268       break;\r
5269 \r
5270     case EP_BlackKnight:\r
5271       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5272       fromX = fromY = -1;\r
5273       break;\r
5274 \r
5275     case EP_BlackBishop:\r
5276       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5277       fromX = fromY = -1;\r
5278       break;\r
5279 \r
5280     case EP_BlackRook:\r
5281       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5282       fromX = fromY = -1;\r
5283       break;\r
5284 \r
5285     case EP_BlackQueen:\r
5286       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5287       fromX = fromY = -1;\r
5288       break;\r
5289 \r
5290     case EP_BlackFerz:\r
5291       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5292       fromX = fromY = -1;\r
5293       break;\r
5294 \r
5295     case EP_BlackWazir:\r
5296       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5297       fromX = fromY = -1;\r
5298       break;\r
5299 \r
5300     case EP_BlackAlfil:\r
5301       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5302       fromX = fromY = -1;\r
5303       break;\r
5304 \r
5305     case EP_BlackCannon:\r
5306       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5307       fromX = fromY = -1;\r
5308       break;\r
5309 \r
5310     case EP_BlackCardinal:\r
5311       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5312       fromX = fromY = -1;\r
5313       break;\r
5314 \r
5315     case EP_BlackMarshall:\r
5316       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5317       fromX = fromY = -1;\r
5318       break;\r
5319 \r
5320     case EP_BlackKing:\r
5321       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5322       fromX = fromY = -1;\r
5323       break;\r
5324 \r
5325     case EP_EmptySquare:\r
5326       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5327       fromX = fromY = -1;\r
5328       break;\r
5329 \r
5330     case EP_ClearBoard:\r
5331       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5332       fromX = fromY = -1;\r
5333       break;\r
5334 \r
5335     case EP_White:\r
5336       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5337       fromX = fromY = -1;\r
5338       break;\r
5339 \r
5340     case EP_Black:\r
5341       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5342       fromX = fromY = -1;\r
5343       break;\r
5344 \r
5345     case EP_Promote:\r
5346       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5347       fromX = fromY = -1;\r
5348       break;\r
5349 \r
5350     case EP_Demote:\r
5351       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5352       fromX = fromY = -1;\r
5353       break;\r
5354 \r
5355     case DP_Pawn:\r
5356       DropMenuEvent(WhitePawn, fromX, fromY);\r
5357       fromX = fromY = -1;\r
5358       break;\r
5359 \r
5360     case DP_Knight:\r
5361       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5362       fromX = fromY = -1;\r
5363       break;\r
5364 \r
5365     case DP_Bishop:\r
5366       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5367       fromX = fromY = -1;\r
5368       break;\r
5369 \r
5370     case DP_Rook:\r
5371       DropMenuEvent(WhiteRook, fromX, fromY);\r
5372       fromX = fromY = -1;\r
5373       break;\r
5374 \r
5375     case DP_Queen:\r
5376       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5377       fromX = fromY = -1;\r
5378       break;\r
5379 \r
5380     case IDM_English:\r
5381       barbaric = 0;\r
5382       TranslateMenus(0);\r
5383       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5384       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5385       lastChecked = wmId;\r
5386       break;\r
5387 \r
5388     default:\r
5389       if(wmId > IDM_English && wmId < IDM_English+5) {\r
5390           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5391           TranslateMenus(0);\r
5392           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5393           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5394           lastChecked = wmId;\r
5395           break;\r
5396       }\r
5397       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5398     }\r
5399     break;\r
5400 \r
5401   case WM_TIMER:\r
5402     switch (wParam) {\r
5403     case CLOCK_TIMER_ID:\r
5404       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5405       clockTimerEvent = 0;\r
5406       DecrementClocks(); /* call into back end */\r
5407       break;\r
5408     case LOAD_GAME_TIMER_ID:\r
5409       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5410       loadGameTimerEvent = 0;\r
5411       AutoPlayGameLoop(); /* call into back end */\r
5412       break;\r
5413     case ANALYSIS_TIMER_ID:\r
5414       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5415                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5416         AnalysisPeriodicEvent(0);\r
5417       } else {\r
5418         KillTimer(hwnd, analysisTimerEvent);\r
5419         analysisTimerEvent = 0;\r
5420       }\r
5421       break;\r
5422     case DELAYED_TIMER_ID:\r
5423       KillTimer(hwnd, delayedTimerEvent);\r
5424       delayedTimerEvent = 0;\r
5425       delayedTimerCallback();\r
5426       break;\r
5427     }\r
5428     break;\r
5429 \r
5430   case WM_USER_Input:\r
5431     InputEvent(hwnd, message, wParam, lParam);\r
5432     break;\r
5433 \r
5434   /* [AS] Also move "attached" child windows */\r
5435   case WM_WINDOWPOSCHANGING:\r
5436 \r
5437     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5438         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5439 \r
5440         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5441             /* Window is moving */\r
5442             RECT rcMain;\r
5443 \r
5444 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5445             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5446             rcMain.right  = wpMain.x + wpMain.width;\r
5447             rcMain.top    = wpMain.y;\r
5448             rcMain.bottom = wpMain.y + wpMain.height;\r
5449             \r
5450             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5451             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5452             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5453             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5454             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5455             wpMain.x = lpwp->x;\r
5456             wpMain.y = lpwp->y;\r
5457         }\r
5458     }\r
5459     break;\r
5460 \r
5461   /* [AS] Snapping */\r
5462   case WM_ENTERSIZEMOVE:\r
5463     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5464     if (hwnd == hwndMain) {\r
5465       doingSizing = TRUE;\r
5466       lastSizing = 0;\r
5467     }\r
5468     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5469     break;\r
5470 \r
5471   case WM_SIZING:\r
5472     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5473     if (hwnd == hwndMain) {\r
5474       lastSizing = wParam;\r
5475     }\r
5476     break;\r
5477 \r
5478   case WM_MOVING:\r
5479     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5480       return OnMoving( &sd, hwnd, wParam, lParam );\r
5481 \r
5482   case WM_EXITSIZEMOVE:\r
5483     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5484     if (hwnd == hwndMain) {\r
5485       RECT client;\r
5486       doingSizing = FALSE;\r
5487       InvalidateRect(hwnd, &boardRect, FALSE);\r
5488       GetClientRect(hwnd, &client);\r
5489       ResizeBoard(client.right, client.bottom, lastSizing);\r
5490       lastSizing = 0;\r
5491       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5492     }\r
5493     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5494     break;\r
5495 \r
5496   case WM_DESTROY: /* message: window being destroyed */\r
5497     PostQuitMessage(0);\r
5498     break;\r
5499 \r
5500   case WM_CLOSE:\r
5501     if (hwnd == hwndMain) {\r
5502       ExitEvent(0);\r
5503     }\r
5504     break;\r
5505 \r
5506   default:      /* Passes it on if unprocessed */\r
5507     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5508   }\r
5509   return 0;\r
5510 }\r
5511 \r
5512 /*---------------------------------------------------------------------------*\\r
5513  *\r
5514  * Misc utility routines\r
5515  *\r
5516 \*---------------------------------------------------------------------------*/\r
5517 \r
5518 /*\r
5519  * Decent random number generator, at least not as bad as Windows\r
5520  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5521  */\r
5522 unsigned int randstate;\r
5523 \r
5524 int\r
5525 myrandom(void)\r
5526 {\r
5527   randstate = randstate * 1664525 + 1013904223;\r
5528   return (int) randstate & 0x7fffffff;\r
5529 }\r
5530 \r
5531 void\r
5532 mysrandom(unsigned int seed)\r
5533 {\r
5534   randstate = seed;\r
5535 }\r
5536 \r
5537 \r
5538 /* \r
5539  * returns TRUE if user selects a different color, FALSE otherwise \r
5540  */\r
5541 \r
5542 BOOL\r
5543 ChangeColor(HWND hwnd, COLORREF *which)\r
5544 {\r
5545   static BOOL firstTime = TRUE;\r
5546   static DWORD customColors[16];\r
5547   CHOOSECOLOR cc;\r
5548   COLORREF newcolor;\r
5549   int i;\r
5550   ColorClass ccl;\r
5551 \r
5552   if (firstTime) {\r
5553     /* Make initial colors in use available as custom colors */\r
5554     /* Should we put the compiled-in defaults here instead? */\r
5555     i = 0;\r
5556     customColors[i++] = lightSquareColor & 0xffffff;\r
5557     customColors[i++] = darkSquareColor & 0xffffff;\r
5558     customColors[i++] = whitePieceColor & 0xffffff;\r
5559     customColors[i++] = blackPieceColor & 0xffffff;\r
5560     customColors[i++] = highlightSquareColor & 0xffffff;\r
5561     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5562 \r
5563     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5564       customColors[i++] = textAttribs[ccl].color;\r
5565     }\r
5566     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5567     firstTime = FALSE;\r
5568   }\r
5569 \r
5570   cc.lStructSize = sizeof(cc);\r
5571   cc.hwndOwner = hwnd;\r
5572   cc.hInstance = NULL;\r
5573   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5574   cc.lpCustColors = (LPDWORD) customColors;\r
5575   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5576 \r
5577   if (!ChooseColor(&cc)) return FALSE;\r
5578 \r
5579   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5580   if (newcolor == *which) return FALSE;\r
5581   *which = newcolor;\r
5582   return TRUE;\r
5583 \r
5584   /*\r
5585   InitDrawingColors();\r
5586   InvalidateRect(hwnd, &boardRect, FALSE);\r
5587   */\r
5588 }\r
5589 \r
5590 BOOLEAN\r
5591 MyLoadSound(MySound *ms)\r
5592 {\r
5593   BOOL ok = FALSE;\r
5594   struct stat st;\r
5595   FILE *f;\r
5596 \r
5597   if (ms->data) free(ms->data);\r
5598   ms->data = NULL;\r
5599 \r
5600   switch (ms->name[0]) {\r
5601   case NULLCHAR:\r
5602     /* Silence */\r
5603     ok = TRUE;\r
5604     break;\r
5605   case '$':\r
5606     /* System sound from Control Panel.  Don't preload here. */\r
5607     ok = TRUE;\r
5608     break;\r
5609   case '!':\r
5610     if (ms->name[1] == NULLCHAR) {\r
5611       /* "!" alone = silence */\r
5612       ok = TRUE;\r
5613     } else {\r
5614       /* Builtin wave resource.  Error if not found. */\r
5615       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5616       if (h == NULL) break;\r
5617       ms->data = (void *)LoadResource(hInst, h);\r
5618       if (h == NULL) break;\r
5619       ok = TRUE;\r
5620     }\r
5621     break;\r
5622   default:\r
5623     /* .wav file.  Error if not found. */\r
5624     f = fopen(ms->name, "rb");\r
5625     if (f == NULL) break;\r
5626     if (fstat(fileno(f), &st) < 0) break;\r
5627     ms->data = malloc(st.st_size);\r
5628     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5629     fclose(f);\r
5630     ok = TRUE;\r
5631     break;\r
5632   }\r
5633   if (!ok) {\r
5634     char buf[MSG_SIZ];\r
5635       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5636     DisplayError(buf, GetLastError());\r
5637   }\r
5638   return ok;\r
5639 }\r
5640 \r
5641 BOOLEAN\r
5642 MyPlaySound(MySound *ms)\r
5643 {\r
5644   BOOLEAN ok = FALSE;\r
5645 \r
5646   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5647   switch (ms->name[0]) {\r
5648   case NULLCHAR:\r
5649         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5650     /* Silence */\r
5651     ok = TRUE;\r
5652     break;\r
5653   case '$':\r
5654     /* System sound from Control Panel (deprecated feature).\r
5655        "$" alone or an unset sound name gets default beep (still in use). */\r
5656     if (ms->name[1]) {\r
5657       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5658     }\r
5659     if (!ok) ok = MessageBeep(MB_OK);\r
5660     break; \r
5661   case '!':\r
5662     /* Builtin wave resource, or "!" alone for silence */\r
5663     if (ms->name[1]) {\r
5664       if (ms->data == NULL) return FALSE;\r
5665       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5666     } else {\r
5667       ok = TRUE;\r
5668     }\r
5669     break;\r
5670   default:\r
5671     /* .wav file.  Error if not found. */\r
5672     if (ms->data == NULL) return FALSE;\r
5673     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5674     break;\r
5675   }\r
5676   /* Don't print an error: this can happen innocently if the sound driver\r
5677      is busy; for instance, if another instance of WinBoard is playing\r
5678      a sound at about the same time. */\r
5679   return ok;\r
5680 }\r
5681 \r
5682 \r
5683 LRESULT CALLBACK\r
5684 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5685 {\r
5686   BOOL ok;\r
5687   OPENFILENAME *ofn;\r
5688   static UINT *number; /* gross that this is static */\r
5689 \r
5690   switch (message) {\r
5691   case WM_INITDIALOG: /* message: initialize dialog box */\r
5692     /* Center the dialog over the application window */\r
5693     ofn = (OPENFILENAME *) lParam;\r
5694     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5695       number = (UINT *) ofn->lCustData;\r
5696       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5697     } else {\r
5698       number = NULL;\r
5699     }\r
5700     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5701     Translate(hDlg, 1536);\r
5702     return FALSE;  /* Allow for further processing */\r
5703 \r
5704   case WM_COMMAND:\r
5705     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5706       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5707     }\r
5708     return FALSE;  /* Allow for further processing */\r
5709   }\r
5710   return FALSE;\r
5711 }\r
5712 \r
5713 UINT APIENTRY\r
5714 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5715 {\r
5716   static UINT *number;\r
5717   OPENFILENAME *ofname;\r
5718   OFNOTIFY *ofnot;\r
5719   switch (uiMsg) {\r
5720   case WM_INITDIALOG:\r
5721     Translate(hdlg, DLG_IndexNumber);\r
5722     ofname = (OPENFILENAME *)lParam;\r
5723     number = (UINT *)(ofname->lCustData);\r
5724     break;\r
5725   case WM_NOTIFY:\r
5726     ofnot = (OFNOTIFY *)lParam;\r
5727     if (ofnot->hdr.code == CDN_FILEOK) {\r
5728       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5729     }\r
5730     break;\r
5731   }\r
5732   return 0;\r
5733 }\r
5734 \r
5735 \r
5736 FILE *\r
5737 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5738                char *nameFilt, char *dlgTitle, UINT *number,\r
5739                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5740 {\r
5741   OPENFILENAME openFileName;\r
5742   char buf1[MSG_SIZ];\r
5743   FILE *f;\r
5744 \r
5745   if (fileName == NULL) fileName = buf1;\r
5746   if (defName == NULL) {\r
5747     safeStrCpy(fileName, "*.", 3 );\r
5748     strcat(fileName, defExt);\r
5749   } else {\r
5750     safeStrCpy(fileName, defName, MSG_SIZ );\r
5751   }\r
5752     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5753   if (number) *number = 0;\r
5754 \r
5755   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5756   openFileName.hwndOwner         = hwnd;\r
5757   openFileName.hInstance         = (HANDLE) hInst;\r
5758   openFileName.lpstrFilter       = nameFilt;\r
5759   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5760   openFileName.nMaxCustFilter    = 0L;\r
5761   openFileName.nFilterIndex      = 1L;\r
5762   openFileName.lpstrFile         = fileName;\r
5763   openFileName.nMaxFile          = MSG_SIZ;\r
5764   openFileName.lpstrFileTitle    = fileTitle;\r
5765   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5766   openFileName.lpstrInitialDir   = NULL;\r
5767   openFileName.lpstrTitle        = dlgTitle;\r
5768   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5769     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5770     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5771     | (oldDialog ? 0 : OFN_EXPLORER);\r
5772   openFileName.nFileOffset       = 0;\r
5773   openFileName.nFileExtension    = 0;\r
5774   openFileName.lpstrDefExt       = defExt;\r
5775   openFileName.lCustData         = (LONG) number;\r
5776   openFileName.lpfnHook          = oldDialog ?\r
5777     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5778   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5779 \r
5780   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5781                         GetOpenFileName(&openFileName)) {\r
5782     /* open the file */\r
5783     f = fopen(openFileName.lpstrFile, write);\r
5784     if (f == NULL) {\r
5785       MessageBox(hwnd, _("File open failed"), NULL,\r
5786                  MB_OK|MB_ICONEXCLAMATION);\r
5787       return NULL;\r
5788     }\r
5789   } else {\r
5790     int err = CommDlgExtendedError();\r
5791     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5792     return FALSE;\r
5793   }\r
5794   return f;\r
5795 }\r
5796 \r
5797 \r
5798 \r
5799 VOID APIENTRY\r
5800 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5801 {\r
5802   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5803 \r
5804   /*\r
5805    * Get the first pop-up menu in the menu template. This is the\r
5806    * menu that TrackPopupMenu displays.\r
5807    */\r
5808   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5809 \r
5810   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5811 \r
5812   /*\r
5813    * TrackPopup uses screen coordinates, so convert the\r
5814    * coordinates of the mouse click to screen coordinates.\r
5815    */\r
5816   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5817 \r
5818   /* Draw and track the floating pop-up menu. */\r
5819   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5820                  pt.x, pt.y, 0, hwnd, NULL);\r
5821 \r
5822   /* Destroy the menu.*/\r
5823   DestroyMenu(hmenu);\r
5824 }\r
5825    \r
5826 typedef struct {\r
5827   HWND hDlg, hText;\r
5828   int sizeX, sizeY, newSizeX, newSizeY;\r
5829   HDWP hdwp;\r
5830 } ResizeEditPlusButtonsClosure;\r
5831 \r
5832 BOOL CALLBACK\r
5833 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5834 {\r
5835   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5836   RECT rect;\r
5837   POINT pt;\r
5838 \r
5839   if (hChild == cl->hText) return TRUE;\r
5840   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5841   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5842   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5843   ScreenToClient(cl->hDlg, &pt);\r
5844   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5845     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5846   return TRUE;\r
5847 }\r
5848 \r
5849 /* Resize a dialog that has a (rich) edit field filling most of\r
5850    the top, with a row of buttons below */\r
5851 VOID\r
5852 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5853 {\r
5854   RECT rectText;\r
5855   int newTextHeight, newTextWidth;\r
5856   ResizeEditPlusButtonsClosure cl;\r
5857   \r
5858   /*if (IsIconic(hDlg)) return;*/\r
5859   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5860   \r
5861   cl.hdwp = BeginDeferWindowPos(8);\r
5862 \r
5863   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5864   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5865   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5866   if (newTextHeight < 0) {\r
5867     newSizeY += -newTextHeight;\r
5868     newTextHeight = 0;\r
5869   }\r
5870   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5871     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5872 \r
5873   cl.hDlg = hDlg;\r
5874   cl.hText = hText;\r
5875   cl.sizeX = sizeX;\r
5876   cl.sizeY = sizeY;\r
5877   cl.newSizeX = newSizeX;\r
5878   cl.newSizeY = newSizeY;\r
5879   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5880 \r
5881   EndDeferWindowPos(cl.hdwp);\r
5882 }\r
5883 \r
5884 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5885 {\r
5886     RECT    rChild, rParent;\r
5887     int     wChild, hChild, wParent, hParent;\r
5888     int     wScreen, hScreen, xNew, yNew;\r
5889     HDC     hdc;\r
5890 \r
5891     /* Get the Height and Width of the child window */\r
5892     GetWindowRect (hwndChild, &rChild);\r
5893     wChild = rChild.right - rChild.left;\r
5894     hChild = rChild.bottom - rChild.top;\r
5895 \r
5896     /* Get the Height and Width of the parent window */\r
5897     GetWindowRect (hwndParent, &rParent);\r
5898     wParent = rParent.right - rParent.left;\r
5899     hParent = rParent.bottom - rParent.top;\r
5900 \r
5901     /* Get the display limits */\r
5902     hdc = GetDC (hwndChild);\r
5903     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5904     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5905     ReleaseDC(hwndChild, hdc);\r
5906 \r
5907     /* Calculate new X position, then adjust for screen */\r
5908     xNew = rParent.left + ((wParent - wChild) /2);\r
5909     if (xNew < 0) {\r
5910         xNew = 0;\r
5911     } else if ((xNew+wChild) > wScreen) {\r
5912         xNew = wScreen - wChild;\r
5913     }\r
5914 \r
5915     /* Calculate new Y position, then adjust for screen */\r
5916     if( mode == 0 ) {\r
5917         yNew = rParent.top  + ((hParent - hChild) /2);\r
5918     }\r
5919     else {\r
5920         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5921     }\r
5922 \r
5923     if (yNew < 0) {\r
5924         yNew = 0;\r
5925     } else if ((yNew+hChild) > hScreen) {\r
5926         yNew = hScreen - hChild;\r
5927     }\r
5928 \r
5929     /* Set it, and return */\r
5930     return SetWindowPos (hwndChild, NULL,\r
5931                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5932 }\r
5933 \r
5934 /* Center one window over another */\r
5935 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5936 {\r
5937     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5938 }\r
5939 \r
5940 /*---------------------------------------------------------------------------*\\r
5941  *\r
5942  * Startup Dialog functions\r
5943  *\r
5944 \*---------------------------------------------------------------------------*/\r
5945 void\r
5946 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5947 {\r
5948   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5949 \r
5950   while (*cd != NULL) {\r
5951     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5952     cd++;\r
5953   }\r
5954 }\r
5955 \r
5956 void\r
5957 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5958 {\r
5959   char buf1[MAX_ARG_LEN];\r
5960   int len;\r
5961 \r
5962   if (str[0] == '@') {\r
5963     FILE* f = fopen(str + 1, "r");\r
5964     if (f == NULL) {\r
5965       DisplayFatalError(str + 1, errno, 2);\r
5966       return;\r
5967     }\r
5968     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5969     fclose(f);\r
5970     buf1[len] = NULLCHAR;\r
5971     str = buf1;\r
5972   }\r
5973 \r
5974   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5975 \r
5976   for (;;) {\r
5977     char buf[MSG_SIZ];\r
5978     char *end = strchr(str, '\n');\r
5979     if (end == NULL) return;\r
5980     memcpy(buf, str, end - str);\r
5981     buf[end - str] = NULLCHAR;\r
5982     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5983     str = end + 1;\r
5984   }\r
5985 }\r
5986 \r
5987 void\r
5988 SetStartupDialogEnables(HWND hDlg)\r
5989 {\r
5990   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
5991     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5992     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
5993   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5994     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
5995   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
5996     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
5997   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
5998     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
5999   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6000     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6001     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6002     IsDlgButtonChecked(hDlg, OPT_View));\r
6003 }\r
6004 \r
6005 char *\r
6006 QuoteForFilename(char *filename)\r
6007 {\r
6008   int dquote, space;\r
6009   dquote = strchr(filename, '"') != NULL;\r
6010   space = strchr(filename, ' ') != NULL;\r
6011   if (dquote || space) {\r
6012     if (dquote) {\r
6013       return "'";\r
6014     } else {\r
6015       return "\"";\r
6016     }\r
6017   } else {\r
6018     return "";\r
6019   }\r
6020 }\r
6021 \r
6022 VOID\r
6023 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6024 {\r
6025   char buf[MSG_SIZ];\r
6026   char *q;\r
6027 \r
6028   InitComboStringsFromOption(hwndCombo, nthnames);\r
6029   q = QuoteForFilename(nthcp);\r
6030     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6031   if (*nthdir != NULLCHAR) {\r
6032     q = QuoteForFilename(nthdir);\r
6033       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6034   }\r
6035   if (*nthcp == NULLCHAR) {\r
6036     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6037   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6038     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6039     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6040   }\r
6041 }\r
6042 \r
6043 LRESULT CALLBACK\r
6044 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6045 {\r
6046   char buf[MSG_SIZ];\r
6047   HANDLE hwndCombo;\r
6048   char *p;\r
6049 \r
6050   switch (message) {\r
6051   case WM_INITDIALOG:\r
6052     /* Center the dialog */\r
6053     CenterWindow (hDlg, GetDesktopWindow());\r
6054     Translate(hDlg, DLG_Startup);\r
6055     /* Initialize the dialog items */\r
6056     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6057                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6058                   firstChessProgramNames);\r
6059     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6060                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6061                   secondChessProgramNames);\r
6062     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6063     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6064       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6065     if (*appData.icsHelper != NULLCHAR) {\r
6066       char *q = QuoteForFilename(appData.icsHelper);\r
6067       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6068     }\r
6069     if (*appData.icsHost == NULLCHAR) {\r
6070       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6071       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6072     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6073       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6074       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6075     }\r
6076 \r
6077     if (appData.icsActive) {\r
6078       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6079     }\r
6080     else if (appData.noChessProgram) {\r
6081       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6082     }\r
6083     else {\r
6084       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6085     }\r
6086 \r
6087     SetStartupDialogEnables(hDlg);\r
6088     return TRUE;\r
6089 \r
6090   case WM_COMMAND:\r
6091     switch (LOWORD(wParam)) {\r
6092     case IDOK:\r
6093       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6094         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6095         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6096         p = buf;\r
6097         ParseArgs(StringGet, &p);\r
6098         safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6099         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6100         p = buf;\r
6101         ParseArgs(StringGet, &p);\r
6102         appData.noChessProgram = FALSE;\r
6103         appData.icsActive = FALSE;\r
6104       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6105         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6106         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6107         p = buf;\r
6108         ParseArgs(StringGet, &p);\r
6109         if (appData.zippyPlay) {\r
6110           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6111           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6112           p = buf;\r
6113           ParseArgs(StringGet, &p);\r
6114         }\r
6115       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6116         appData.noChessProgram = TRUE;\r
6117         appData.icsActive = FALSE;\r
6118       } else {\r
6119         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6120                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6121         return TRUE;\r
6122       }\r
6123       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6124         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6125         p = buf;\r
6126         ParseArgs(StringGet, &p);\r
6127       }\r
6128       EndDialog(hDlg, TRUE);\r
6129       return TRUE;\r
6130 \r
6131     case IDCANCEL:\r
6132       ExitEvent(0);\r
6133       return TRUE;\r
6134 \r
6135     case IDM_HELPCONTENTS:\r
6136       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6137         MessageBox (GetFocus(),\r
6138                     _("Unable to activate help"),\r
6139                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6140       }\r
6141       break;\r
6142 \r
6143     default:\r
6144       SetStartupDialogEnables(hDlg);\r
6145       break;\r
6146     }\r
6147     break;\r
6148   }\r
6149   return FALSE;\r
6150 }\r
6151 \r
6152 /*---------------------------------------------------------------------------*\\r
6153  *\r
6154  * About box dialog functions\r
6155  *\r
6156 \*---------------------------------------------------------------------------*/\r
6157 \r
6158 /* Process messages for "About" dialog box */\r
6159 LRESULT CALLBACK\r
6160 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6161 {\r
6162   switch (message) {\r
6163   case WM_INITDIALOG: /* message: initialize dialog box */\r
6164     /* Center the dialog over the application window */\r
6165     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6166     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6167     Translate(hDlg, ABOUTBOX);\r
6168     JAWS_COPYRIGHT\r
6169     return (TRUE);\r
6170 \r
6171   case WM_COMMAND: /* message: received a command */\r
6172     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6173         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6174       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6175       return (TRUE);\r
6176     }\r
6177     break;\r
6178   }\r
6179   return (FALSE);\r
6180 }\r
6181 \r
6182 /*---------------------------------------------------------------------------*\\r
6183  *\r
6184  * Comment Dialog functions\r
6185  *\r
6186 \*---------------------------------------------------------------------------*/\r
6187 \r
6188 LRESULT CALLBACK\r
6189 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6190 {\r
6191   static HANDLE hwndText = NULL;\r
6192   int len, newSizeX, newSizeY, flags;\r
6193   static int sizeX, sizeY;\r
6194   char *str;\r
6195   RECT rect;\r
6196   MINMAXINFO *mmi;\r
6197 \r
6198   switch (message) {\r
6199   case WM_INITDIALOG: /* message: initialize dialog box */\r
6200     /* Initialize the dialog items */\r
6201     Translate(hDlg, DLG_EditComment);\r
6202     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6203     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6204     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6205     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6206     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6207     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6208     SetWindowText(hDlg, commentTitle);\r
6209     if (editComment) {\r
6210       SetFocus(hwndText);\r
6211     } else {\r
6212       SetFocus(GetDlgItem(hDlg, IDOK));\r
6213     }\r
6214     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6215                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6216                 MAKELPARAM(FALSE, 0));\r
6217     /* Size and position the dialog */\r
6218     if (!commentDialog) {\r
6219       commentDialog = hDlg;\r
6220       flags = SWP_NOZORDER;\r
6221       GetClientRect(hDlg, &rect);\r
6222       sizeX = rect.right;\r
6223       sizeY = rect.bottom;\r
6224       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6225           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6226         WINDOWPLACEMENT wp;\r
6227         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6228         wp.length = sizeof(WINDOWPLACEMENT);\r
6229         wp.flags = 0;\r
6230         wp.showCmd = SW_SHOW;\r
6231         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6232         wp.rcNormalPosition.left = wpComment.x;\r
6233         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6234         wp.rcNormalPosition.top = wpComment.y;\r
6235         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6236         SetWindowPlacement(hDlg, &wp);\r
6237 \r
6238         GetClientRect(hDlg, &rect);\r
6239         newSizeX = rect.right;\r
6240         newSizeY = rect.bottom;\r
6241         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6242                               newSizeX, newSizeY);\r
6243         sizeX = newSizeX;\r
6244         sizeY = newSizeY;\r
6245       }\r
6246     }\r
6247     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );\r
6248     return FALSE;\r
6249 \r
6250   case WM_COMMAND: /* message: received a command */\r
6251     switch (LOWORD(wParam)) {\r
6252     case IDOK:\r
6253       if (editComment) {\r
6254         char *p, *q;\r
6255         /* Read changed options from the dialog box */\r
6256         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6257         len = GetWindowTextLength(hwndText);\r
6258         str = (char *) malloc(len + 1);\r
6259         GetWindowText(hwndText, str, len + 1);\r
6260         p = q = str;\r
6261         while (*q) {\r
6262           if (*q == '\r')\r
6263             q++;\r
6264           else\r
6265             *p++ = *q++;\r
6266         }\r
6267         *p = NULLCHAR;\r
6268         ReplaceComment(commentIndex, str);\r
6269         free(str);\r
6270       }\r
6271       CommentPopDown();\r
6272       return TRUE;\r
6273 \r
6274     case IDCANCEL:\r
6275     case OPT_CancelComment:\r
6276       CommentPopDown();\r
6277       return TRUE;\r
6278 \r
6279     case OPT_ClearComment:\r
6280       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6281       break;\r
6282 \r
6283     case OPT_EditComment:\r
6284       EditCommentEvent();\r
6285       return TRUE;\r
6286 \r
6287     default:\r
6288       break;\r
6289     }\r
6290     break;\r
6291 \r
6292   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6293         if( wParam == OPT_CommentText ) {\r
6294             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6295 \r
6296             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {\r
6297                 POINTL pt;\r
6298                 LRESULT index;\r
6299 \r
6300                 pt.x = LOWORD( lpMF->lParam );\r
6301                 pt.y = HIWORD( lpMF->lParam );\r
6302 \r
6303                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6304 \r
6305                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6306                 len = GetWindowTextLength(hwndText);\r
6307                 str = (char *) malloc(len + 1);\r
6308                 GetWindowText(hwndText, str, len + 1);\r
6309                 ReplaceComment(commentIndex, str);\r
6310                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6311                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6312                 free(str);\r
6313 \r
6314                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6315                 lpMF->msg = WM_USER;\r
6316 \r
6317                 return TRUE;\r
6318             }\r
6319         }\r
6320         break;\r
6321 \r
6322   case WM_SIZE:\r
6323     newSizeX = LOWORD(lParam);\r
6324     newSizeY = HIWORD(lParam);\r
6325     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6326     sizeX = newSizeX;\r
6327     sizeY = newSizeY;\r
6328     break;\r
6329 \r
6330   case WM_GETMINMAXINFO:\r
6331     /* Prevent resizing window too small */\r
6332     mmi = (MINMAXINFO *) lParam;\r
6333     mmi->ptMinTrackSize.x = 100;\r
6334     mmi->ptMinTrackSize.y = 100;\r
6335     break;\r
6336   }\r
6337   return FALSE;\r
6338 }\r
6339 \r
6340 VOID\r
6341 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6342 {\r
6343   FARPROC lpProc;\r
6344   char *p, *q;\r
6345 \r
6346   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6347 \r
6348   if (str == NULL) str = "";\r
6349   p = (char *) malloc(2 * strlen(str) + 2);\r
6350   q = p;\r
6351   while (*str) {\r
6352     if (*str == '\n') *q++ = '\r';\r
6353     *q++ = *str++;\r
6354   }\r
6355   *q = NULLCHAR;\r
6356   if (commentText != NULL) free(commentText);\r
6357 \r
6358   commentIndex = index;\r
6359   commentTitle = title;\r
6360   commentText = p;\r
6361   editComment = edit;\r
6362 \r
6363   if (commentDialog) {\r
6364     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6365     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6366   } else {\r
6367     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6368     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6369                  hwndMain, (DLGPROC)lpProc);\r
6370     FreeProcInstance(lpProc);\r
6371   }\r
6372   commentUp = TRUE;\r
6373 }\r
6374 \r
6375 \r
6376 /*---------------------------------------------------------------------------*\\r
6377  *\r
6378  * Type-in move dialog functions\r
6379  * \r
6380 \*---------------------------------------------------------------------------*/\r
6381 \r
6382 LRESULT CALLBACK\r
6383 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6384 {\r
6385   char move[MSG_SIZ];\r
6386   HWND hInput;\r
6387   ChessMove moveType;\r
6388   int fromX, fromY, toX, toY;\r
6389   char promoChar;\r
6390 \r
6391   switch (message) {\r
6392   case WM_INITDIALOG:\r
6393     move[0] = (char) lParam;\r
6394     move[1] = NULLCHAR;\r
6395     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6396     Translate(hDlg, DLG_TypeInMove);\r
6397     hInput = GetDlgItem(hDlg, OPT_Move);\r
6398     SetWindowText(hInput, move);\r
6399     SetFocus(hInput);\r
6400     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6401     return FALSE;\r
6402 \r
6403   case WM_COMMAND:\r
6404     switch (LOWORD(wParam)) {\r
6405     case IDOK:
6406       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6407       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6408       { int n; Board board;\r
6409         // [HGM] FENedit\r
6410         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
6411                 EditPositionPasteFEN(move);\r
6412                 EndDialog(hDlg, TRUE);\r
6413                 return TRUE;\r
6414         }\r
6415         // [HGM] movenum: allow move number to be typed in any mode\r
6416         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
6417           ToNrEvent(2*n-1);\r
6418           EndDialog(hDlg, TRUE);\r
6419           return TRUE;\r
6420         }\r
6421       }\r
6422       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6423         gameMode != Training) {\r
6424         DisplayMoveError(_("Displayed move is not current"));\r
6425       } else {\r
6426 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
6427         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6428           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
6429         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
6430         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6431           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6432           if (gameMode != Training)\r
6433               forwardMostMove = currentMove;\r
6434           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6435         } else {\r
6436           DisplayMoveError(_("Could not parse move"));\r
6437         }\r
6438       }\r
6439       EndDialog(hDlg, TRUE);\r
6440       return TRUE;\r
6441     case IDCANCEL:\r
6442       EndDialog(hDlg, FALSE);\r
6443       return TRUE;\r
6444     default:\r
6445       break;\r
6446     }\r
6447     break;\r
6448   }\r
6449   return FALSE;\r
6450 }\r
6451 \r
6452 VOID\r
6453 PopUpMoveDialog(char firstchar)\r
6454 {\r
6455     FARPROC lpProc;\r
6456     \r
6457     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6458         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6459         gameMode == AnalyzeMode || gameMode == EditGame || \r
6460         gameMode == EditPosition || gameMode == IcsExamining ||\r
6461         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6462         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
6463                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
6464                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
6465         gameMode == Training) {\r
6466       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6467       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6468         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6469       FreeProcInstance(lpProc);\r
6470     }\r
6471 }\r
6472 \r
6473 /*---------------------------------------------------------------------------*\\r
6474  *\r
6475  * Type-in name dialog functions\r
6476  * \r
6477 \*---------------------------------------------------------------------------*/\r
6478 \r
6479 LRESULT CALLBACK\r
6480 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6481 {\r
6482   char move[MSG_SIZ];\r
6483   HWND hInput;\r
6484 \r
6485   switch (message) {\r
6486   case WM_INITDIALOG:\r
6487     move[0] = (char) lParam;\r
6488     move[1] = NULLCHAR;\r
6489     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6490     Translate(hDlg, DLG_TypeInName);\r
6491     hInput = GetDlgItem(hDlg, OPT_Name);\r
6492     SetWindowText(hInput, move);\r
6493     SetFocus(hInput);\r
6494     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6495     return FALSE;\r
6496 \r
6497   case WM_COMMAND:\r
6498     switch (LOWORD(wParam)) {\r
6499     case IDOK:\r
6500       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6501       appData.userName = strdup(move);\r
6502       SetUserLogo();\r
6503       SetGameInfo();\r
6504       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6505         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6506         DisplayTitle(move);\r
6507       }\r
6508 \r
6509 \r
6510       EndDialog(hDlg, TRUE);\r
6511       return TRUE;\r
6512     case IDCANCEL:\r
6513       EndDialog(hDlg, FALSE);\r
6514       return TRUE;\r
6515     default:\r
6516       break;\r
6517     }\r
6518     break;\r
6519   }\r
6520   return FALSE;\r
6521 }\r
6522 \r
6523 VOID\r
6524 PopUpNameDialog(char firstchar)\r
6525 {\r
6526     FARPROC lpProc;\r
6527     \r
6528       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6529       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6530         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6531       FreeProcInstance(lpProc);\r
6532 }\r
6533 \r
6534 /*---------------------------------------------------------------------------*\\r
6535  *\r
6536  *  Error dialogs\r
6537  * \r
6538 \*---------------------------------------------------------------------------*/\r
6539 \r
6540 /* Nonmodal error box */\r
6541 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6542                              WPARAM wParam, LPARAM lParam);\r
6543 \r
6544 VOID\r
6545 ErrorPopUp(char *title, char *content)\r
6546 {\r
6547   FARPROC lpProc;\r
6548   char *p, *q;\r
6549   BOOLEAN modal = hwndMain == NULL;\r
6550 \r
6551   p = content;\r
6552   q = errorMessage;\r
6553   while (*p) {\r
6554     if (*p == '\n') {\r
6555       if (modal) {\r
6556         *q++ = ' ';\r
6557         p++;\r
6558       } else {\r
6559         *q++ = '\r';\r
6560         *q++ = *p++;\r
6561       }\r
6562     } else {\r
6563       *q++ = *p++;\r
6564     }\r
6565   }\r
6566   *q = NULLCHAR;\r
6567   strncpy(errorTitle, title, sizeof(errorTitle));\r
6568   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6569   \r
6570   if (modal) {\r
6571     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6572   } else {\r
6573     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6574     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6575                  hwndMain, (DLGPROC)lpProc);\r
6576     FreeProcInstance(lpProc);\r
6577   }\r
6578 }\r
6579 \r
6580 VOID\r
6581 ErrorPopDown()\r
6582 {\r
6583   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6584   if (errorDialog == NULL) return;\r
6585   DestroyWindow(errorDialog);\r
6586   errorDialog = NULL;\r
6587   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6588 }\r
6589 \r
6590 LRESULT CALLBACK\r
6591 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6592 {\r
6593   HANDLE hwndText;\r
6594   RECT rChild;\r
6595 \r
6596   switch (message) {\r
6597   case WM_INITDIALOG:\r
6598     GetWindowRect(hDlg, &rChild);\r
6599 \r
6600     /*\r
6601     SetWindowPos(hDlg, NULL, rChild.left,\r
6602       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6603       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6604     */\r
6605 \r
6606     /* \r
6607         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6608         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6609         and it doesn't work when you resize the dialog.\r
6610         For now, just give it a default position.\r
6611     */\r
6612     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6613     Translate(hDlg, DLG_Error);\r
6614 \r
6615     errorDialog = hDlg;\r
6616     SetWindowText(hDlg, errorTitle);\r
6617     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6618     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6619     return FALSE;\r
6620 \r
6621   case WM_COMMAND:\r
6622     switch (LOWORD(wParam)) {\r
6623     case IDOK:\r
6624     case IDCANCEL:\r
6625       if (errorDialog == hDlg) errorDialog = NULL;\r
6626       DestroyWindow(hDlg);\r
6627       return TRUE;\r
6628 \r
6629     default:\r
6630       break;\r
6631     }\r
6632     break;\r
6633   }\r
6634   return FALSE;\r
6635 }\r
6636 \r
6637 #ifdef GOTHIC\r
6638 HWND gothicDialog = NULL;\r
6639 \r
6640 LRESULT CALLBACK\r
6641 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6642 {\r
6643   HANDLE hwndText;\r
6644   RECT rChild;\r
6645   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6646 \r
6647   switch (message) {\r
6648   case WM_INITDIALOG:\r
6649     GetWindowRect(hDlg, &rChild);\r
6650 \r
6651     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6652                                                              SWP_NOZORDER);\r
6653 \r
6654     /* \r
6655         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6656         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6657         and it doesn't work when you resize the dialog.\r
6658         For now, just give it a default position.\r
6659     */\r
6660     gothicDialog = hDlg;\r
6661     SetWindowText(hDlg, errorTitle);\r
6662     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6663     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6664     return FALSE;\r
6665 \r
6666   case WM_COMMAND:\r
6667     switch (LOWORD(wParam)) {\r
6668     case IDOK:\r
6669     case IDCANCEL:\r
6670       if (errorDialog == hDlg) errorDialog = NULL;\r
6671       DestroyWindow(hDlg);\r
6672       return TRUE;\r
6673 \r
6674     default:\r
6675       break;\r
6676     }\r
6677     break;\r
6678   }\r
6679   return FALSE;\r
6680 }\r
6681 \r
6682 VOID\r
6683 GothicPopUp(char *title, VariantClass variant)\r
6684 {\r
6685   FARPROC lpProc;\r
6686   static char *lastTitle;\r
6687 \r
6688   strncpy(errorTitle, title, sizeof(errorTitle));\r
6689   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6690 \r
6691   if(lastTitle != title && gothicDialog != NULL) {\r
6692     DestroyWindow(gothicDialog);\r
6693     gothicDialog = NULL;\r
6694   }\r
6695   if(variant != VariantNormal && gothicDialog == NULL) {\r
6696     title = lastTitle;\r
6697     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6698     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6699                  hwndMain, (DLGPROC)lpProc);\r
6700     FreeProcInstance(lpProc);\r
6701   }\r
6702 }\r
6703 #endif\r
6704 \r
6705 /*---------------------------------------------------------------------------*\\r
6706  *\r
6707  *  Ics Interaction console functions\r
6708  *\r
6709 \*---------------------------------------------------------------------------*/\r
6710 \r
6711 #define HISTORY_SIZE 64\r
6712 static char *history[HISTORY_SIZE];\r
6713 int histIn = 0, histP = 0;\r
6714 \r
6715 VOID\r
6716 SaveInHistory(char *cmd)\r
6717 {\r
6718   if (history[histIn] != NULL) {\r
6719     free(history[histIn]);\r
6720     history[histIn] = NULL;\r
6721   }\r
6722   if (*cmd == NULLCHAR) return;\r
6723   history[histIn] = StrSave(cmd);\r
6724   histIn = (histIn + 1) % HISTORY_SIZE;\r
6725   if (history[histIn] != NULL) {\r
6726     free(history[histIn]);\r
6727     history[histIn] = NULL;\r
6728   }\r
6729   histP = histIn;\r
6730 }\r
6731 \r
6732 char *\r
6733 PrevInHistory(char *cmd)\r
6734 {\r
6735   int newhp;\r
6736   if (histP == histIn) {\r
6737     if (history[histIn] != NULL) free(history[histIn]);\r
6738     history[histIn] = StrSave(cmd);\r
6739   }\r
6740   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6741   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6742   histP = newhp;\r
6743   return history[histP];\r
6744 }\r
6745 \r
6746 char *\r
6747 NextInHistory()\r
6748 {\r
6749   if (histP == histIn) return NULL;\r
6750   histP = (histP + 1) % HISTORY_SIZE;\r
6751   return history[histP];   \r
6752 }\r
6753 \r
6754 HMENU\r
6755 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6756 {\r
6757   HMENU hmenu, h;\r
6758   int i = 0;\r
6759   hmenu = LoadMenu(hInst, "TextMenu");\r
6760   h = GetSubMenu(hmenu, 0);\r
6761   while (e->item) {\r
6762     if (strcmp(e->item, "-") == 0) {\r
6763       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6764     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6765       int flags = MF_STRING, j = 0;\r
6766       if (e->item[0] == '|') {\r
6767         flags |= MF_MENUBARBREAK;\r
6768         j++;\r
6769       }\r
6770       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6771       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6772     }\r
6773     e++;\r
6774     i++;\r
6775   } \r
6776   return hmenu;\r
6777 }\r
6778 \r
6779 WNDPROC consoleTextWindowProc;\r
6780 \r
6781 void\r
6782 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6783 {\r
6784   char buf[MSG_SIZ], name[MSG_SIZ];\r
6785   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6786   CHARRANGE sel;\r
6787 \r
6788   if (!getname) {\r
6789     SetWindowText(hInput, command);\r
6790     if (immediate) {\r
6791       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6792     } else {\r
6793       sel.cpMin = 999999;\r
6794       sel.cpMax = 999999;\r
6795       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6796       SetFocus(hInput);\r
6797     }\r
6798     return;\r
6799   }    \r
6800   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6801   if (sel.cpMin == sel.cpMax) {\r
6802     /* Expand to surrounding word */\r
6803     TEXTRANGE tr;\r
6804     do {\r
6805       tr.chrg.cpMax = sel.cpMin;\r
6806       tr.chrg.cpMin = --sel.cpMin;\r
6807       if (sel.cpMin < 0) break;\r
6808       tr.lpstrText = name;\r
6809       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6810     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6811     sel.cpMin++;\r
6812 \r
6813     do {\r
6814       tr.chrg.cpMin = sel.cpMax;\r
6815       tr.chrg.cpMax = ++sel.cpMax;\r
6816       tr.lpstrText = name;\r
6817       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6818     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6819     sel.cpMax--;\r
6820 \r
6821     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6822       MessageBeep(MB_ICONEXCLAMATION);\r
6823       return;\r
6824     }\r
6825     tr.chrg = sel;\r
6826     tr.lpstrText = name;\r
6827     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6828   } else {\r
6829     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6830       MessageBeep(MB_ICONEXCLAMATION);\r
6831       return;\r
6832     }\r
6833     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6834   }\r
6835   if (immediate) {\r
6836     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6837     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6838     SetWindowText(hInput, buf);\r
6839     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6840   } else {\r
6841     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6842       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6843     SetWindowText(hInput, buf);\r
6844     sel.cpMin = 999999;\r
6845     sel.cpMax = 999999;\r
6846     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6847     SetFocus(hInput);\r
6848   }\r
6849 }\r
6850 \r
6851 LRESULT CALLBACK \r
6852 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6853 {\r
6854   HWND hInput;\r
6855   CHARRANGE sel;\r
6856 \r
6857   switch (message) {\r
6858   case WM_KEYDOWN:\r
6859     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6860     switch (wParam) {\r
6861     case VK_PRIOR:\r
6862       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6863       return 0;\r
6864     case VK_NEXT:\r
6865       sel.cpMin = 999999;\r
6866       sel.cpMax = 999999;\r
6867       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6868       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6869       return 0;\r
6870     }\r
6871     break;\r
6872   case WM_CHAR:\r
6873    if(wParam != '\022') {\r
6874     if (wParam == '\t') {\r
6875       if (GetKeyState(VK_SHIFT) < 0) {\r
6876         /* shifted */\r
6877         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6878         if (buttonDesc[0].hwnd) {\r
6879           SetFocus(buttonDesc[0].hwnd);\r
6880         } else {\r
6881           SetFocus(hwndMain);\r
6882         }\r
6883       } else {\r
6884         /* unshifted */\r
6885         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6886       }\r
6887     } else {\r
6888       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6889       JAWS_DELETE( SetFocus(hInput); )\r
6890       SendMessage(hInput, message, wParam, lParam);\r
6891     }\r
6892     return 0;\r
6893    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
6894   case WM_RBUTTONDOWN:\r
6895     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6896       /* Move selection here if it was empty */\r
6897       POINT pt;\r
6898       pt.x = LOWORD(lParam);\r
6899       pt.y = HIWORD(lParam);\r
6900       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6901       if (sel.cpMin == sel.cpMax) {\r
6902         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6903         sel.cpMax = sel.cpMin;\r
6904         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6905       }\r
6906       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6907 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6908       POINT pt;\r
6909       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6910       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6911       if (sel.cpMin == sel.cpMax) {\r
6912         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6913         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6914       }\r
6915       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6916         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6917       }\r
6918       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6919       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6920       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6921       MenuPopup(hwnd, pt, hmenu, -1);\r
6922 }\r
6923     }\r
6924     return 0;\r
6925   case WM_RBUTTONUP:\r
6926     if (GetKeyState(VK_SHIFT) & ~1) {\r
6927       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6928         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6929     }\r
6930     return 0;\r
6931   case WM_PASTE:\r
6932     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6933     SetFocus(hInput);\r
6934     return SendMessage(hInput, message, wParam, lParam);\r
6935   case WM_MBUTTONDOWN:\r
6936     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6937   case WM_COMMAND:\r
6938     switch (LOWORD(wParam)) {\r
6939     case IDM_QuickPaste:\r
6940       {\r
6941         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6942         if (sel.cpMin == sel.cpMax) {\r
6943           MessageBeep(MB_ICONEXCLAMATION);\r
6944           return 0;\r
6945         }\r
6946         SendMessage(hwnd, WM_COPY, 0, 0);\r
6947         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6948         SendMessage(hInput, WM_PASTE, 0, 0);\r
6949         SetFocus(hInput);\r
6950         return 0;\r
6951       }\r
6952     case IDM_Cut:\r
6953       SendMessage(hwnd, WM_CUT, 0, 0);\r
6954       return 0;\r
6955     case IDM_Paste:\r
6956       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6957       return 0;\r
6958     case IDM_Copy:\r
6959       SendMessage(hwnd, WM_COPY, 0, 0);\r
6960       return 0;\r
6961     default:\r
6962       {\r
6963         int i = LOWORD(wParam) - IDM_CommandX;\r
6964         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6965             icsTextMenuEntry[i].command != NULL) {\r
6966           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6967                    icsTextMenuEntry[i].getname,\r
6968                    icsTextMenuEntry[i].immediate);\r
6969           return 0;\r
6970         }\r
6971       }\r
6972       break;\r
6973     }\r
6974     break;\r
6975   }\r
6976   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6977 }\r
6978 \r
6979 WNDPROC consoleInputWindowProc;\r
6980 \r
6981 LRESULT CALLBACK\r
6982 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6983 {\r
6984   char buf[MSG_SIZ];\r
6985   char *p;\r
6986   static BOOL sendNextChar = FALSE;\r
6987   static BOOL quoteNextChar = FALSE;\r
6988   InputSource *is = consoleInputSource;\r
6989   CHARFORMAT cf;\r
6990   CHARRANGE sel;\r
6991 \r
6992   switch (message) {\r
6993   case WM_CHAR:\r
6994     if (!appData.localLineEditing || sendNextChar) {\r
6995       is->buf[0] = (CHAR) wParam;\r
6996       is->count = 1;\r
6997       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6998       sendNextChar = FALSE;\r
6999       return 0;\r
7000     }\r
7001     if (quoteNextChar) {\r
7002       buf[0] = (char) wParam;\r
7003       buf[1] = NULLCHAR;\r
7004       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7005       quoteNextChar = FALSE;\r
7006       return 0;\r
7007     }\r
7008     switch (wParam) {\r
7009     case '\r':   /* Enter key */\r
7010       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7011       if (consoleEcho) SaveInHistory(is->buf);\r
7012       is->buf[is->count++] = '\n';\r
7013       is->buf[is->count] = NULLCHAR;\r
7014       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7015       if (consoleEcho) {\r
7016         ConsoleOutput(is->buf, is->count, TRUE);\r
7017       } else if (appData.localLineEditing) {\r
7018         ConsoleOutput("\n", 1, TRUE);\r
7019       }\r
7020       /* fall thru */\r
7021     case '\033': /* Escape key */\r
7022       SetWindowText(hwnd, "");\r
7023       cf.cbSize = sizeof(CHARFORMAT);\r
7024       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7025       if (consoleEcho) {\r
7026         cf.crTextColor = textAttribs[ColorNormal].color;\r
7027       } else {\r
7028         cf.crTextColor = COLOR_ECHOOFF;\r
7029       }\r
7030       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7031       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7032       return 0;\r
7033     case '\t':   /* Tab key */\r
7034       if (GetKeyState(VK_SHIFT) < 0) {\r
7035         /* shifted */\r
7036         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7037       } else {\r
7038         /* unshifted */\r
7039         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7040         if (buttonDesc[0].hwnd) {\r
7041           SetFocus(buttonDesc[0].hwnd);\r
7042         } else {\r
7043           SetFocus(hwndMain);\r
7044         }\r
7045       }\r
7046       return 0;\r
7047     case '\023': /* Ctrl+S */\r
7048       sendNextChar = TRUE;\r
7049       return 0;\r
7050     case '\021': /* Ctrl+Q */\r
7051       quoteNextChar = TRUE;\r
7052       return 0;\r
7053     JAWS_REPLAY\r
7054     default:\r
7055       break;\r
7056     }\r
7057     break;\r
7058   case WM_KEYDOWN:\r
7059     switch (wParam) {\r
7060     case VK_UP:\r
7061       GetWindowText(hwnd, buf, MSG_SIZ);\r
7062       p = PrevInHistory(buf);\r
7063       if (p != NULL) {\r
7064         SetWindowText(hwnd, p);\r
7065         sel.cpMin = 999999;\r
7066         sel.cpMax = 999999;\r
7067         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7068         return 0;\r
7069       }\r
7070       break;\r
7071     case VK_DOWN:\r
7072       p = NextInHistory();\r
7073       if (p != NULL) {\r
7074         SetWindowText(hwnd, p);\r
7075         sel.cpMin = 999999;\r
7076         sel.cpMax = 999999;\r
7077         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7078         return 0;\r
7079       }\r
7080       break;\r
7081     case VK_HOME:\r
7082     case VK_END:\r
7083       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7084       /* fall thru */\r
7085     case VK_PRIOR:\r
7086     case VK_NEXT:\r
7087       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7088       return 0;\r
7089     }\r
7090     break;\r
7091   case WM_MBUTTONDOWN:\r
7092     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7093       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7094     break;\r
7095   case WM_RBUTTONUP:\r
7096     if (GetKeyState(VK_SHIFT) & ~1) {\r
7097       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7098         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7099     } else {\r
7100       POINT pt;\r
7101       HMENU hmenu;\r
7102       hmenu = LoadMenu(hInst, "InputMenu");\r
7103       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7104       if (sel.cpMin == sel.cpMax) {\r
7105         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7106         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7107       }\r
7108       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7109         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7110       }\r
7111       pt.x = LOWORD(lParam);\r
7112       pt.y = HIWORD(lParam);\r
7113       MenuPopup(hwnd, pt, hmenu, -1);\r
7114     }\r
7115     return 0;\r
7116   case WM_COMMAND:\r
7117     switch (LOWORD(wParam)) { \r
7118     case IDM_Undo:\r
7119       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7120       return 0;\r
7121     case IDM_SelectAll:\r
7122       sel.cpMin = 0;\r
7123       sel.cpMax = -1; /*999999?*/\r
7124       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7125       return 0;\r
7126     case IDM_Cut:\r
7127       SendMessage(hwnd, WM_CUT, 0, 0);\r
7128       return 0;\r
7129     case IDM_Paste:\r
7130       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7131       return 0;\r
7132     case IDM_Copy:\r
7133       SendMessage(hwnd, WM_COPY, 0, 0);\r
7134       return 0;\r
7135     }\r
7136     break;\r
7137   }\r
7138   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7139 }\r
7140 \r
7141 #define CO_MAX  100000\r
7142 #define CO_TRIM   1000\r
7143 \r
7144 LRESULT CALLBACK\r
7145 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7146 {\r
7147   static SnapData sd;\r
7148   HWND hText, hInput;\r
7149   RECT rect;\r
7150   static int sizeX, sizeY;\r
7151   int newSizeX, newSizeY;\r
7152   MINMAXINFO *mmi;\r
7153   WORD wMask;\r
7154 \r
7155   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7156   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7157 \r
7158   switch (message) {\r
7159   case WM_NOTIFY:\r
7160     if (((NMHDR*)lParam)->code == EN_LINK)\r
7161     {\r
7162       ENLINK *pLink = (ENLINK*)lParam;\r
7163       if (pLink->msg == WM_LBUTTONUP)\r
7164       {\r
7165         TEXTRANGE tr;\r
7166 \r
7167         tr.chrg = pLink->chrg;\r
7168         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7169         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7170         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7171         free(tr.lpstrText);\r
7172       }\r
7173     }\r
7174     break;\r
7175   case WM_INITDIALOG: /* message: initialize dialog box */\r
7176     hwndConsole = hDlg;\r
7177     SetFocus(hInput);\r
7178     consoleTextWindowProc = (WNDPROC)\r
7179       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7180     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7181     consoleInputWindowProc = (WNDPROC)\r
7182       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7183     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7184     Colorize(ColorNormal, TRUE);\r
7185     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7186     ChangedConsoleFont();\r
7187     GetClientRect(hDlg, &rect);\r
7188     sizeX = rect.right;\r
7189     sizeY = rect.bottom;\r
7190     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7191         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7192       WINDOWPLACEMENT wp;\r
7193       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7194       wp.length = sizeof(WINDOWPLACEMENT);\r
7195       wp.flags = 0;\r
7196       wp.showCmd = SW_SHOW;\r
7197       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7198       wp.rcNormalPosition.left = wpConsole.x;\r
7199       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7200       wp.rcNormalPosition.top = wpConsole.y;\r
7201       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7202       SetWindowPlacement(hDlg, &wp);\r
7203     }\r
7204 \r
7205    // [HGM] Chessknight's change 2004-07-13\r
7206    else { /* Determine Defaults */\r
7207        WINDOWPLACEMENT wp;\r
7208        wpConsole.x = wpMain.width + 1;\r
7209        wpConsole.y = wpMain.y;\r
7210        wpConsole.width = screenWidth -  wpMain.width;\r
7211        wpConsole.height = wpMain.height;\r
7212        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7213        wp.length = sizeof(WINDOWPLACEMENT);\r
7214        wp.flags = 0;\r
7215        wp.showCmd = SW_SHOW;\r
7216        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7217        wp.rcNormalPosition.left = wpConsole.x;\r
7218        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7219        wp.rcNormalPosition.top = wpConsole.y;\r
7220        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7221        SetWindowPlacement(hDlg, &wp);\r
7222     }\r
7223 \r
7224    // Allow hText to highlight URLs and send notifications on them\r
7225    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7226    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7227    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7228    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
7229 \r
7230     return FALSE;\r
7231 \r
7232   case WM_SETFOCUS:\r
7233     SetFocus(hInput);\r
7234     return 0;\r
7235 \r
7236   case WM_CLOSE:\r
7237     ExitEvent(0);\r
7238     /* not reached */\r
7239     break;\r
7240 \r
7241   case WM_SIZE:\r
7242     if (IsIconic(hDlg)) break;\r
7243     newSizeX = LOWORD(lParam);\r
7244     newSizeY = HIWORD(lParam);\r
7245     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7246       RECT rectText, rectInput;\r
7247       POINT pt;\r
7248       int newTextHeight, newTextWidth;\r
7249       GetWindowRect(hText, &rectText);\r
7250       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7251       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7252       if (newTextHeight < 0) {\r
7253         newSizeY += -newTextHeight;\r
7254         newTextHeight = 0;\r
7255       }\r
7256       SetWindowPos(hText, NULL, 0, 0,\r
7257         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7258       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7259       pt.x = rectInput.left;\r
7260       pt.y = rectInput.top + newSizeY - sizeY;\r
7261       ScreenToClient(hDlg, &pt);\r
7262       SetWindowPos(hInput, NULL, \r
7263         pt.x, pt.y, /* needs client coords */   \r
7264         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7265         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7266     }\r
7267     sizeX = newSizeX;\r
7268     sizeY = newSizeY;\r
7269     break;\r
7270 \r
7271   case WM_GETMINMAXINFO:\r
7272     /* Prevent resizing window too small */\r
7273     mmi = (MINMAXINFO *) lParam;\r
7274     mmi->ptMinTrackSize.x = 100;\r
7275     mmi->ptMinTrackSize.y = 100;\r
7276     break;\r
7277 \r
7278   /* [AS] Snapping */\r
7279   case WM_ENTERSIZEMOVE:\r
7280     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7281 \r
7282   case WM_SIZING:\r
7283     return OnSizing( &sd, hDlg, wParam, lParam );\r
7284 \r
7285   case WM_MOVING:\r
7286     return OnMoving( &sd, hDlg, wParam, lParam );\r
7287 \r
7288   case WM_EXITSIZEMOVE:\r
7289         UpdateICSWidth(hText);\r
7290     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7291   }\r
7292 \r
7293   return DefWindowProc(hDlg, message, wParam, lParam);\r
7294 }\r
7295 \r
7296 \r
7297 VOID\r
7298 ConsoleCreate()\r
7299 {\r
7300   HWND hCons;\r
7301   if (hwndConsole) return;\r
7302   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7303   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7304 }\r
7305 \r
7306 \r
7307 VOID\r
7308 ConsoleOutput(char* data, int length, int forceVisible)\r
7309 {\r
7310   HWND hText;\r
7311   int trim, exlen;\r
7312   char *p, *q;\r
7313   char buf[CO_MAX+1];\r
7314   POINT pEnd;\r
7315   RECT rect;\r
7316   static int delayLF = 0;\r
7317   CHARRANGE savesel, sel;\r
7318 \r
7319   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7320   p = data;\r
7321   q = buf;\r
7322   if (delayLF) {\r
7323     *q++ = '\r';\r
7324     *q++ = '\n';\r
7325     delayLF = 0;\r
7326   }\r
7327   while (length--) {\r
7328     if (*p == '\n') {\r
7329       if (*++p) {\r
7330         *q++ = '\r';\r
7331         *q++ = '\n';\r
7332       } else {\r
7333         delayLF = 1;\r
7334       }\r
7335     } else if (*p == '\007') {\r
7336        MyPlaySound(&sounds[(int)SoundBell]);\r
7337        p++;\r
7338     } else {\r
7339       *q++ = *p++;\r
7340     }\r
7341   }\r
7342   *q = NULLCHAR;\r
7343   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7344   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7345   /* Save current selection */\r
7346   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7347   exlen = GetWindowTextLength(hText);\r
7348   /* Find out whether current end of text is visible */\r
7349   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7350   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7351   /* Trim existing text if it's too long */\r
7352   if (exlen + (q - buf) > CO_MAX) {\r
7353     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7354     sel.cpMin = 0;\r
7355     sel.cpMax = trim;\r
7356     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7357     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7358     exlen -= trim;\r
7359     savesel.cpMin -= trim;\r
7360     savesel.cpMax -= trim;\r
7361     if (exlen < 0) exlen = 0;\r
7362     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7363     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7364   }\r
7365   /* Append the new text */\r
7366   sel.cpMin = exlen;\r
7367   sel.cpMax = exlen;\r
7368   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7369   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7370   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7371   if (forceVisible || exlen == 0 ||\r
7372       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7373        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7374     /* Scroll to make new end of text visible if old end of text\r
7375        was visible or new text is an echo of user typein */\r
7376     sel.cpMin = 9999999;\r
7377     sel.cpMax = 9999999;\r
7378     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7379     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7380     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7381     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7382   }\r
7383   if (savesel.cpMax == exlen || forceVisible) {\r
7384     /* Move insert point to new end of text if it was at the old\r
7385        end of text or if the new text is an echo of user typein */\r
7386     sel.cpMin = 9999999;\r
7387     sel.cpMax = 9999999;\r
7388     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7389   } else {\r
7390     /* Restore previous selection */\r
7391     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7392   }\r
7393   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7394 }\r
7395 \r
7396 /*---------*/\r
7397 \r
7398 \r
7399 void\r
7400 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7401 {\r
7402   char buf[100];\r
7403   char *str;\r
7404   COLORREF oldFg, oldBg;\r
7405   HFONT oldFont;\r
7406   RECT rect;\r
7407 \r
7408   if(copyNumber > 1)
7409     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7410 \r
7411   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7412   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7413   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7414 \r
7415   rect.left = x;\r
7416   rect.right = x + squareSize;\r
7417   rect.top  = y;\r
7418   rect.bottom = y + squareSize;\r
7419   str = buf;\r
7420 \r
7421   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7422                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7423              y, ETO_CLIPPED|ETO_OPAQUE,\r
7424              &rect, str, strlen(str), NULL);\r
7425 \r
7426   (void) SetTextColor(hdc, oldFg);\r
7427   (void) SetBkColor(hdc, oldBg);\r
7428   (void) SelectObject(hdc, oldFont);\r
7429 }\r
7430 \r
7431 void\r
7432 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7433               RECT *rect, char *color, char *flagFell)\r
7434 {\r
7435   char buf[100];\r
7436   char *str;\r
7437   COLORREF oldFg, oldBg;\r
7438   HFONT oldFont;\r
7439 \r
7440   if (appData.clockMode) {\r
7441     if (tinyLayout)\r
7442       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7443     else\r
7444       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7445     str = buf;\r
7446   } else {\r
7447     str = color;\r
7448   }\r
7449 \r
7450   if (highlight) {\r
7451     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7452     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7453   } else {\r
7454     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7455     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7456   }\r
7457   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7458 \r
7459   JAWS_SILENCE\r
7460 \r
7461   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7462              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7463              rect, str, strlen(str), NULL);\r
7464   if(logoHeight > 0 && appData.clockMode) {\r
7465       RECT r;\r
7466       str += strlen(color)+2;\r
7467       r.top = rect->top + logoHeight/2;\r
7468       r.left = rect->left;\r
7469       r.right = rect->right;\r
7470       r.bottom = rect->bottom;\r
7471       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7472                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7473                  &r, str, strlen(str), NULL);\r
7474   }\r
7475   (void) SetTextColor(hdc, oldFg);\r
7476   (void) SetBkColor(hdc, oldBg);\r
7477   (void) SelectObject(hdc, oldFont);\r
7478 }\r
7479 \r
7480 \r
7481 int\r
7482 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7483            OVERLAPPED *ovl)\r
7484 {\r
7485   int ok, err;\r
7486 \r
7487   /* [AS]  */\r
7488   if( count <= 0 ) {\r
7489     if (appData.debugMode) {\r
7490       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7491     }\r
7492 \r
7493     return ERROR_INVALID_USER_BUFFER;\r
7494   }\r
7495 \r
7496   ResetEvent(ovl->hEvent);\r
7497   ovl->Offset = ovl->OffsetHigh = 0;\r
7498   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7499   if (ok) {\r
7500     err = NO_ERROR;\r
7501   } else {\r
7502     err = GetLastError();\r
7503     if (err == ERROR_IO_PENDING) {\r
7504       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7505       if (ok)\r
7506         err = NO_ERROR;\r
7507       else\r
7508         err = GetLastError();\r
7509     }\r
7510   }\r
7511   return err;\r
7512 }\r
7513 \r
7514 int\r
7515 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7516             OVERLAPPED *ovl)\r
7517 {\r
7518   int ok, err;\r
7519 \r
7520   ResetEvent(ovl->hEvent);\r
7521   ovl->Offset = ovl->OffsetHigh = 0;\r
7522   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7523   if (ok) {\r
7524     err = NO_ERROR;\r
7525   } else {\r
7526     err = GetLastError();\r
7527     if (err == ERROR_IO_PENDING) {\r
7528       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7529       if (ok)\r
7530         err = NO_ERROR;\r
7531       else\r
7532         err = GetLastError();\r
7533     }\r
7534   }\r
7535   return err;\r
7536 }\r
7537 \r
7538 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7539 void CheckForInputBufferFull( InputSource * is )\r
7540 {\r
7541     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7542         /* Look for end of line */\r
7543         char * p = is->buf;\r
7544         \r
7545         while( p < is->next && *p != '\n' ) {\r
7546             p++;\r
7547         }\r
7548 \r
7549         if( p >= is->next ) {\r
7550             if (appData.debugMode) {\r
7551                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7552             }\r
7553 \r
7554             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7555             is->count = (DWORD) -1;\r
7556             is->next = is->buf;\r
7557         }\r
7558     }\r
7559 }\r
7560 \r
7561 DWORD\r
7562 InputThread(LPVOID arg)\r
7563 {\r
7564   InputSource *is;\r
7565   OVERLAPPED ovl;\r
7566 \r
7567   is = (InputSource *) arg;\r
7568   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7569   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7570   while (is->hThread != NULL) {\r
7571     is->error = DoReadFile(is->hFile, is->next,\r
7572                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7573                            &is->count, &ovl);\r
7574     if (is->error == NO_ERROR) {\r
7575       is->next += is->count;\r
7576     } else {\r
7577       if (is->error == ERROR_BROKEN_PIPE) {\r
7578         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7579         is->count = 0;\r
7580       } else {\r
7581         is->count = (DWORD) -1;\r
7582         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7583         break; \r
7584       }\r
7585     }\r
7586 \r
7587     CheckForInputBufferFull( is );\r
7588 \r
7589     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7590 \r
7591     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7592 \r
7593     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7594   }\r
7595 \r
7596   CloseHandle(ovl.hEvent);\r
7597   CloseHandle(is->hFile);\r
7598 \r
7599   if (appData.debugMode) {\r
7600     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7601   }\r
7602 \r
7603   return 0;\r
7604 }\r
7605 \r
7606 \r
7607 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7608 DWORD\r
7609 NonOvlInputThread(LPVOID arg)\r
7610 {\r
7611   InputSource *is;\r
7612   char *p, *q;\r
7613   int i;\r
7614   char prev;\r
7615 \r
7616   is = (InputSource *) arg;\r
7617   while (is->hThread != NULL) {\r
7618     is->error = ReadFile(is->hFile, is->next,\r
7619                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7620                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7621     if (is->error == NO_ERROR) {\r
7622       /* Change CRLF to LF */\r
7623       if (is->next > is->buf) {\r
7624         p = is->next - 1;\r
7625         i = is->count + 1;\r
7626       } else {\r
7627         p = is->next;\r
7628         i = is->count;\r
7629       }\r
7630       q = p;\r
7631       prev = NULLCHAR;\r
7632       while (i > 0) {\r
7633         if (prev == '\r' && *p == '\n') {\r
7634           *(q-1) = '\n';\r
7635           is->count--;\r
7636         } else { \r
7637           *q++ = *p;\r
7638         }\r
7639         prev = *p++;\r
7640         i--;\r
7641       }\r
7642       *q = NULLCHAR;\r
7643       is->next = q;\r
7644     } else {\r
7645       if (is->error == ERROR_BROKEN_PIPE) {\r
7646         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7647         is->count = 0; \r
7648       } else {\r
7649         is->count = (DWORD) -1;\r
7650       }\r
7651     }\r
7652 \r
7653     CheckForInputBufferFull( is );\r
7654 \r
7655     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7656 \r
7657     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7658 \r
7659     if (is->count < 0) break;  /* Quit on error */\r
7660   }\r
7661   CloseHandle(is->hFile);\r
7662   return 0;\r
7663 }\r
7664 \r
7665 DWORD\r
7666 SocketInputThread(LPVOID arg)\r
7667 {\r
7668   InputSource *is;\r
7669 \r
7670   is = (InputSource *) arg;\r
7671   while (is->hThread != NULL) {\r
7672     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7673     if ((int)is->count == SOCKET_ERROR) {\r
7674       is->count = (DWORD) -1;\r
7675       is->error = WSAGetLastError();\r
7676     } else {\r
7677       is->error = NO_ERROR;\r
7678       is->next += is->count;\r
7679       if (is->count == 0 && is->second == is) {\r
7680         /* End of file on stderr; quit with no message */\r
7681         break;\r
7682       }\r
7683     }\r
7684     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7685 \r
7686     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7687 \r
7688     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7689   }\r
7690   return 0;\r
7691 }\r
7692 \r
7693 VOID\r
7694 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7695 {\r
7696   InputSource *is;\r
7697 \r
7698   is = (InputSource *) lParam;\r
7699   if (is->lineByLine) {\r
7700     /* Feed in lines one by one */\r
7701     char *p = is->buf;\r
7702     char *q = p;\r
7703     while (q < is->next) {\r
7704       if (*q++ == '\n') {\r
7705         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7706         p = q;\r
7707       }\r
7708     }\r
7709     \r
7710     /* Move any partial line to the start of the buffer */\r
7711     q = is->buf;\r
7712     while (p < is->next) {\r
7713       *q++ = *p++;\r
7714     }\r
7715     is->next = q;\r
7716 \r
7717     if (is->error != NO_ERROR || is->count == 0) {\r
7718       /* Notify backend of the error.  Note: If there was a partial\r
7719          line at the end, it is not flushed through. */\r
7720       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7721     }\r
7722   } else {\r
7723     /* Feed in the whole chunk of input at once */\r
7724     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7725     is->next = is->buf;\r
7726   }\r
7727 }\r
7728 \r
7729 /*---------------------------------------------------------------------------*\\r
7730  *\r
7731  *  Menu enables. Used when setting various modes.\r
7732  *\r
7733 \*---------------------------------------------------------------------------*/\r
7734 \r
7735 typedef struct {\r
7736   int item;\r
7737   int flags;\r
7738 } Enables;\r
7739 \r
7740 VOID\r
7741 GreyRevert(Boolean grey)\r
7742 { // [HGM] vari: for retracting variations in local mode\r
7743   HMENU hmenu = GetMenu(hwndMain);\r
7744   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7745   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7746 }\r
7747 \r
7748 VOID\r
7749 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7750 {\r
7751   while (enab->item > 0) {\r
7752     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7753     enab++;\r
7754   }\r
7755 }\r
7756 \r
7757 Enables gnuEnables[] = {\r
7758   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7759   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7760   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7761   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7762   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7763   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7764   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7765   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7766   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7767   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7768   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7769   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7770   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7771   { -1, -1 }\r
7772 };\r
7773 \r
7774 Enables icsEnables[] = {\r
7775   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7776   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7777   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7778   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7779   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7780   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7781   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7782   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7783   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7784   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7785   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7786   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7787   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7788   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7789   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7790   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7791   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7792   { -1, -1 }\r
7793 };\r
7794 \r
7795 #if ZIPPY\r
7796 Enables zippyEnables[] = {\r
7797   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7798   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7799   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7800   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7801   { -1, -1 }\r
7802 };\r
7803 #endif\r
7804 \r
7805 Enables ncpEnables[] = {\r
7806   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7807   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7808   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7809   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7810   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7811   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7812   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7813   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7814   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7815   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7816   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7817   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7818   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7819   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7820   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7821   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7822   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7823   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7824   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7825   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7826   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7827   { -1, -1 }\r
7828 };\r
7829 \r
7830 Enables trainingOnEnables[] = {\r
7831   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7832   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7833   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7834   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7835   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7836   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7837   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7838   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7839   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7840   { -1, -1 }\r
7841 };\r
7842 \r
7843 Enables trainingOffEnables[] = {\r
7844   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7845   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7846   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7847   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7848   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7849   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7850   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7851   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7852   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7853   { -1, -1 }\r
7854 };\r
7855 \r
7856 /* These modify either ncpEnables or gnuEnables */\r
7857 Enables cmailEnables[] = {\r
7858   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7859   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7860   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7861   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7862   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7863   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7864   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7865   { -1, -1 }\r
7866 };\r
7867 \r
7868 Enables machineThinkingEnables[] = {\r
7869   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7870   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7871   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7872   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7873   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7874   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7875   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7876   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7877   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7878   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7879   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7880   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7881   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7882   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7883   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7884   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7885   { -1, -1 }\r
7886 };\r
7887 \r
7888 Enables userThinkingEnables[] = {\r
7889   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7890   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7891   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7892   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7893   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7894   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7895   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7896   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7897   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7898   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7899   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7900   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7901   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7902   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7903   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7904   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7905   { -1, -1 }\r
7906 };\r
7907 \r
7908 /*---------------------------------------------------------------------------*\\r
7909  *\r
7910  *  Front-end interface functions exported by XBoard.\r
7911  *  Functions appear in same order as prototypes in frontend.h.\r
7912  * \r
7913 \*---------------------------------------------------------------------------*/\r
7914 VOID\r
7915 ModeHighlight()\r
7916 {\r
7917   static UINT prevChecked = 0;\r
7918   static int prevPausing = 0;\r
7919   UINT nowChecked;\r
7920 \r
7921   if (pausing != prevPausing) {\r
7922     prevPausing = pausing;\r
7923     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7924                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7925     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7926   }\r
7927 \r
7928   switch (gameMode) {\r
7929   case BeginningOfGame:\r
7930     if (appData.icsActive)\r
7931       nowChecked = IDM_IcsClient;\r
7932     else if (appData.noChessProgram)\r
7933       nowChecked = IDM_EditGame;\r
7934     else\r
7935       nowChecked = IDM_MachineBlack;\r
7936     break;\r
7937   case MachinePlaysBlack:\r
7938     nowChecked = IDM_MachineBlack;\r
7939     break;\r
7940   case MachinePlaysWhite:\r
7941     nowChecked = IDM_MachineWhite;\r
7942     break;\r
7943   case TwoMachinesPlay:\r
7944     nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match\r
7945     break;\r
7946   case AnalyzeMode:\r
7947     nowChecked = IDM_AnalysisMode;\r
7948     break;\r
7949   case AnalyzeFile:\r
7950     nowChecked = IDM_AnalyzeFile;\r
7951     break;\r
7952   case EditGame:\r
7953     nowChecked = IDM_EditGame;\r
7954     break;\r
7955   case PlayFromGameFile:\r
7956     nowChecked = IDM_LoadGame;\r
7957     break;\r
7958   case EditPosition:\r
7959     nowChecked = IDM_EditPosition;\r
7960     break;\r
7961   case Training:\r
7962     nowChecked = IDM_Training;\r
7963     break;\r
7964   case IcsPlayingWhite:\r
7965   case IcsPlayingBlack:\r
7966   case IcsObserving:\r
7967   case IcsIdle:\r
7968     nowChecked = IDM_IcsClient;\r
7969     break;\r
7970   default:\r
7971   case EndOfGame:\r
7972     nowChecked = 0;\r
7973     break;\r
7974   }\r
7975   if (prevChecked != 0)\r
7976     (void) CheckMenuItem(GetMenu(hwndMain),\r
7977                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7978   if (nowChecked != 0)\r
7979     (void) CheckMenuItem(GetMenu(hwndMain),\r
7980                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
7981 \r
7982   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7983     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7984                           MF_BYCOMMAND|MF_ENABLED);\r
7985   } else {\r
7986     (void) EnableMenuItem(GetMenu(hwndMain), \r
7987                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7988   }\r
7989 \r
7990   prevChecked = nowChecked;\r
7991 \r
7992   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
7993   if (appData.icsActive) {\r
7994        if (appData.icsEngineAnalyze) {\r
7995                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7996                        MF_BYCOMMAND|MF_CHECKED);\r
7997        } else {\r
7998                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7999                        MF_BYCOMMAND|MF_UNCHECKED);\r
8000        }\r
8001   }\r
8002 }\r
8003 \r
8004 VOID\r
8005 SetICSMode()\r
8006 {\r
8007   HMENU hmenu = GetMenu(hwndMain);\r
8008   SetMenuEnables(hmenu, icsEnables);\r
8009   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8010     MF_BYPOSITION|MF_ENABLED);\r
8011 #if ZIPPY\r
8012   if (appData.zippyPlay) {\r
8013     SetMenuEnables(hmenu, zippyEnables);\r
8014     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8015          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8016           MF_BYCOMMAND|MF_ENABLED);\r
8017   }\r
8018 #endif\r
8019 }\r
8020 \r
8021 VOID\r
8022 SetGNUMode()\r
8023 {\r
8024   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8025 }\r
8026 \r
8027 VOID\r
8028 SetNCPMode()\r
8029 {\r
8030   HMENU hmenu = GetMenu(hwndMain);\r
8031   SetMenuEnables(hmenu, ncpEnables);\r
8032   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8033     MF_BYPOSITION|MF_GRAYED);\r
8034     DrawMenuBar(hwndMain);\r
8035 }\r
8036 \r
8037 VOID\r
8038 SetCmailMode()\r
8039 {\r
8040   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8041 }\r
8042 \r
8043 VOID \r
8044 SetTrainingModeOn()\r
8045 {\r
8046   int i;\r
8047   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8048   for (i = 0; i < N_BUTTONS; i++) {\r
8049     if (buttonDesc[i].hwnd != NULL)\r
8050       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8051   }\r
8052   CommentPopDown();\r
8053 }\r
8054 \r
8055 VOID SetTrainingModeOff()\r
8056 {\r
8057   int i;\r
8058   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8059   for (i = 0; i < N_BUTTONS; i++) {\r
8060     if (buttonDesc[i].hwnd != NULL)\r
8061       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8062   }\r
8063 }\r
8064 \r
8065 \r
8066 VOID\r
8067 SetUserThinkingEnables()\r
8068 {\r
8069   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8070 }\r
8071 \r
8072 VOID\r
8073 SetMachineThinkingEnables()\r
8074 {\r
8075   HMENU hMenu = GetMenu(hwndMain);\r
8076   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8077 \r
8078   SetMenuEnables(hMenu, machineThinkingEnables);\r
8079 \r
8080   if (gameMode == MachinePlaysBlack) {\r
8081     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8082   } else if (gameMode == MachinePlaysWhite) {\r
8083     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8084   } else if (gameMode == TwoMachinesPlay) {\r
8085     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8086   }\r
8087 }\r
8088 \r
8089 \r
8090 VOID\r
8091 DisplayTitle(char *str)\r
8092 {\r
8093   char title[MSG_SIZ], *host;\r
8094   if (str[0] != NULLCHAR) {\r
8095     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8096   } else if (appData.icsActive) {\r
8097     if (appData.icsCommPort[0] != NULLCHAR)\r
8098       host = "ICS";\r
8099     else \r
8100       host = appData.icsHost;\r
8101       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8102   } else if (appData.noChessProgram) {\r
8103     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8104   } else {\r
8105     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8106     strcat(title, ": ");\r
8107     strcat(title, first.tidy);\r
8108   }\r
8109   SetWindowText(hwndMain, title);\r
8110 }\r
8111 \r
8112 \r
8113 VOID\r
8114 DisplayMessage(char *str1, char *str2)\r
8115 {\r
8116   HDC hdc;\r
8117   HFONT oldFont;\r
8118   int remain = MESSAGE_TEXT_MAX - 1;\r
8119   int len;\r
8120 \r
8121   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8122   messageText[0] = NULLCHAR;\r
8123   if (*str1) {\r
8124     len = strlen(str1);\r
8125     if (len > remain) len = remain;\r
8126     strncpy(messageText, str1, len);\r
8127     messageText[len] = NULLCHAR;\r
8128     remain -= len;\r
8129   }\r
8130   if (*str2 && remain >= 2) {\r
8131     if (*str1) {\r
8132       strcat(messageText, "  ");\r
8133       remain -= 2;\r
8134     }\r
8135     len = strlen(str2);\r
8136     if (len > remain) len = remain;\r
8137     strncat(messageText, str2, len);\r
8138   }\r
8139   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8140 \r
8141   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8142 \r
8143   SAYMACHINEMOVE();\r
8144 \r
8145   hdc = GetDC(hwndMain);\r
8146   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8147   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8148              &messageRect, messageText, strlen(messageText), NULL);\r
8149   (void) SelectObject(hdc, oldFont);\r
8150   (void) ReleaseDC(hwndMain, hdc);\r
8151 }\r
8152 \r
8153 VOID\r
8154 DisplayError(char *str, int error)\r
8155 {\r
8156   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8157   int len;\r
8158 \r
8159   if (error == 0) {\r
8160     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8161   } else {\r
8162     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8163                         NULL, error, LANG_NEUTRAL,\r
8164                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8165     if (len > 0) {\r
8166       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8167     } else {\r
8168       ErrorMap *em = errmap;\r
8169       while (em->err != 0 && em->err != error) em++;\r
8170       if (em->err != 0) {\r
8171         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8172       } else {\r
8173         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8174       }\r
8175     }\r
8176   }\r
8177   \r
8178   ErrorPopUp(_("Error"), buf);\r
8179 }\r
8180 \r
8181 \r
8182 VOID\r
8183 DisplayMoveError(char *str)\r
8184 {\r
8185   fromX = fromY = -1;\r
8186   ClearHighlights();\r
8187   DrawPosition(FALSE, NULL);\r
8188   if (appData.popupMoveErrors) {\r
8189     ErrorPopUp(_("Error"), str);\r
8190   } else {\r
8191     DisplayMessage(str, "");\r
8192     moveErrorMessageUp = TRUE;\r
8193   }\r
8194 }\r
8195 \r
8196 VOID\r
8197 DisplayFatalError(char *str, int error, int exitStatus)\r
8198 {\r
8199   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8200   int len;\r
8201   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8202 \r
8203   if (error != 0) {\r
8204     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8205                         NULL, error, LANG_NEUTRAL,\r
8206                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8207     if (len > 0) {\r
8208       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8209     } else {\r
8210       ErrorMap *em = errmap;\r
8211       while (em->err != 0 && em->err != error) em++;\r
8212       if (em->err != 0) {\r
8213         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8214       } else {\r
8215         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8216       }\r
8217     }\r
8218     str = buf;\r
8219   }\r
8220   if (appData.debugMode) {\r
8221     fprintf(debugFP, "%s: %s\n", label, str);\r
8222   }\r
8223   if (appData.popupExitMessage) {\r
8224     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8225                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8226   }\r
8227   ExitEvent(exitStatus);\r
8228 }\r
8229 \r
8230 \r
8231 VOID\r
8232 DisplayInformation(char *str)\r
8233 {\r
8234   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8235 }\r
8236 \r
8237 \r
8238 VOID\r
8239 DisplayNote(char *str)\r
8240 {\r
8241   ErrorPopUp(_("Note"), str);\r
8242 }\r
8243 \r
8244 \r
8245 typedef struct {\r
8246   char *title, *question, *replyPrefix;\r
8247   ProcRef pr;\r
8248 } QuestionParams;\r
8249 \r
8250 LRESULT CALLBACK\r
8251 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8252 {\r
8253   static QuestionParams *qp;\r
8254   char reply[MSG_SIZ];\r
8255   int len, err;\r
8256 \r
8257   switch (message) {\r
8258   case WM_INITDIALOG:\r
8259     qp = (QuestionParams *) lParam;\r
8260     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8261     Translate(hDlg, DLG_Question);\r
8262     SetWindowText(hDlg, qp->title);\r
8263     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8264     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8265     return FALSE;\r
8266 \r
8267   case WM_COMMAND:\r
8268     switch (LOWORD(wParam)) {\r
8269     case IDOK:\r
8270       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8271       if (*reply) strcat(reply, " ");\r
8272       len = strlen(reply);\r
8273       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8274       strcat(reply, "\n");\r
8275       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8276       EndDialog(hDlg, TRUE);\r
8277       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8278       return TRUE;\r
8279     case IDCANCEL:\r
8280       EndDialog(hDlg, FALSE);\r
8281       return TRUE;\r
8282     default:\r
8283       break;\r
8284     }\r
8285     break;\r
8286   }\r
8287   return FALSE;\r
8288 }\r
8289 \r
8290 VOID\r
8291 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8292 {\r
8293     QuestionParams qp;\r
8294     FARPROC lpProc;\r
8295     \r
8296     qp.title = title;\r
8297     qp.question = question;\r
8298     qp.replyPrefix = replyPrefix;\r
8299     qp.pr = pr;\r
8300     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8301     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8302       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8303     FreeProcInstance(lpProc);\r
8304 }\r
8305 \r
8306 /* [AS] Pick FRC position */\r
8307 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8308 {\r
8309     static int * lpIndexFRC;\r
8310     BOOL index_is_ok;\r
8311     char buf[16];\r
8312 \r
8313     switch( message )\r
8314     {\r
8315     case WM_INITDIALOG:\r
8316         lpIndexFRC = (int *) lParam;\r
8317 \r
8318         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8319         Translate(hDlg, DLG_NewGameFRC);\r
8320 \r
8321         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8322         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8323         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8324         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8325 \r
8326         break;\r
8327 \r
8328     case WM_COMMAND:\r
8329         switch( LOWORD(wParam) ) {\r
8330         case IDOK:\r
8331             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8332             EndDialog( hDlg, 0 );\r
8333             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8334             return TRUE;\r
8335         case IDCANCEL:\r
8336             EndDialog( hDlg, 1 );   \r
8337             return TRUE;\r
8338         case IDC_NFG_Edit:\r
8339             if( HIWORD(wParam) == EN_CHANGE ) {\r
8340                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8341 \r
8342                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8343             }\r
8344             return TRUE;\r
8345         case IDC_NFG_Random:\r
8346           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8347             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8348             return TRUE;\r
8349         }\r
8350 \r
8351         break;\r
8352     }\r
8353 \r
8354     return FALSE;\r
8355 }\r
8356 \r
8357 int NewGameFRC()\r
8358 {\r
8359     int result;\r
8360     int index = appData.defaultFrcPosition;\r
8361     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8362 \r
8363     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8364 \r
8365     if( result == 0 ) {\r
8366         appData.defaultFrcPosition = index;\r
8367     }\r
8368 \r
8369     return result;\r
8370 }\r
8371 \r
8372 /* [AS] Game list options. Refactored by HGM */\r
8373 \r
8374 HWND gameListOptionsDialog;\r
8375 \r
8376 // low-level front-end: clear text edit / list widget\r
8377 void\r
8378 GLT_ClearList()\r
8379 {\r
8380     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8381 }\r
8382 \r
8383 // low-level front-end: clear text edit / list widget\r
8384 void\r
8385 GLT_DeSelectList()\r
8386 {\r
8387     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8388 }\r
8389 \r
8390 // low-level front-end: append line to text edit / list widget\r
8391 void\r
8392 GLT_AddToList( char *name )\r
8393 {\r
8394     if( name != 0 ) {\r
8395             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8396     }\r
8397 }\r
8398 \r
8399 // low-level front-end: get line from text edit / list widget\r
8400 Boolean\r
8401 GLT_GetFromList( int index, char *name )\r
8402 {\r
8403     if( name != 0 ) {\r
8404             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8405                 return TRUE;\r
8406     }\r
8407     return FALSE;\r
8408 }\r
8409 \r
8410 void GLT_MoveSelection( HWND hDlg, int delta )\r
8411 {\r
8412     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8413     int idx2 = idx1 + delta;\r
8414     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8415 \r
8416     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8417         char buf[128];\r
8418 \r
8419         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8420         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8421         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8422         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8423     }\r
8424 }\r
8425 \r
8426 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8427 {\r
8428     switch( message )\r
8429     {\r
8430     case WM_INITDIALOG:\r
8431         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8432         \r
8433         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8434         Translate(hDlg, DLG_GameListOptions);\r
8435 \r
8436         /* Initialize list */\r
8437         GLT_TagsToList( lpUserGLT );\r
8438 \r
8439         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8440 \r
8441         break;\r
8442 \r
8443     case WM_COMMAND:\r
8444         switch( LOWORD(wParam) ) {\r
8445         case IDOK:\r
8446             GLT_ParseList();\r
8447             EndDialog( hDlg, 0 );\r
8448             return TRUE;\r
8449         case IDCANCEL:\r
8450             EndDialog( hDlg, 1 );\r
8451             return TRUE;\r
8452 \r
8453         case IDC_GLT_Default:\r
8454             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8455             return TRUE;\r
8456 \r
8457         case IDC_GLT_Restore:\r
8458             GLT_TagsToList( appData.gameListTags );\r
8459             return TRUE;\r
8460 \r
8461         case IDC_GLT_Up:\r
8462             GLT_MoveSelection( hDlg, -1 );\r
8463             return TRUE;\r
8464 \r
8465         case IDC_GLT_Down:\r
8466             GLT_MoveSelection( hDlg, +1 );\r
8467             return TRUE;\r
8468         }\r
8469 \r
8470         break;\r
8471     }\r
8472 \r
8473     return FALSE;\r
8474 }\r
8475 \r
8476 int GameListOptions()\r
8477 {\r
8478     int result;\r
8479     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8480 \r
8481       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8482 \r
8483     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8484 \r
8485     if( result == 0 ) {\r
8486         /* [AS] Memory leak here! */\r
8487         appData.gameListTags = strdup( lpUserGLT ); \r
8488     }\r
8489 \r
8490     return result;\r
8491 }\r
8492 \r
8493 VOID\r
8494 DisplayIcsInteractionTitle(char *str)\r
8495 {\r
8496   char consoleTitle[MSG_SIZ];\r
8497 \r
8498     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8499   SetWindowText(hwndConsole, consoleTitle);\r
8500 }\r
8501 \r
8502 void\r
8503 DrawPosition(int fullRedraw, Board board)\r
8504 {\r
8505   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8506 }\r
8507 \r
8508 void NotifyFrontendLogin()\r
8509 {\r
8510         if (hwndConsole)\r
8511                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8512 }\r
8513 \r
8514 VOID\r
8515 ResetFrontEnd()\r
8516 {\r
8517   fromX = fromY = -1;\r
8518   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8519     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8520     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8521     dragInfo.lastpos = dragInfo.pos;\r
8522     dragInfo.start.x = dragInfo.start.y = -1;\r
8523     dragInfo.from = dragInfo.start;\r
8524     ReleaseCapture();\r
8525     DrawPosition(TRUE, NULL);\r
8526   }\r
8527   TagsPopDown();\r
8528 }\r
8529 \r
8530 \r
8531 VOID\r
8532 CommentPopUp(char *title, char *str)\r
8533 {\r
8534   HWND hwnd = GetActiveWindow();\r
8535   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8536   SAY(str);\r
8537   SetActiveWindow(hwnd);\r
8538 }\r
8539 \r
8540 VOID\r
8541 CommentPopDown(void)\r
8542 {\r
8543   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8544   if (commentDialog) {\r
8545     ShowWindow(commentDialog, SW_HIDE);\r
8546   }\r
8547   commentUp = FALSE;\r
8548 }\r
8549 \r
8550 VOID\r
8551 EditCommentPopUp(int index, char *title, char *str)\r
8552 {\r
8553   EitherCommentPopUp(index, title, str, TRUE);\r
8554 }\r
8555 \r
8556 \r
8557 VOID\r
8558 RingBell()\r
8559 {\r
8560   MyPlaySound(&sounds[(int)SoundMove]);\r
8561 }\r
8562 \r
8563 VOID PlayIcsWinSound()\r
8564 {\r
8565   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8566 }\r
8567 \r
8568 VOID PlayIcsLossSound()\r
8569 {\r
8570   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8571 }\r
8572 \r
8573 VOID PlayIcsDrawSound()\r
8574 {\r
8575   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8576 }\r
8577 \r
8578 VOID PlayIcsUnfinishedSound()\r
8579 {\r
8580   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8581 }\r
8582 \r
8583 VOID\r
8584 PlayAlarmSound()\r
8585 {\r
8586   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8587 }\r
8588 \r
8589 \r
8590 VOID\r
8591 EchoOn()\r
8592 {\r
8593   HWND hInput;\r
8594   consoleEcho = TRUE;\r
8595   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8596   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8597   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8598 }\r
8599 \r
8600 \r
8601 VOID\r
8602 EchoOff()\r
8603 {\r
8604   CHARFORMAT cf;\r
8605   HWND hInput;\r
8606   consoleEcho = FALSE;\r
8607   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8608   /* This works OK: set text and background both to the same color */\r
8609   cf = consoleCF;\r
8610   cf.crTextColor = COLOR_ECHOOFF;\r
8611   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8612   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8613 }\r
8614 \r
8615 /* No Raw()...? */\r
8616 \r
8617 void Colorize(ColorClass cc, int continuation)\r
8618 {\r
8619   currentColorClass = cc;\r
8620   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8621   consoleCF.crTextColor = textAttribs[cc].color;\r
8622   consoleCF.dwEffects = textAttribs[cc].effects;\r
8623   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8624 }\r
8625 \r
8626 char *\r
8627 UserName()\r
8628 {\r
8629   static char buf[MSG_SIZ];\r
8630   DWORD bufsiz = MSG_SIZ;\r
8631 \r
8632   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8633         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8634   }\r
8635   if (!GetUserName(buf, &bufsiz)) {\r
8636     /*DisplayError("Error getting user name", GetLastError());*/\r
8637     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8638   }\r
8639   return buf;\r
8640 }\r
8641 \r
8642 char *\r
8643 HostName()\r
8644 {\r
8645   static char buf[MSG_SIZ];\r
8646   DWORD bufsiz = MSG_SIZ;\r
8647 \r
8648   if (!GetComputerName(buf, &bufsiz)) {\r
8649     /*DisplayError("Error getting host name", GetLastError());*/\r
8650     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8651   }\r
8652   return buf;\r
8653 }\r
8654 \r
8655 \r
8656 int\r
8657 ClockTimerRunning()\r
8658 {\r
8659   return clockTimerEvent != 0;\r
8660 }\r
8661 \r
8662 int\r
8663 StopClockTimer()\r
8664 {\r
8665   if (clockTimerEvent == 0) return FALSE;\r
8666   KillTimer(hwndMain, clockTimerEvent);\r
8667   clockTimerEvent = 0;\r
8668   return TRUE;\r
8669 }\r
8670 \r
8671 void\r
8672 StartClockTimer(long millisec)\r
8673 {\r
8674   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8675                              (UINT) millisec, NULL);\r
8676 }\r
8677 \r
8678 void\r
8679 DisplayWhiteClock(long timeRemaining, int highlight)\r
8680 {\r
8681   HDC hdc;\r
8682   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8683 \r
8684   if(appData.noGUI) return;\r
8685   hdc = GetDC(hwndMain);\r
8686   if (!IsIconic(hwndMain)) {\r
8687     DisplayAClock(hdc, timeRemaining, highlight, \r
8688                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8689   }\r
8690   if (highlight && iconCurrent == iconBlack) {\r
8691     iconCurrent = iconWhite;\r
8692     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8693     if (IsIconic(hwndMain)) {\r
8694       DrawIcon(hdc, 2, 2, iconCurrent);\r
8695     }\r
8696   }\r
8697   (void) ReleaseDC(hwndMain, hdc);\r
8698   if (hwndConsole)\r
8699     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8700 }\r
8701 \r
8702 void\r
8703 DisplayBlackClock(long timeRemaining, int highlight)\r
8704 {\r
8705   HDC hdc;\r
8706   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8707 \r
8708   if(appData.noGUI) return;\r
8709   hdc = GetDC(hwndMain);\r
8710   if (!IsIconic(hwndMain)) {\r
8711     DisplayAClock(hdc, timeRemaining, highlight, \r
8712                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8713   }\r
8714   if (highlight && iconCurrent == iconWhite) {\r
8715     iconCurrent = iconBlack;\r
8716     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8717     if (IsIconic(hwndMain)) {\r
8718       DrawIcon(hdc, 2, 2, iconCurrent);\r
8719     }\r
8720   }\r
8721   (void) ReleaseDC(hwndMain, hdc);\r
8722   if (hwndConsole)\r
8723     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8724 }\r
8725 \r
8726 \r
8727 int\r
8728 LoadGameTimerRunning()\r
8729 {\r
8730   return loadGameTimerEvent != 0;\r
8731 }\r
8732 \r
8733 int\r
8734 StopLoadGameTimer()\r
8735 {\r
8736   if (loadGameTimerEvent == 0) return FALSE;\r
8737   KillTimer(hwndMain, loadGameTimerEvent);\r
8738   loadGameTimerEvent = 0;\r
8739   return TRUE;\r
8740 }\r
8741 \r
8742 void\r
8743 StartLoadGameTimer(long millisec)\r
8744 {\r
8745   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8746                                 (UINT) millisec, NULL);\r
8747 }\r
8748 \r
8749 void\r
8750 AutoSaveGame()\r
8751 {\r
8752   char *defName;\r
8753   FILE *f;\r
8754   char fileTitle[MSG_SIZ];\r
8755 \r
8756   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8757   f = OpenFileDialog(hwndMain, "a", defName,\r
8758                      appData.oldSaveStyle ? "gam" : "pgn",\r
8759                      GAME_FILT, \r
8760                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8761   if (f != NULL) {\r
8762     SaveGame(f, 0, "");\r
8763     fclose(f);\r
8764   }\r
8765 }\r
8766 \r
8767 \r
8768 void\r
8769 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8770 {\r
8771   if (delayedTimerEvent != 0) {\r
8772     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8773       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8774     }\r
8775     KillTimer(hwndMain, delayedTimerEvent);\r
8776     delayedTimerEvent = 0;\r
8777     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8778     delayedTimerCallback();\r
8779   }\r
8780   delayedTimerCallback = cb;\r
8781   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8782                                 (UINT) millisec, NULL);\r
8783 }\r
8784 \r
8785 DelayedEventCallback\r
8786 GetDelayedEvent()\r
8787 {\r
8788   if (delayedTimerEvent) {\r
8789     return delayedTimerCallback;\r
8790   } else {\r
8791     return NULL;\r
8792   }\r
8793 }\r
8794 \r
8795 void\r
8796 CancelDelayedEvent()\r
8797 {\r
8798   if (delayedTimerEvent) {\r
8799     KillTimer(hwndMain, delayedTimerEvent);\r
8800     delayedTimerEvent = 0;\r
8801   }\r
8802 }\r
8803 \r
8804 DWORD GetWin32Priority(int nice)\r
8805 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8806 /*\r
8807 REALTIME_PRIORITY_CLASS     0x00000100\r
8808 HIGH_PRIORITY_CLASS         0x00000080\r
8809 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8810 NORMAL_PRIORITY_CLASS       0x00000020\r
8811 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8812 IDLE_PRIORITY_CLASS         0x00000040\r
8813 */\r
8814         if (nice < -15) return 0x00000080;\r
8815         if (nice < 0)   return 0x00008000;\r
8816         if (nice == 0)  return 0x00000020;\r
8817         if (nice < 15)  return 0x00004000;\r
8818         return 0x00000040;\r
8819 }\r
8820 \r
8821 /* Start a child process running the given program.\r
8822    The process's standard output can be read from "from", and its\r
8823    standard input can be written to "to".\r
8824    Exit with fatal error if anything goes wrong.\r
8825    Returns an opaque pointer that can be used to destroy the process\r
8826    later.\r
8827 */\r
8828 int\r
8829 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8830 {\r
8831 #define BUFSIZE 4096\r
8832 \r
8833   HANDLE hChildStdinRd, hChildStdinWr,\r
8834     hChildStdoutRd, hChildStdoutWr;\r
8835   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8836   SECURITY_ATTRIBUTES saAttr;\r
8837   BOOL fSuccess;\r
8838   PROCESS_INFORMATION piProcInfo;\r
8839   STARTUPINFO siStartInfo;\r
8840   ChildProc *cp;\r
8841   char buf[MSG_SIZ];\r
8842   DWORD err;\r
8843 \r
8844   if (appData.debugMode) {\r
8845     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8846   }\r
8847 \r
8848   *pr = NoProc;\r
8849 \r
8850   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8851   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8852   saAttr.bInheritHandle = TRUE;\r
8853   saAttr.lpSecurityDescriptor = NULL;\r
8854 \r
8855   /*\r
8856    * The steps for redirecting child's STDOUT:\r
8857    *     1. Create anonymous pipe to be STDOUT for child.\r
8858    *     2. Create a noninheritable duplicate of read handle,\r
8859    *         and close the inheritable read handle.\r
8860    */\r
8861 \r
8862   /* Create a pipe for the child's STDOUT. */\r
8863   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8864     return GetLastError();\r
8865   }\r
8866 \r
8867   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8868   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8869                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8870                              FALSE,     /* not inherited */\r
8871                              DUPLICATE_SAME_ACCESS);\r
8872   if (! fSuccess) {\r
8873     return GetLastError();\r
8874   }\r
8875   CloseHandle(hChildStdoutRd);\r
8876 \r
8877   /*\r
8878    * The steps for redirecting child's STDIN:\r
8879    *     1. Create anonymous pipe to be STDIN for child.\r
8880    *     2. Create a noninheritable duplicate of write handle,\r
8881    *         and close the inheritable write handle.\r
8882    */\r
8883 \r
8884   /* Create a pipe for the child's STDIN. */\r
8885   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8886     return GetLastError();\r
8887   }\r
8888 \r
8889   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8890   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8891                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8892                              FALSE,     /* not inherited */\r
8893                              DUPLICATE_SAME_ACCESS);\r
8894   if (! fSuccess) {\r
8895     return GetLastError();\r
8896   }\r
8897   CloseHandle(hChildStdinWr);\r
8898 \r
8899   /* Arrange to (1) look in dir for the child .exe file, and\r
8900    * (2) have dir be the child's working directory.  Interpret\r
8901    * dir relative to the directory WinBoard loaded from. */\r
8902   GetCurrentDirectory(MSG_SIZ, buf);\r
8903   SetCurrentDirectory(installDir);\r
8904   SetCurrentDirectory(dir);\r
8905 \r
8906   /* Now create the child process. */\r
8907 \r
8908   siStartInfo.cb = sizeof(STARTUPINFO);\r
8909   siStartInfo.lpReserved = NULL;\r
8910   siStartInfo.lpDesktop = NULL;\r
8911   siStartInfo.lpTitle = NULL;\r
8912   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8913   siStartInfo.cbReserved2 = 0;\r
8914   siStartInfo.lpReserved2 = NULL;\r
8915   siStartInfo.hStdInput = hChildStdinRd;\r
8916   siStartInfo.hStdOutput = hChildStdoutWr;\r
8917   siStartInfo.hStdError = hChildStdoutWr;\r
8918 \r
8919   fSuccess = CreateProcess(NULL,\r
8920                            cmdLine,        /* command line */\r
8921                            NULL,           /* process security attributes */\r
8922                            NULL,           /* primary thread security attrs */\r
8923                            TRUE,           /* handles are inherited */\r
8924                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8925                            NULL,           /* use parent's environment */\r
8926                            NULL,\r
8927                            &siStartInfo, /* STARTUPINFO pointer */\r
8928                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8929 \r
8930   err = GetLastError();\r
8931   SetCurrentDirectory(buf); /* return to prev directory */\r
8932   if (! fSuccess) {\r
8933     return err;\r
8934   }\r
8935 \r
8936   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8937     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8938     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8939   }\r
8940 \r
8941   /* Close the handles we don't need in the parent */\r
8942   CloseHandle(piProcInfo.hThread);\r
8943   CloseHandle(hChildStdinRd);\r
8944   CloseHandle(hChildStdoutWr);\r
8945 \r
8946   /* Prepare return value */\r
8947   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8948   cp->kind = CPReal;\r
8949   cp->hProcess = piProcInfo.hProcess;\r
8950   cp->pid = piProcInfo.dwProcessId;\r
8951   cp->hFrom = hChildStdoutRdDup;\r
8952   cp->hTo = hChildStdinWrDup;\r
8953 \r
8954   *pr = (void *) cp;\r
8955 \r
8956   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8957      2000 where engines sometimes don't see the initial command(s)\r
8958      from WinBoard and hang.  I don't understand how that can happen,\r
8959      but the Sleep is harmless, so I've put it in.  Others have also\r
8960      reported what may be the same problem, so hopefully this will fix\r
8961      it for them too.  */\r
8962   Sleep(500);\r
8963 \r
8964   return NO_ERROR;\r
8965 }\r
8966 \r
8967 \r
8968 void\r
8969 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8970 {\r
8971   ChildProc *cp; int result;\r
8972 \r
8973   cp = (ChildProc *) pr;\r
8974   if (cp == NULL) return;\r
8975 \r
8976   switch (cp->kind) {\r
8977   case CPReal:\r
8978     /* TerminateProcess is considered harmful, so... */\r
8979     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8980     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8981     /* The following doesn't work because the chess program\r
8982        doesn't "have the same console" as WinBoard.  Maybe\r
8983        we could arrange for this even though neither WinBoard\r
8984        nor the chess program uses a console for stdio? */\r
8985     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8986 \r
8987     /* [AS] Special termination modes for misbehaving programs... */\r
8988     if( signal == 9 ) { \r
8989         result = TerminateProcess( cp->hProcess, 0 );\r
8990 \r
8991         if ( appData.debugMode) {\r
8992             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
8993         }\r
8994     }\r
8995     else if( signal == 10 ) {\r
8996         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
8997 \r
8998         if( dw != WAIT_OBJECT_0 ) {\r
8999             result = TerminateProcess( cp->hProcess, 0 );\r
9000 \r
9001             if ( appData.debugMode) {\r
9002                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9003             }\r
9004 \r
9005         }\r
9006     }\r
9007 \r
9008     CloseHandle(cp->hProcess);\r
9009     break;\r
9010 \r
9011   case CPComm:\r
9012     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9013     break;\r
9014 \r
9015   case CPSock:\r
9016     closesocket(cp->sock);\r
9017     WSACleanup();\r
9018     break;\r
9019 \r
9020   case CPRcmd:\r
9021     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9022     closesocket(cp->sock);\r
9023     closesocket(cp->sock2);\r
9024     WSACleanup();\r
9025     break;\r
9026   }\r
9027   free(cp);\r
9028 }\r
9029 \r
9030 void\r
9031 InterruptChildProcess(ProcRef pr)\r
9032 {\r
9033   ChildProc *cp;\r
9034 \r
9035   cp = (ChildProc *) pr;\r
9036   if (cp == NULL) return;\r
9037   switch (cp->kind) {\r
9038   case CPReal:\r
9039     /* The following doesn't work because the chess program\r
9040        doesn't "have the same console" as WinBoard.  Maybe\r
9041        we could arrange for this even though neither WinBoard\r
9042        nor the chess program uses a console for stdio */\r
9043     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9044     break;\r
9045 \r
9046   case CPComm:\r
9047   case CPSock:\r
9048     /* Can't interrupt */\r
9049     break;\r
9050 \r
9051   case CPRcmd:\r
9052     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9053     break;\r
9054   }\r
9055 }\r
9056 \r
9057 \r
9058 int\r
9059 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9060 {\r
9061   char cmdLine[MSG_SIZ];\r
9062 \r
9063   if (port[0] == NULLCHAR) {\r
9064     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9065   } else {\r
9066     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9067   }\r
9068   return StartChildProcess(cmdLine, "", pr);\r
9069 }\r
9070 \r
9071 \r
9072 /* Code to open TCP sockets */\r
9073 \r
9074 int\r
9075 OpenTCP(char *host, char *port, ProcRef *pr)\r
9076 {\r
9077   ChildProc *cp;\r
9078   int err;\r
9079   SOCKET s;\r
9080   struct sockaddr_in sa, mysa;\r
9081   struct hostent FAR *hp;\r
9082   unsigned short uport;\r
9083   WORD wVersionRequested;\r
9084   WSADATA wsaData;\r
9085 \r
9086   /* Initialize socket DLL */\r
9087   wVersionRequested = MAKEWORD(1, 1);\r
9088   err = WSAStartup(wVersionRequested, &wsaData);\r
9089   if (err != 0) return err;\r
9090 \r
9091   /* Make socket */\r
9092   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9093     err = WSAGetLastError();\r
9094     WSACleanup();\r
9095     return err;\r
9096   }\r
9097 \r
9098   /* Bind local address using (mostly) don't-care values.\r
9099    */\r
9100   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9101   mysa.sin_family = AF_INET;\r
9102   mysa.sin_addr.s_addr = INADDR_ANY;\r
9103   uport = (unsigned short) 0;\r
9104   mysa.sin_port = htons(uport);\r
9105   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9106       == SOCKET_ERROR) {\r
9107     err = WSAGetLastError();\r
9108     WSACleanup();\r
9109     return err;\r
9110   }\r
9111 \r
9112   /* Resolve remote host name */\r
9113   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9114   if (!(hp = gethostbyname(host))) {\r
9115     unsigned int b0, b1, b2, b3;\r
9116 \r
9117     err = WSAGetLastError();\r
9118 \r
9119     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9120       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9121       hp->h_addrtype = AF_INET;\r
9122       hp->h_length = 4;\r
9123       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9124       hp->h_addr_list[0] = (char *) malloc(4);\r
9125       hp->h_addr_list[0][0] = (char) b0;\r
9126       hp->h_addr_list[0][1] = (char) b1;\r
9127       hp->h_addr_list[0][2] = (char) b2;\r
9128       hp->h_addr_list[0][3] = (char) b3;\r
9129     } else {\r
9130       WSACleanup();\r
9131       return err;\r
9132     }\r
9133   }\r
9134   sa.sin_family = hp->h_addrtype;\r
9135   uport = (unsigned short) atoi(port);\r
9136   sa.sin_port = htons(uport);\r
9137   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9138 \r
9139   /* Make connection */\r
9140   if (connect(s, (struct sockaddr *) &sa,\r
9141               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9142     err = WSAGetLastError();\r
9143     WSACleanup();\r
9144     return err;\r
9145   }\r
9146 \r
9147   /* Prepare return value */\r
9148   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9149   cp->kind = CPSock;\r
9150   cp->sock = s;\r
9151   *pr = (ProcRef *) cp;\r
9152 \r
9153   return NO_ERROR;\r
9154 }\r
9155 \r
9156 int\r
9157 OpenCommPort(char *name, ProcRef *pr)\r
9158 {\r
9159   HANDLE h;\r
9160   COMMTIMEOUTS ct;\r
9161   ChildProc *cp;\r
9162   char fullname[MSG_SIZ];\r
9163 \r
9164   if (*name != '\\')\r
9165     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9166   else\r
9167     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9168 \r
9169   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9170                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9171   if (h == (HANDLE) -1) {\r
9172     return GetLastError();\r
9173   }\r
9174   hCommPort = h;\r
9175 \r
9176   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9177 \r
9178   /* Accumulate characters until a 100ms pause, then parse */\r
9179   ct.ReadIntervalTimeout = 100;\r
9180   ct.ReadTotalTimeoutMultiplier = 0;\r
9181   ct.ReadTotalTimeoutConstant = 0;\r
9182   ct.WriteTotalTimeoutMultiplier = 0;\r
9183   ct.WriteTotalTimeoutConstant = 0;\r
9184   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9185 \r
9186   /* Prepare return value */\r
9187   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9188   cp->kind = CPComm;\r
9189   cp->hFrom = h;\r
9190   cp->hTo = h;\r
9191   *pr = (ProcRef *) cp;\r
9192 \r
9193   return NO_ERROR;\r
9194 }\r
9195 \r
9196 int\r
9197 OpenLoopback(ProcRef *pr)\r
9198 {\r
9199   DisplayFatalError(_("Not implemented"), 0, 1);\r
9200   return NO_ERROR;\r
9201 }\r
9202 \r
9203 \r
9204 int\r
9205 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9206 {\r
9207   ChildProc *cp;\r
9208   int err;\r
9209   SOCKET s, s2, s3;\r
9210   struct sockaddr_in sa, mysa;\r
9211   struct hostent FAR *hp;\r
9212   unsigned short uport;\r
9213   WORD wVersionRequested;\r
9214   WSADATA wsaData;\r
9215   int fromPort;\r
9216   char stderrPortStr[MSG_SIZ];\r
9217 \r
9218   /* Initialize socket DLL */\r
9219   wVersionRequested = MAKEWORD(1, 1);\r
9220   err = WSAStartup(wVersionRequested, &wsaData);\r
9221   if (err != 0) return err;\r
9222 \r
9223   /* Resolve remote host name */\r
9224   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9225   if (!(hp = gethostbyname(host))) {\r
9226     unsigned int b0, b1, b2, b3;\r
9227 \r
9228     err = WSAGetLastError();\r
9229 \r
9230     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9231       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9232       hp->h_addrtype = AF_INET;\r
9233       hp->h_length = 4;\r
9234       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9235       hp->h_addr_list[0] = (char *) malloc(4);\r
9236       hp->h_addr_list[0][0] = (char) b0;\r
9237       hp->h_addr_list[0][1] = (char) b1;\r
9238       hp->h_addr_list[0][2] = (char) b2;\r
9239       hp->h_addr_list[0][3] = (char) b3;\r
9240     } else {\r
9241       WSACleanup();\r
9242       return err;\r
9243     }\r
9244   }\r
9245   sa.sin_family = hp->h_addrtype;\r
9246   uport = (unsigned short) 514;\r
9247   sa.sin_port = htons(uport);\r
9248   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9249 \r
9250   /* Bind local socket to unused "privileged" port address\r
9251    */\r
9252   s = INVALID_SOCKET;\r
9253   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9254   mysa.sin_family = AF_INET;\r
9255   mysa.sin_addr.s_addr = INADDR_ANY;\r
9256   for (fromPort = 1023;; fromPort--) {\r
9257     if (fromPort < 0) {\r
9258       WSACleanup();\r
9259       return WSAEADDRINUSE;\r
9260     }\r
9261     if (s == INVALID_SOCKET) {\r
9262       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9263         err = WSAGetLastError();\r
9264         WSACleanup();\r
9265         return err;\r
9266       }\r
9267     }\r
9268     uport = (unsigned short) fromPort;\r
9269     mysa.sin_port = htons(uport);\r
9270     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9271         == SOCKET_ERROR) {\r
9272       err = WSAGetLastError();\r
9273       if (err == WSAEADDRINUSE) continue;\r
9274       WSACleanup();\r
9275       return err;\r
9276     }\r
9277     if (connect(s, (struct sockaddr *) &sa,\r
9278       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9279       err = WSAGetLastError();\r
9280       if (err == WSAEADDRINUSE) {\r
9281         closesocket(s);\r
9282         s = -1;\r
9283         continue;\r
9284       }\r
9285       WSACleanup();\r
9286       return err;\r
9287     }\r
9288     break;\r
9289   }\r
9290 \r
9291   /* Bind stderr local socket to unused "privileged" port address\r
9292    */\r
9293   s2 = INVALID_SOCKET;\r
9294   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9295   mysa.sin_family = AF_INET;\r
9296   mysa.sin_addr.s_addr = INADDR_ANY;\r
9297   for (fromPort = 1023;; fromPort--) {\r
9298     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9299     if (fromPort < 0) {\r
9300       (void) closesocket(s);\r
9301       WSACleanup();\r
9302       return WSAEADDRINUSE;\r
9303     }\r
9304     if (s2 == INVALID_SOCKET) {\r
9305       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9306         err = WSAGetLastError();\r
9307         closesocket(s);\r
9308         WSACleanup();\r
9309         return err;\r
9310       }\r
9311     }\r
9312     uport = (unsigned short) fromPort;\r
9313     mysa.sin_port = htons(uport);\r
9314     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9315         == SOCKET_ERROR) {\r
9316       err = WSAGetLastError();\r
9317       if (err == WSAEADDRINUSE) continue;\r
9318       (void) closesocket(s);\r
9319       WSACleanup();\r
9320       return err;\r
9321     }\r
9322     if (listen(s2, 1) == SOCKET_ERROR) {\r
9323       err = WSAGetLastError();\r
9324       if (err == WSAEADDRINUSE) {\r
9325         closesocket(s2);\r
9326         s2 = INVALID_SOCKET;\r
9327         continue;\r
9328       }\r
9329       (void) closesocket(s);\r
9330       (void) closesocket(s2);\r
9331       WSACleanup();\r
9332       return err;\r
9333     }\r
9334     break;\r
9335   }\r
9336   prevStderrPort = fromPort; // remember port used\r
9337   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9338 \r
9339   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9340     err = WSAGetLastError();\r
9341     (void) closesocket(s);\r
9342     (void) closesocket(s2);\r
9343     WSACleanup();\r
9344     return err;\r
9345   }\r
9346 \r
9347   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9348     err = WSAGetLastError();\r
9349     (void) closesocket(s);\r
9350     (void) closesocket(s2);\r
9351     WSACleanup();\r
9352     return err;\r
9353   }\r
9354   if (*user == NULLCHAR) user = UserName();\r
9355   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9356     err = WSAGetLastError();\r
9357     (void) closesocket(s);\r
9358     (void) closesocket(s2);\r
9359     WSACleanup();\r
9360     return err;\r
9361   }\r
9362   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9363     err = WSAGetLastError();\r
9364     (void) closesocket(s);\r
9365     (void) closesocket(s2);\r
9366     WSACleanup();\r
9367     return err;\r
9368   }\r
9369 \r
9370   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9371     err = WSAGetLastError();\r
9372     (void) closesocket(s);\r
9373     (void) closesocket(s2);\r
9374     WSACleanup();\r
9375     return err;\r
9376   }\r
9377   (void) closesocket(s2);  /* Stop listening */\r
9378 \r
9379   /* Prepare return value */\r
9380   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9381   cp->kind = CPRcmd;\r
9382   cp->sock = s;\r
9383   cp->sock2 = s3;\r
9384   *pr = (ProcRef *) cp;\r
9385 \r
9386   return NO_ERROR;\r
9387 }\r
9388 \r
9389 \r
9390 InputSourceRef\r
9391 AddInputSource(ProcRef pr, int lineByLine,\r
9392                InputCallback func, VOIDSTAR closure)\r
9393 {\r
9394   InputSource *is, *is2 = NULL;\r
9395   ChildProc *cp = (ChildProc *) pr;\r
9396 \r
9397   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9398   is->lineByLine = lineByLine;\r
9399   is->func = func;\r
9400   is->closure = closure;\r
9401   is->second = NULL;\r
9402   is->next = is->buf;\r
9403   if (pr == NoProc) {\r
9404     is->kind = CPReal;\r
9405     consoleInputSource = is;\r
9406   } else {\r
9407     is->kind = cp->kind;\r
9408     /* \r
9409         [AS] Try to avoid a race condition if the thread is given control too early:\r
9410         we create all threads suspended so that the is->hThread variable can be\r
9411         safely assigned, then let the threads start with ResumeThread.\r
9412     */\r
9413     switch (cp->kind) {\r
9414     case CPReal:\r
9415       is->hFile = cp->hFrom;\r
9416       cp->hFrom = NULL; /* now owned by InputThread */\r
9417       is->hThread =\r
9418         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9419                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9420       break;\r
9421 \r
9422     case CPComm:\r
9423       is->hFile = cp->hFrom;\r
9424       cp->hFrom = NULL; /* now owned by InputThread */\r
9425       is->hThread =\r
9426         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9427                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9428       break;\r
9429 \r
9430     case CPSock:\r
9431       is->sock = cp->sock;\r
9432       is->hThread =\r
9433         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9434                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9435       break;\r
9436 \r
9437     case CPRcmd:\r
9438       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9439       *is2 = *is;\r
9440       is->sock = cp->sock;\r
9441       is->second = is2;\r
9442       is2->sock = cp->sock2;\r
9443       is2->second = is2;\r
9444       is->hThread =\r
9445         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9446                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9447       is2->hThread =\r
9448         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9449                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9450       break;\r
9451     }\r
9452 \r
9453     if( is->hThread != NULL ) {\r
9454         ResumeThread( is->hThread );\r
9455     }\r
9456 \r
9457     if( is2 != NULL && is2->hThread != NULL ) {\r
9458         ResumeThread( is2->hThread );\r
9459     }\r
9460   }\r
9461 \r
9462   return (InputSourceRef) is;\r
9463 }\r
9464 \r
9465 void\r
9466 RemoveInputSource(InputSourceRef isr)\r
9467 {\r
9468   InputSource *is;\r
9469 \r
9470   is = (InputSource *) isr;\r
9471   is->hThread = NULL;  /* tell thread to stop */\r
9472   CloseHandle(is->hThread);\r
9473   if (is->second != NULL) {\r
9474     is->second->hThread = NULL;\r
9475     CloseHandle(is->second->hThread);\r
9476   }\r
9477 }\r
9478 \r
9479 int no_wrap(char *message, int count)\r
9480 {\r
9481     ConsoleOutput(message, count, FALSE);\r
9482     return count;\r
9483 }\r
9484 \r
9485 int\r
9486 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9487 {\r
9488   DWORD dOutCount;\r
9489   int outCount = SOCKET_ERROR;\r
9490   ChildProc *cp = (ChildProc *) pr;\r
9491   static OVERLAPPED ovl;\r
9492   static int line = 0;\r
9493 \r
9494   if (pr == NoProc)\r
9495   {\r
9496     if (appData.noJoin || !appData.useInternalWrap)\r
9497       return no_wrap(message, count);\r
9498     else\r
9499     {\r
9500       int width = get_term_width();\r
9501       int len = wrap(NULL, message, count, width, &line);\r
9502       char *msg = malloc(len);\r
9503       int dbgchk;\r
9504 \r
9505       if (!msg)\r
9506         return no_wrap(message, count);\r
9507       else\r
9508       {\r
9509         dbgchk = wrap(msg, message, count, width, &line);\r
9510         if (dbgchk != len && appData.debugMode)\r
9511             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9512         ConsoleOutput(msg, len, FALSE);\r
9513         free(msg);\r
9514         return len;\r
9515       }\r
9516     }\r
9517   }\r
9518 \r
9519   if (ovl.hEvent == NULL) {\r
9520     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9521   }\r
9522   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9523 \r
9524   switch (cp->kind) {\r
9525   case CPSock:\r
9526   case CPRcmd:\r
9527     outCount = send(cp->sock, message, count, 0);\r
9528     if (outCount == SOCKET_ERROR) {\r
9529       *outError = WSAGetLastError();\r
9530     } else {\r
9531       *outError = NO_ERROR;\r
9532     }\r
9533     break;\r
9534 \r
9535   case CPReal:\r
9536     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9537                   &dOutCount, NULL)) {\r
9538       *outError = NO_ERROR;\r
9539       outCount = (int) dOutCount;\r
9540     } else {\r
9541       *outError = GetLastError();\r
9542     }\r
9543     break;\r
9544 \r
9545   case CPComm:\r
9546     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9547                             &dOutCount, &ovl);\r
9548     if (*outError == NO_ERROR) {\r
9549       outCount = (int) dOutCount;\r
9550     }\r
9551     break;\r
9552   }\r
9553   return outCount;\r
9554 }\r
9555 \r
9556 int\r
9557 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9558                        long msdelay)\r
9559 {\r
9560   /* Ignore delay, not implemented for WinBoard */\r
9561   return OutputToProcess(pr, message, count, outError);\r
9562 }\r
9563 \r
9564 \r
9565 void\r
9566 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9567                         char *buf, int count, int error)\r
9568 {\r
9569   DisplayFatalError(_("Not implemented"), 0, 1);\r
9570 }\r
9571 \r
9572 /* see wgamelist.c for Game List functions */\r
9573 /* see wedittags.c for Edit Tags functions */\r
9574 \r
9575 \r
9576 VOID\r
9577 ICSInitScript()\r
9578 {\r
9579   FILE *f;\r
9580   char buf[MSG_SIZ];\r
9581   char *dummy;\r
9582 \r
9583   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9584     f = fopen(buf, "r");\r
9585     if (f != NULL) {\r
9586       ProcessICSInitScript(f);\r
9587       fclose(f);\r
9588     }\r
9589   }\r
9590 }\r
9591 \r
9592 \r
9593 VOID\r
9594 StartAnalysisClock()\r
9595 {\r
9596   if (analysisTimerEvent) return;\r
9597   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9598                                         (UINT) 2000, NULL);\r
9599 }\r
9600 \r
9601 VOID\r
9602 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9603 {\r
9604   highlightInfo.sq[0].x = fromX;\r
9605   highlightInfo.sq[0].y = fromY;\r
9606   highlightInfo.sq[1].x = toX;\r
9607   highlightInfo.sq[1].y = toY;\r
9608 }\r
9609 \r
9610 VOID\r
9611 ClearHighlights()\r
9612 {\r
9613   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9614     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9615 }\r
9616 \r
9617 VOID\r
9618 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9619 {\r
9620   premoveHighlightInfo.sq[0].x = fromX;\r
9621   premoveHighlightInfo.sq[0].y = fromY;\r
9622   premoveHighlightInfo.sq[1].x = toX;\r
9623   premoveHighlightInfo.sq[1].y = toY;\r
9624 }\r
9625 \r
9626 VOID\r
9627 ClearPremoveHighlights()\r
9628 {\r
9629   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9630     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9631 }\r
9632 \r
9633 VOID\r
9634 ShutDownFrontEnd()\r
9635 {\r
9636   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9637   DeleteClipboardTempFiles();\r
9638 }\r
9639 \r
9640 void\r
9641 BoardToTop()\r
9642 {\r
9643     if (IsIconic(hwndMain))\r
9644       ShowWindow(hwndMain, SW_RESTORE);\r
9645 \r
9646     SetActiveWindow(hwndMain);\r
9647 }\r
9648 \r
9649 /*\r
9650  * Prototypes for animation support routines\r
9651  */\r
9652 static void ScreenSquare(int column, int row, POINT * pt);\r
9653 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9654      POINT frames[], int * nFrames);\r
9655 \r
9656 \r
9657 #define kFactor 4\r
9658 \r
9659 void\r
9660 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9661 {       // [HGM] atomic: animate blast wave\r
9662         int i;\r
9663 \r
9664         explodeInfo.fromX = fromX;\r
9665         explodeInfo.fromY = fromY;\r
9666         explodeInfo.toX = toX;\r
9667         explodeInfo.toY = toY;\r
9668         for(i=1; i<4*kFactor; i++) {\r
9669             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9670             DrawPosition(FALSE, board);\r
9671             Sleep(appData.animSpeed);\r
9672         }\r
9673         explodeInfo.radius = 0;\r
9674         DrawPosition(TRUE, board);\r
9675 }\r
9676 \r
9677 void\r
9678 AnimateMove(board, fromX, fromY, toX, toY)\r
9679      Board board;\r
9680      int fromX;\r
9681      int fromY;\r
9682      int toX;\r
9683      int toY;\r
9684 {\r
9685   ChessSquare piece;\r
9686   POINT start, finish, mid;\r
9687   POINT frames[kFactor * 2 + 1];\r
9688   int nFrames, n;\r
9689 \r
9690   if (!appData.animate) return;\r
9691   if (doingSizing) return;\r
9692   if (fromY < 0 || fromX < 0) return;\r
9693   piece = board[fromY][fromX];\r
9694   if (piece >= EmptySquare) return;\r
9695 \r
9696   ScreenSquare(fromX, fromY, &start);\r
9697   ScreenSquare(toX, toY, &finish);\r
9698 \r
9699   /* All moves except knight jumps move in straight line */\r
9700   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9701     mid.x = start.x + (finish.x - start.x) / 2;\r
9702     mid.y = start.y + (finish.y - start.y) / 2;\r
9703   } else {\r
9704     /* Knight: make straight movement then diagonal */\r
9705     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9706        mid.x = start.x + (finish.x - start.x) / 2;\r
9707        mid.y = start.y;\r
9708      } else {\r
9709        mid.x = start.x;\r
9710        mid.y = start.y + (finish.y - start.y) / 2;\r
9711      }\r
9712   }\r
9713   \r
9714   /* Don't use as many frames for very short moves */\r
9715   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9716     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9717   else\r
9718     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9719 \r
9720   animInfo.from.x = fromX;\r
9721   animInfo.from.y = fromY;\r
9722   animInfo.to.x = toX;\r
9723   animInfo.to.y = toY;\r
9724   animInfo.lastpos = start;\r
9725   animInfo.piece = piece;\r
9726   for (n = 0; n < nFrames; n++) {\r
9727     animInfo.pos = frames[n];\r
9728     DrawPosition(FALSE, NULL);\r
9729     animInfo.lastpos = animInfo.pos;\r
9730     Sleep(appData.animSpeed);\r
9731   }\r
9732   animInfo.pos = finish;\r
9733   DrawPosition(FALSE, NULL);\r
9734   animInfo.piece = EmptySquare;\r
9735   Explode(board, fromX, fromY, toX, toY);\r
9736 }\r
9737 \r
9738 /*      Convert board position to corner of screen rect and color       */\r
9739 \r
9740 static void\r
9741 ScreenSquare(column, row, pt)\r
9742      int column; int row; POINT * pt;\r
9743 {\r
9744   if (flipView) {\r
9745     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9746     pt->y = lineGap + row * (squareSize + lineGap);\r
9747   } else {\r
9748     pt->x = lineGap + column * (squareSize + lineGap);\r
9749     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9750   }\r
9751 }\r
9752 \r
9753 /*      Generate a series of frame coords from start->mid->finish.\r
9754         The movement rate doubles until the half way point is\r
9755         reached, then halves back down to the final destination,\r
9756         which gives a nice slow in/out effect. The algorithmn\r
9757         may seem to generate too many intermediates for short\r
9758         moves, but remember that the purpose is to attract the\r
9759         viewers attention to the piece about to be moved and\r
9760         then to where it ends up. Too few frames would be less\r
9761         noticeable.                                             */\r
9762 \r
9763 static void\r
9764 Tween(start, mid, finish, factor, frames, nFrames)\r
9765      POINT * start; POINT * mid;\r
9766      POINT * finish; int factor;\r
9767      POINT frames[]; int * nFrames;\r
9768 {\r
9769   int n, fraction = 1, count = 0;\r
9770 \r
9771   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9772   for (n = 0; n < factor; n++)\r
9773     fraction *= 2;\r
9774   for (n = 0; n < factor; n++) {\r
9775     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9776     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9777     count ++;\r
9778     fraction = fraction / 2;\r
9779   }\r
9780   \r
9781   /* Midpoint */\r
9782   frames[count] = *mid;\r
9783   count ++;\r
9784   \r
9785   /* Slow out, stepping 1/2, then 1/4, ... */\r
9786   fraction = 2;\r
9787   for (n = 0; n < factor; n++) {\r
9788     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9789     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9790     count ++;\r
9791     fraction = fraction * 2;\r
9792   }\r
9793   *nFrames = count;\r
9794 }\r
9795 \r
9796 void\r
9797 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9798 {\r
9799     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9800 \r
9801     EvalGraphSet( first, last, current, pvInfoList );\r
9802 }\r
9803 \r
9804 void\r
9805 SettingsPopUp(ChessProgramState *cps)\r
9806 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9807       EngineOptionsPopup(savedHwnd, cps);\r
9808 }\r