Divorce the Edit and Show Tags/Comment menu items
[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, 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 }, \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           }\r
888     }\r
889 }\r
890 \r
891 BOOL\r
892 InitApplication(HINSTANCE hInstance)\r
893 {\r
894   WNDCLASS wc;\r
895 \r
896   /* Fill in window class structure with parameters that describe the */\r
897   /* main window. */\r
898 \r
899   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
900   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
901   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
902   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
903   wc.hInstance     = hInstance;         /* Owner of this class */\r
904   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
905   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
906   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
907   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
908   wc.lpszClassName = szAppName;                 /* Name to register as */\r
909 \r
910   /* Register the window class and return success/failure code. */\r
911   if (!RegisterClass(&wc)) return FALSE;\r
912 \r
913   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
914   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
915   wc.cbClsExtra    = 0;\r
916   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
917   wc.hInstance     = hInstance;\r
918   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
919   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
920   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
921   wc.lpszMenuName  = NULL;\r
922   wc.lpszClassName = szConsoleName;\r
923 \r
924   if (!RegisterClass(&wc)) return FALSE;\r
925   return TRUE;\r
926 }\r
927 \r
928 \r
929 /* Set by InitInstance, used by EnsureOnScreen */\r
930 int screenHeight, screenWidth;\r
931 \r
932 void\r
933 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
934 {\r
935 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
936   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
937   if (*x > screenWidth - 32) *x = 0;\r
938   if (*y > screenHeight - 32) *y = 0;\r
939   if (*x < minX) *x = minX;\r
940   if (*y < minY) *y = minY;\r
941 }\r
942 \r
943 BOOL\r
944 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
945 {\r
946   HWND hwnd; /* Main window handle. */\r
947   int ibs;\r
948   WINDOWPLACEMENT wp;\r
949   char *filepart;\r
950 \r
951   hInst = hInstance;    /* Store instance handle in our global variable */\r
952   programName = szAppName;\r
953 \r
954   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
955     *filepart = NULLCHAR;\r
956   } else {\r
957     GetCurrentDirectory(MSG_SIZ, installDir);\r
958   }\r
959   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
960   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
961   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
962   /* xboard, and older WinBoards, controlled the move sound with the\r
963      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
964      always turn the option on (so that the backend will call us),\r
965      then let the user turn the sound off by setting it to silence if\r
966      desired.  To accommodate old winboard.ini files saved by old\r
967      versions of WinBoard, we also turn off the sound if the option\r
968      was initially set to false. [HGM] taken out of InitAppData */\r
969   if (!appData.ringBellAfterMoves) {\r
970     sounds[(int)SoundMove].name = strdup("");\r
971     appData.ringBellAfterMoves = TRUE;\r
972   }\r
973   if (appData.debugMode) {\r
974     debugFP = fopen(appData.nameOfDebugFile, "w");\r
975     setbuf(debugFP, NULL);\r
976   }\r
977 \r
978   LoadLanguageFile(appData.language);\r
979 \r
980   InitBackEnd1();\r
981 \r
982 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
983 //  InitEngineUCI( installDir, &second );\r
984 \r
985   /* Create a main window for this application instance. */\r
986   hwnd = CreateWindow(szAppName, szTitle,\r
987                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
988                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
989                       NULL, NULL, hInstance, NULL);\r
990   hwndMain = hwnd;\r
991 \r
992   /* If window could not be created, return "failure" */\r
993   if (!hwnd) {\r
994     return (FALSE);\r
995   }\r
996 \r
997   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
998   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
999       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1000 \r
1001       if (first.programLogo == NULL && appData.debugMode) {\r
1002           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
1003       }\r
1004   } else if(appData.autoLogo) {\r
1005       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
1006         char buf[MSG_SIZ];\r
1007           snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.firstDirectory);\r
1008         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
1009       }\r
1010   }\r
1011 \r
1012   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
1013       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1014 \r
1015       if (second.programLogo == NULL && appData.debugMode) {\r
1016           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
1017       }\r
1018   } else if(appData.autoLogo) {\r
1019       char buf[MSG_SIZ];\r
1020       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
1021         snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);\r
1022         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1023       } else\r
1024       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
1025         snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);\r
1026         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
1027       }\r
1028   }\r
1029 \r
1030   SetUserLogo();\r
1031 \r
1032   iconWhite = LoadIcon(hInstance, "icon_white");\r
1033   iconBlack = LoadIcon(hInstance, "icon_black");\r
1034   iconCurrent = iconWhite;\r
1035   InitDrawingColors();\r
1036   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1037   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1038   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1039     /* Compute window size for each board size, and use the largest\r
1040        size that fits on this screen as the default. */\r
1041     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1042     if (boardSize == (BoardSize)-1 &&\r
1043         winH <= screenHeight\r
1044            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1045         && winW <= screenWidth) {\r
1046       boardSize = (BoardSize)ibs;\r
1047     }\r
1048   }\r
1049 \r
1050   InitDrawingSizes(boardSize, 0);\r
1051   TranslateMenus(1);\r
1052   InitMenuChecks();\r
1053   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1054 \r
1055   /* [AS] Load textures if specified */\r
1056   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1057   \r
1058   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1059       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1060       liteBackTextureMode = appData.liteBackTextureMode;\r
1061 \r
1062       if (liteBackTexture == NULL && appData.debugMode) {\r
1063           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1064       }\r
1065   }\r
1066   \r
1067   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1068       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1069       darkBackTextureMode = appData.darkBackTextureMode;\r
1070 \r
1071       if (darkBackTexture == NULL && appData.debugMode) {\r
1072           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1073       }\r
1074   }\r
1075 \r
1076   mysrandom( (unsigned) time(NULL) );\r
1077 \r
1078   /* [AS] Restore layout */\r
1079   if( wpMoveHistory.visible ) {\r
1080       MoveHistoryPopUp();\r
1081   }\r
1082 \r
1083   if( wpEvalGraph.visible ) {\r
1084       EvalGraphPopUp();\r
1085   }\r
1086 \r
1087   if( wpEngineOutput.visible ) {\r
1088       EngineOutputPopUp();\r
1089   }\r
1090 \r
1091   /* Make the window visible; update its client area; and return "success" */\r
1092   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1093   wp.length = sizeof(WINDOWPLACEMENT);\r
1094   wp.flags = 0;\r
1095   wp.showCmd = nCmdShow;\r
1096   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1097   wp.rcNormalPosition.left = wpMain.x;\r
1098   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1099   wp.rcNormalPosition.top = wpMain.y;\r
1100   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1101   SetWindowPlacement(hwndMain, &wp);\r
1102 \r
1103   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1104 \r
1105   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1106                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1107 \r
1108   if (hwndConsole) {\r
1109 #if AOT_CONSOLE\r
1110     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1111                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1112 #endif\r
1113     ShowWindow(hwndConsole, nCmdShow);\r
1114     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
1115       char buf[MSG_SIZ], *p = buf, *q;\r
1116         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
1117       do {\r
1118         q = strchr(p, ';');\r
1119         if(q) *q++ = 0;\r
1120         if(*p) ChatPopUp(p);\r
1121       } while(p=q);\r
1122     }\r
1123     SetActiveWindow(hwndConsole);\r
1124   }\r
1125   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1126   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1127 \r
1128   return TRUE;\r
1129 \r
1130 }\r
1131 \r
1132 VOID\r
1133 InitMenuChecks()\r
1134 {\r
1135   HMENU hmenu = GetMenu(hwndMain);\r
1136 \r
1137   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1138                         MF_BYCOMMAND|((appData.icsActive &&\r
1139                                        *appData.icsCommPort != NULLCHAR) ?\r
1140                                       MF_ENABLED : MF_GRAYED));\r
1141   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1142                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1143                                      MF_CHECKED : MF_UNCHECKED));\r
1144 }\r
1145 \r
1146 //---------------------------------------------------------------------------------------------------------\r
1147 \r
1148 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1149 #define XBOARD FALSE\r
1150 \r
1151 #define OPTCHAR "/"\r
1152 #define SEPCHAR "="\r
1153 \r
1154 #include "args.h"\r
1155 \r
1156 // front-end part of option handling\r
1157 \r
1158 VOID\r
1159 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1160 {\r
1161   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1162   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1163   DeleteDC(hdc);\r
1164   lf->lfWidth = 0;\r
1165   lf->lfEscapement = 0;\r
1166   lf->lfOrientation = 0;\r
1167   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1168   lf->lfItalic = mfp->italic;\r
1169   lf->lfUnderline = mfp->underline;\r
1170   lf->lfStrikeOut = mfp->strikeout;\r
1171   lf->lfCharSet = mfp->charset;\r
1172   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1173   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1174   lf->lfQuality = DEFAULT_QUALITY;\r
1175   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1176     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1177 }\r
1178 \r
1179 void\r
1180 CreateFontInMF(MyFont *mf)\r
1181\r
1182   LFfromMFP(&mf->lf, &mf->mfp);\r
1183   if (mf->hf) DeleteObject(mf->hf);\r
1184   mf->hf = CreateFontIndirect(&mf->lf);\r
1185 }\r
1186 \r
1187 // [HGM] This platform-dependent table provides the location for storing the color info\r
1188 void *\r
1189 colorVariable[] = {\r
1190   &whitePieceColor, \r
1191   &blackPieceColor, \r
1192   &lightSquareColor,\r
1193   &darkSquareColor, \r
1194   &highlightSquareColor,\r
1195   &premoveHighlightColor,\r
1196   NULL,\r
1197   &consoleBackgroundColor,\r
1198   &appData.fontForeColorWhite,\r
1199   &appData.fontBackColorWhite,\r
1200   &appData.fontForeColorBlack,\r
1201   &appData.fontBackColorBlack,\r
1202   &appData.evalHistColorWhite,\r
1203   &appData.evalHistColorBlack,\r
1204   &appData.highlightArrowColor,\r
1205 };\r
1206 \r
1207 /* Command line font name parser.  NULL name means do nothing.\r
1208    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1209    For backward compatibility, syntax without the colon is also\r
1210    accepted, but font names with digits in them won't work in that case.\r
1211 */\r
1212 VOID\r
1213 ParseFontName(char *name, MyFontParams *mfp)\r
1214 {\r
1215   char *p, *q;\r
1216   if (name == NULL) return;\r
1217   p = name;\r
1218   q = strchr(p, ':');\r
1219   if (q) {\r
1220     if (q - p >= sizeof(mfp->faceName))\r
1221       ExitArgError(_("Font name too long:"), name);\r
1222     memcpy(mfp->faceName, p, q - p);\r
1223     mfp->faceName[q - p] = NULLCHAR;\r
1224     p = q + 1;\r
1225   } else {\r
1226     q = mfp->faceName;\r
1227     while (*p && !isdigit(*p)) {\r
1228       *q++ = *p++;\r
1229       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1230         ExitArgError(_("Font name too long:"), name);\r
1231     }\r
1232     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1233     *q = NULLCHAR;\r
1234   }\r
1235   if (!*p) ExitArgError(_("Font point size missing:"), name);\r
1236   mfp->pointSize = (float) atof(p);\r
1237   mfp->bold = (strchr(p, 'b') != NULL);\r
1238   mfp->italic = (strchr(p, 'i') != NULL);\r
1239   mfp->underline = (strchr(p, 'u') != NULL);\r
1240   mfp->strikeout = (strchr(p, 's') != NULL);\r
1241   mfp->charset = DEFAULT_CHARSET;\r
1242   q = strchr(p, 'c');\r
1243   if (q)\r
1244     mfp->charset = (BYTE) atoi(q+1);\r
1245 }\r
1246 \r
1247 void\r
1248 ParseFont(char *name, int number)\r
1249 { // wrapper to shield back-end from 'font'\r
1250   ParseFontName(name, &font[boardSize][number]->mfp);\r
1251 }\r
1252 \r
1253 void\r
1254 SetFontDefaults()\r
1255 { // in WB  we have a 2D array of fonts; this initializes their description\r
1256   int i, j;\r
1257   /* Point font array elements to structures and\r
1258      parse default font names */\r
1259   for (i=0; i<NUM_FONTS; i++) {\r
1260     for (j=0; j<NUM_SIZES; j++) {\r
1261       font[j][i] = &fontRec[j][i];\r
1262       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1263     }\r
1264   }\r
1265 }\r
1266 \r
1267 void\r
1268 CreateFonts()\r
1269 { // here we create the actual fonts from the selected descriptions\r
1270   int i, j;\r
1271   for (i=0; i<NUM_FONTS; i++) {\r
1272     for (j=0; j<NUM_SIZES; j++) {\r
1273       CreateFontInMF(font[j][i]);\r
1274     }\r
1275   }\r
1276 }\r
1277 /* Color name parser.\r
1278    X version accepts X color names, but this one\r
1279    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1280 COLORREF\r
1281 ParseColorName(char *name)\r
1282 {\r
1283   int red, green, blue, count;\r
1284   char buf[MSG_SIZ];\r
1285 \r
1286   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1287   if (count != 3) {\r
1288     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1289       &red, &green, &blue);\r
1290   }\r
1291   if (count != 3) {\r
1292     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1293     DisplayError(buf, 0);\r
1294     return RGB(0, 0, 0);\r
1295   }\r
1296   return PALETTERGB(red, green, blue);\r
1297 }\r
1298 \r
1299 void\r
1300 ParseColor(int n, char *name)\r
1301 { // for WinBoard the color is an int, which needs to be derived from the string\r
1302   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1303 }\r
1304 \r
1305 void\r
1306 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1307 {\r
1308   char *e = argValue;\r
1309   int eff = 0;\r
1310 \r
1311   while (*e) {\r
1312     if (*e == 'b')      eff |= CFE_BOLD;\r
1313     else if (*e == 'i') eff |= CFE_ITALIC;\r
1314     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1315     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1316     else if (*e == '#' || isdigit(*e)) break;\r
1317     e++;\r
1318   }\r
1319   *effects = eff;\r
1320   *color   = ParseColorName(e);\r
1321 }\r
1322 \r
1323 void\r
1324 ParseTextAttribs(ColorClass cc, char *s)\r
1325 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1326     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1327     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1328 }\r
1329 \r
1330 void\r
1331 ParseBoardSize(void *addr, char *name)\r
1332 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1333   BoardSize bs = SizeTiny;\r
1334   while (sizeInfo[bs].name != NULL) {\r
1335     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1336         *(BoardSize *)addr = bs;\r
1337         return;\r
1338     }\r
1339     bs++;\r
1340   }\r
1341   ExitArgError(_("Unrecognized board size value"), name);\r
1342 }\r
1343 \r
1344 void\r
1345 LoadAllSounds()\r
1346 { // [HGM] import name from appData first\r
1347   ColorClass cc;\r
1348   SoundClass sc;\r
1349   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1350     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1351     textAttribs[cc].sound.data = NULL;\r
1352     MyLoadSound(&textAttribs[cc].sound);\r
1353   }\r
1354   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1355     textAttribs[cc].sound.name = strdup("");\r
1356     textAttribs[cc].sound.data = NULL;\r
1357   }\r
1358   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1359     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1360     sounds[sc].data = NULL;\r
1361     MyLoadSound(&sounds[sc]);\r
1362   }\r
1363 }\r
1364 \r
1365 void\r
1366 SetCommPortDefaults()\r
1367 {\r
1368    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1369   dcb.DCBlength = sizeof(DCB);\r
1370   dcb.BaudRate = 9600;\r
1371   dcb.fBinary = TRUE;\r
1372   dcb.fParity = FALSE;\r
1373   dcb.fOutxCtsFlow = FALSE;\r
1374   dcb.fOutxDsrFlow = FALSE;\r
1375   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1376   dcb.fDsrSensitivity = FALSE;\r
1377   dcb.fTXContinueOnXoff = TRUE;\r
1378   dcb.fOutX = FALSE;\r
1379   dcb.fInX = FALSE;\r
1380   dcb.fNull = FALSE;\r
1381   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1382   dcb.fAbortOnError = FALSE;\r
1383   dcb.ByteSize = 7;\r
1384   dcb.Parity = SPACEPARITY;\r
1385   dcb.StopBits = ONESTOPBIT;\r
1386 }\r
1387 \r
1388 // [HGM] args: these three cases taken out to stay in front-end\r
1389 void\r
1390 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1391 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1392         // while the curent board size determines the element. This system should be ported to XBoard.\r
1393         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1394         int bs;\r
1395         for (bs=0; bs<NUM_SIZES; bs++) {\r
1396           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1397           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1398           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1399             ad->argName, mfp->faceName, mfp->pointSize,\r
1400             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1401             mfp->bold ? "b" : "",\r
1402             mfp->italic ? "i" : "",\r
1403             mfp->underline ? "u" : "",\r
1404             mfp->strikeout ? "s" : "",\r
1405             (int)mfp->charset);\r
1406         }\r
1407       }\r
1408 \r
1409 void\r
1410 ExportSounds()\r
1411 { // [HGM] copy the names from the internal WB variables to appData\r
1412   ColorClass cc;\r
1413   SoundClass sc;\r
1414   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1415     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1416   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1417     (&appData.soundMove)[sc] = sounds[sc].name;\r
1418 }\r
1419 \r
1420 void\r
1421 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1422 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1423         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1424         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1425           (ta->effects & CFE_BOLD) ? "b" : "",\r
1426           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1427           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1428           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1429           (ta->effects) ? " " : "",\r
1430           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1431       }\r
1432 \r
1433 void\r
1434 SaveColor(FILE *f, ArgDescriptor *ad)\r
1435 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1436         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1437         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1438           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1439 }\r
1440 \r
1441 void\r
1442 SaveBoardSize(FILE *f, char *name, void *addr)\r
1443 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1444   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1445 }\r
1446 \r
1447 void\r
1448 ParseCommPortSettings(char *s)\r
1449 { // wrapper to keep dcb from back-end\r
1450   ParseCommSettings(s, &dcb);\r
1451 }\r
1452 \r
1453 void\r
1454 GetWindowCoords()\r
1455 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1456   GetActualPlacement(hwndMain, &wpMain);\r
1457   GetActualPlacement(hwndConsole, &wpConsole);\r
1458   GetActualPlacement(commentDialog, &wpComment);\r
1459   GetActualPlacement(editTagsDialog, &wpTags);\r
1460   GetActualPlacement(gameListDialog, &wpGameList);\r
1461   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1462   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1463   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1464 }\r
1465 \r
1466 void\r
1467 PrintCommPortSettings(FILE *f, char *name)\r
1468 { // wrapper to shield back-end from DCB\r
1469       PrintCommSettings(f, name, &dcb);\r
1470 }\r
1471 \r
1472 int\r
1473 MySearchPath(char *installDir, char *name, char *fullname)\r
1474 {\r
1475   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1476   if(name[0]== '%') {\r
1477     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1478     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1479       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1480       *strchr(buf, '%') = 0;\r
1481       strcat(fullname, getenv(buf));\r
1482       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1483     }\r
1484     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1485     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1486     return (int) strlen(fullname);\r
1487   }\r
1488   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1489 }\r
1490 \r
1491 int\r
1492 MyGetFullPathName(char *name, char *fullname)\r
1493 {\r
1494   char *dummy;\r
1495   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1496 }\r
1497 \r
1498 int\r
1499 MainWindowUp()\r
1500 { // [HGM] args: allows testing if main window is realized from back-end\r
1501   return hwndMain != NULL;\r
1502 }\r
1503 \r
1504 void\r
1505 PopUpStartupDialog()\r
1506 {\r
1507     FARPROC lpProc;\r
1508     \r
1509     LoadLanguageFile(appData.language);\r
1510     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1511     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1512     FreeProcInstance(lpProc);\r
1513 }\r
1514 \r
1515 /*---------------------------------------------------------------------------*\\r
1516  *\r
1517  * GDI board drawing routines\r
1518  *\r
1519 \*---------------------------------------------------------------------------*/\r
1520 \r
1521 /* [AS] Draw square using background texture */\r
1522 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1523 {\r
1524     XFORM   x;\r
1525 \r
1526     if( mode == 0 ) {\r
1527         return; /* Should never happen! */\r
1528     }\r
1529 \r
1530     SetGraphicsMode( dst, GM_ADVANCED );\r
1531 \r
1532     switch( mode ) {\r
1533     case 1:\r
1534         /* Identity */\r
1535         break;\r
1536     case 2:\r
1537         /* X reflection */\r
1538         x.eM11 = -1.0;\r
1539         x.eM12 = 0;\r
1540         x.eM21 = 0;\r
1541         x.eM22 = 1.0;\r
1542         x.eDx = (FLOAT) dw + dx - 1;\r
1543         x.eDy = 0;\r
1544         dx = 0;\r
1545         SetWorldTransform( dst, &x );\r
1546         break;\r
1547     case 3:\r
1548         /* Y reflection */\r
1549         x.eM11 = 1.0;\r
1550         x.eM12 = 0;\r
1551         x.eM21 = 0;\r
1552         x.eM22 = -1.0;\r
1553         x.eDx = 0;\r
1554         x.eDy = (FLOAT) dh + dy - 1;\r
1555         dy = 0;\r
1556         SetWorldTransform( dst, &x );\r
1557         break;\r
1558     case 4:\r
1559         /* X/Y flip */\r
1560         x.eM11 = 0;\r
1561         x.eM12 = 1.0;\r
1562         x.eM21 = 1.0;\r
1563         x.eM22 = 0;\r
1564         x.eDx = (FLOAT) dx;\r
1565         x.eDy = (FLOAT) dy;\r
1566         dx = 0;\r
1567         dy = 0;\r
1568         SetWorldTransform( dst, &x );\r
1569         break;\r
1570     }\r
1571 \r
1572     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1573 \r
1574     x.eM11 = 1.0;\r
1575     x.eM12 = 0;\r
1576     x.eM21 = 0;\r
1577     x.eM22 = 1.0;\r
1578     x.eDx = 0;\r
1579     x.eDy = 0;\r
1580     SetWorldTransform( dst, &x );\r
1581 \r
1582     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1583 }\r
1584 \r
1585 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1586 enum {\r
1587     PM_WP = (int) WhitePawn, \r
1588     PM_WN = (int) WhiteKnight, \r
1589     PM_WB = (int) WhiteBishop, \r
1590     PM_WR = (int) WhiteRook, \r
1591     PM_WQ = (int) WhiteQueen, \r
1592     PM_WF = (int) WhiteFerz, \r
1593     PM_WW = (int) WhiteWazir, \r
1594     PM_WE = (int) WhiteAlfil, \r
1595     PM_WM = (int) WhiteMan, \r
1596     PM_WO = (int) WhiteCannon, \r
1597     PM_WU = (int) WhiteUnicorn, \r
1598     PM_WH = (int) WhiteNightrider, \r
1599     PM_WA = (int) WhiteAngel, \r
1600     PM_WC = (int) WhiteMarshall, \r
1601     PM_WAB = (int) WhiteCardinal, \r
1602     PM_WD = (int) WhiteDragon, \r
1603     PM_WL = (int) WhiteLance, \r
1604     PM_WS = (int) WhiteCobra, \r
1605     PM_WV = (int) WhiteFalcon, \r
1606     PM_WSG = (int) WhiteSilver, \r
1607     PM_WG = (int) WhiteGrasshopper, \r
1608     PM_WK = (int) WhiteKing,\r
1609     PM_BP = (int) BlackPawn, \r
1610     PM_BN = (int) BlackKnight, \r
1611     PM_BB = (int) BlackBishop, \r
1612     PM_BR = (int) BlackRook, \r
1613     PM_BQ = (int) BlackQueen, \r
1614     PM_BF = (int) BlackFerz, \r
1615     PM_BW = (int) BlackWazir, \r
1616     PM_BE = (int) BlackAlfil, \r
1617     PM_BM = (int) BlackMan,\r
1618     PM_BO = (int) BlackCannon, \r
1619     PM_BU = (int) BlackUnicorn, \r
1620     PM_BH = (int) BlackNightrider, \r
1621     PM_BA = (int) BlackAngel, \r
1622     PM_BC = (int) BlackMarshall, \r
1623     PM_BG = (int) BlackGrasshopper, \r
1624     PM_BAB = (int) BlackCardinal,\r
1625     PM_BD = (int) BlackDragon,\r
1626     PM_BL = (int) BlackLance,\r
1627     PM_BS = (int) BlackCobra,\r
1628     PM_BV = (int) BlackFalcon,\r
1629     PM_BSG = (int) BlackSilver,\r
1630     PM_BK = (int) BlackKing\r
1631 };\r
1632 \r
1633 static HFONT hPieceFont = NULL;\r
1634 static HBITMAP hPieceMask[(int) EmptySquare];\r
1635 static HBITMAP hPieceFace[(int) EmptySquare];\r
1636 static int fontBitmapSquareSize = 0;\r
1637 static char pieceToFontChar[(int) EmptySquare] =\r
1638                               { 'p', 'n', 'b', 'r', 'q', \r
1639                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1640                       'k', 'o', 'm', 'v', 't', 'w', \r
1641                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1642                                                               'l' };\r
1643 \r
1644 extern BOOL SetCharTable( char *table, const char * map );\r
1645 /* [HGM] moved to backend.c */\r
1646 \r
1647 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1648 {\r
1649     HBRUSH hbrush;\r
1650     BYTE r1 = GetRValue( color );\r
1651     BYTE g1 = GetGValue( color );\r
1652     BYTE b1 = GetBValue( color );\r
1653     BYTE r2 = r1 / 2;\r
1654     BYTE g2 = g1 / 2;\r
1655     BYTE b2 = b1 / 2;\r
1656     RECT rc;\r
1657 \r
1658     /* Create a uniform background first */\r
1659     hbrush = CreateSolidBrush( color );\r
1660     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1661     FillRect( hdc, &rc, hbrush );\r
1662     DeleteObject( hbrush );\r
1663     \r
1664     if( mode == 1 ) {\r
1665         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1666         int steps = squareSize / 2;\r
1667         int i;\r
1668 \r
1669         for( i=0; i<steps; i++ ) {\r
1670             BYTE r = r1 - (r1-r2) * i / steps;\r
1671             BYTE g = g1 - (g1-g2) * i / steps;\r
1672             BYTE b = b1 - (b1-b2) * i / steps;\r
1673 \r
1674             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1675             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1676             FillRect( hdc, &rc, hbrush );\r
1677             DeleteObject(hbrush);\r
1678         }\r
1679     }\r
1680     else if( mode == 2 ) {\r
1681         /* Diagonal gradient, good more or less for every piece */\r
1682         POINT triangle[3];\r
1683         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1684         HBRUSH hbrush_old;\r
1685         int steps = squareSize;\r
1686         int i;\r
1687 \r
1688         triangle[0].x = squareSize - steps;\r
1689         triangle[0].y = squareSize;\r
1690         triangle[1].x = squareSize;\r
1691         triangle[1].y = squareSize;\r
1692         triangle[2].x = squareSize;\r
1693         triangle[2].y = squareSize - steps;\r
1694 \r
1695         for( i=0; i<steps; i++ ) {\r
1696             BYTE r = r1 - (r1-r2) * i / steps;\r
1697             BYTE g = g1 - (g1-g2) * i / steps;\r
1698             BYTE b = b1 - (b1-b2) * i / steps;\r
1699 \r
1700             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1701             hbrush_old = SelectObject( hdc, hbrush );\r
1702             Polygon( hdc, triangle, 3 );\r
1703             SelectObject( hdc, hbrush_old );\r
1704             DeleteObject(hbrush);\r
1705             triangle[0].x++;\r
1706             triangle[2].y++;\r
1707         }\r
1708 \r
1709         SelectObject( hdc, hpen );\r
1710     }\r
1711 }\r
1712 \r
1713 /*\r
1714     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1715     seems to work ok. The main problem here is to find the "inside" of a chess\r
1716     piece: follow the steps as explained below.\r
1717 */\r
1718 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1719 {\r
1720     HBITMAP hbm;\r
1721     HBITMAP hbm_old;\r
1722     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1723     RECT rc;\r
1724     SIZE sz;\r
1725     POINT pt;\r
1726     int backColor = whitePieceColor; \r
1727     int foreColor = blackPieceColor;\r
1728     \r
1729     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1730         backColor = appData.fontBackColorWhite;\r
1731         foreColor = appData.fontForeColorWhite;\r
1732     }\r
1733     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1734         backColor = appData.fontBackColorBlack;\r
1735         foreColor = appData.fontForeColorBlack;\r
1736     }\r
1737 \r
1738     /* Mask */\r
1739     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1740 \r
1741     hbm_old = SelectObject( hdc, hbm );\r
1742 \r
1743     rc.left = 0;\r
1744     rc.top = 0;\r
1745     rc.right = squareSize;\r
1746     rc.bottom = squareSize;\r
1747 \r
1748     /* Step 1: background is now black */\r
1749     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1750 \r
1751     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1752 \r
1753     pt.x = (squareSize - sz.cx) / 2;\r
1754     pt.y = (squareSize - sz.cy) / 2;\r
1755 \r
1756     SetBkMode( hdc, TRANSPARENT );\r
1757     SetTextColor( hdc, chroma );\r
1758     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1759     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1760 \r
1761     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1762     /* Step 3: the area outside the piece is filled with white */\r
1763 //    FloodFill( hdc, 0, 0, chroma );\r
1764     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1765     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1766     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1767     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1768     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1769     /* \r
1770         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1771         but if the start point is not inside the piece we're lost!\r
1772         There should be a better way to do this... if we could create a region or path\r
1773         from the fill operation we would be fine for example.\r
1774     */\r
1775 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1776     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1777 \r
1778     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1779         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1780         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1781 \r
1782         SelectObject( dc2, bm2 );\r
1783         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1784         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1785         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1786         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1787         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1788 \r
1789         DeleteDC( dc2 );\r
1790         DeleteObject( bm2 );\r
1791     }\r
1792 \r
1793     SetTextColor( hdc, 0 );\r
1794     /* \r
1795         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1796         draw the piece again in black for safety.\r
1797     */\r
1798     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1799 \r
1800     SelectObject( hdc, hbm_old );\r
1801 \r
1802     if( hPieceMask[index] != NULL ) {\r
1803         DeleteObject( hPieceMask[index] );\r
1804     }\r
1805 \r
1806     hPieceMask[index] = hbm;\r
1807 \r
1808     /* Face */\r
1809     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1810 \r
1811     SelectObject( hdc, hbm );\r
1812 \r
1813     {\r
1814         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1815         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1816         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1817 \r
1818         SelectObject( dc1, hPieceMask[index] );\r
1819         SelectObject( dc2, bm2 );\r
1820         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1821         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1822         \r
1823         /* \r
1824             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1825             the piece background and deletes (makes transparent) the rest.\r
1826             Thanks to that mask, we are free to paint the background with the greates\r
1827             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1828             We use this, to make gradients and give the pieces a "roundish" look.\r
1829         */\r
1830         SetPieceBackground( hdc, backColor, 2 );\r
1831         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1832 \r
1833         DeleteDC( dc2 );\r
1834         DeleteDC( dc1 );\r
1835         DeleteObject( bm2 );\r
1836     }\r
1837 \r
1838     SetTextColor( hdc, foreColor );\r
1839     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1840 \r
1841     SelectObject( hdc, hbm_old );\r
1842 \r
1843     if( hPieceFace[index] != NULL ) {\r
1844         DeleteObject( hPieceFace[index] );\r
1845     }\r
1846 \r
1847     hPieceFace[index] = hbm;\r
1848 }\r
1849 \r
1850 static int TranslatePieceToFontPiece( int piece )\r
1851 {\r
1852     switch( piece ) {\r
1853     case BlackPawn:\r
1854         return PM_BP;\r
1855     case BlackKnight:\r
1856         return PM_BN;\r
1857     case BlackBishop:\r
1858         return PM_BB;\r
1859     case BlackRook:\r
1860         return PM_BR;\r
1861     case BlackQueen:\r
1862         return PM_BQ;\r
1863     case BlackKing:\r
1864         return PM_BK;\r
1865     case WhitePawn:\r
1866         return PM_WP;\r
1867     case WhiteKnight:\r
1868         return PM_WN;\r
1869     case WhiteBishop:\r
1870         return PM_WB;\r
1871     case WhiteRook:\r
1872         return PM_WR;\r
1873     case WhiteQueen:\r
1874         return PM_WQ;\r
1875     case WhiteKing:\r
1876         return PM_WK;\r
1877 \r
1878     case BlackAngel:\r
1879         return PM_BA;\r
1880     case BlackMarshall:\r
1881         return PM_BC;\r
1882     case BlackFerz:\r
1883         return PM_BF;\r
1884     case BlackNightrider:\r
1885         return PM_BH;\r
1886     case BlackAlfil:\r
1887         return PM_BE;\r
1888     case BlackWazir:\r
1889         return PM_BW;\r
1890     case BlackUnicorn:\r
1891         return PM_BU;\r
1892     case BlackCannon:\r
1893         return PM_BO;\r
1894     case BlackGrasshopper:\r
1895         return PM_BG;\r
1896     case BlackMan:\r
1897         return PM_BM;\r
1898     case BlackSilver:\r
1899         return PM_BSG;\r
1900     case BlackLance:\r
1901         return PM_BL;\r
1902     case BlackFalcon:\r
1903         return PM_BV;\r
1904     case BlackCobra:\r
1905         return PM_BS;\r
1906     case BlackCardinal:\r
1907         return PM_BAB;\r
1908     case BlackDragon:\r
1909         return PM_BD;\r
1910 \r
1911     case WhiteAngel:\r
1912         return PM_WA;\r
1913     case WhiteMarshall:\r
1914         return PM_WC;\r
1915     case WhiteFerz:\r
1916         return PM_WF;\r
1917     case WhiteNightrider:\r
1918         return PM_WH;\r
1919     case WhiteAlfil:\r
1920         return PM_WE;\r
1921     case WhiteWazir:\r
1922         return PM_WW;\r
1923     case WhiteUnicorn:\r
1924         return PM_WU;\r
1925     case WhiteCannon:\r
1926         return PM_WO;\r
1927     case WhiteGrasshopper:\r
1928         return PM_WG;\r
1929     case WhiteMan:\r
1930         return PM_WM;\r
1931     case WhiteSilver:\r
1932         return PM_WSG;\r
1933     case WhiteLance:\r
1934         return PM_WL;\r
1935     case WhiteFalcon:\r
1936         return PM_WV;\r
1937     case WhiteCobra:\r
1938         return PM_WS;\r
1939     case WhiteCardinal:\r
1940         return PM_WAB;\r
1941     case WhiteDragon:\r
1942         return PM_WD;\r
1943     }\r
1944 \r
1945     return 0;\r
1946 }\r
1947 \r
1948 void CreatePiecesFromFont()\r
1949 {\r
1950     LOGFONT lf;\r
1951     HDC hdc_window = NULL;\r
1952     HDC hdc = NULL;\r
1953     HFONT hfont_old;\r
1954     int fontHeight;\r
1955     int i;\r
1956 \r
1957     if( fontBitmapSquareSize < 0 ) {\r
1958         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1959         return;\r
1960     }\r
1961 \r
1962     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1963         fontBitmapSquareSize = -1;\r
1964         return;\r
1965     }\r
1966 \r
1967     if( fontBitmapSquareSize != squareSize ) {\r
1968         hdc_window = GetDC( hwndMain );\r
1969         hdc = CreateCompatibleDC( hdc_window );\r
1970 \r
1971         if( hPieceFont != NULL ) {\r
1972             DeleteObject( hPieceFont );\r
1973         }\r
1974         else {\r
1975             for( i=0; i<=(int)BlackKing; i++ ) {\r
1976                 hPieceMask[i] = NULL;\r
1977                 hPieceFace[i] = NULL;\r
1978             }\r
1979         }\r
1980 \r
1981         fontHeight = 75;\r
1982 \r
1983         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
1984             fontHeight = appData.fontPieceSize;\r
1985         }\r
1986 \r
1987         fontHeight = (fontHeight * squareSize) / 100;\r
1988 \r
1989         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
1990         lf.lfWidth = 0;\r
1991         lf.lfEscapement = 0;\r
1992         lf.lfOrientation = 0;\r
1993         lf.lfWeight = FW_NORMAL;\r
1994         lf.lfItalic = 0;\r
1995         lf.lfUnderline = 0;\r
1996         lf.lfStrikeOut = 0;\r
1997         lf.lfCharSet = DEFAULT_CHARSET;\r
1998         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1999         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2000         lf.lfQuality = PROOF_QUALITY;\r
2001         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2002         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2003         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2004 \r
2005         hPieceFont = CreateFontIndirect( &lf );\r
2006 \r
2007         if( hPieceFont == NULL ) {\r
2008             fontBitmapSquareSize = -2;\r
2009         }\r
2010         else {\r
2011             /* Setup font-to-piece character table */\r
2012             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2013                 /* No (or wrong) global settings, try to detect the font */\r
2014                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2015                     /* Alpha */\r
2016                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2017                 }\r
2018                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2019                     /* DiagramTT* family */\r
2020                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2021                 }\r
2022                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2023                     /* Fairy symbols */\r
2024                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2025                 }\r
2026                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2027                     /* Good Companion (Some characters get warped as literal :-( */\r
2028                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2029                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2030                     SetCharTable(pieceToFontChar, s);\r
2031                 }\r
2032                 else {\r
2033                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2034                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2035                 }\r
2036             }\r
2037 \r
2038             /* Create bitmaps */\r
2039             hfont_old = SelectObject( hdc, hPieceFont );\r
2040             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2041                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2042                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2043 \r
2044             SelectObject( hdc, hfont_old );\r
2045 \r
2046             fontBitmapSquareSize = squareSize;\r
2047         }\r
2048     }\r
2049 \r
2050     if( hdc != NULL ) {\r
2051         DeleteDC( hdc );\r
2052     }\r
2053 \r
2054     if( hdc_window != NULL ) {\r
2055         ReleaseDC( hwndMain, hdc_window );\r
2056     }\r
2057 }\r
2058 \r
2059 HBITMAP\r
2060 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2061 {\r
2062   char name[128];\r
2063 \r
2064     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2065   if (gameInfo.event &&\r
2066       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2067       strcmp(name, "k80s") == 0) {\r
2068     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2069   }\r
2070   return LoadBitmap(hinst, name);\r
2071 }\r
2072 \r
2073 \r
2074 /* Insert a color into the program's logical palette\r
2075    structure.  This code assumes the given color is\r
2076    the result of the RGB or PALETTERGB macro, and it\r
2077    knows how those macros work (which is documented).\r
2078 */\r
2079 VOID\r
2080 InsertInPalette(COLORREF color)\r
2081 {\r
2082   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2083 \r
2084   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2085     DisplayFatalError(_("Too many colors"), 0, 1);\r
2086     pLogPal->palNumEntries--;\r
2087     return;\r
2088   }\r
2089 \r
2090   pe->peFlags = (char) 0;\r
2091   pe->peRed = (char) (0xFF & color);\r
2092   pe->peGreen = (char) (0xFF & (color >> 8));\r
2093   pe->peBlue = (char) (0xFF & (color >> 16));\r
2094   return;\r
2095 }\r
2096 \r
2097 \r
2098 VOID\r
2099 InitDrawingColors()\r
2100 {\r
2101   if (pLogPal == NULL) {\r
2102     /* Allocate enough memory for a logical palette with\r
2103      * PALETTESIZE entries and set the size and version fields\r
2104      * of the logical palette structure.\r
2105      */\r
2106     pLogPal = (NPLOGPALETTE)\r
2107       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2108                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2109     pLogPal->palVersion    = 0x300;\r
2110   }\r
2111   pLogPal->palNumEntries = 0;\r
2112 \r
2113   InsertInPalette(lightSquareColor);\r
2114   InsertInPalette(darkSquareColor);\r
2115   InsertInPalette(whitePieceColor);\r
2116   InsertInPalette(blackPieceColor);\r
2117   InsertInPalette(highlightSquareColor);\r
2118   InsertInPalette(premoveHighlightColor);\r
2119 \r
2120   /*  create a logical color palette according the information\r
2121    *  in the LOGPALETTE structure.\r
2122    */\r
2123   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2124 \r
2125   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2126   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2127   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2128   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2129   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2130   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2131   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2132   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2133   /* [AS] Force rendering of the font-based pieces */\r
2134   if( fontBitmapSquareSize > 0 ) {\r
2135     fontBitmapSquareSize = 0;\r
2136   }\r
2137 }\r
2138 \r
2139 \r
2140 int\r
2141 BoardWidth(int boardSize, int n)\r
2142 { /* [HGM] argument n added to allow different width and height */\r
2143   int lineGap = sizeInfo[boardSize].lineGap;\r
2144 \r
2145   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2146       lineGap = appData.overrideLineGap;\r
2147   }\r
2148 \r
2149   return (n + 1) * lineGap +\r
2150           n * sizeInfo[boardSize].squareSize;\r
2151 }\r
2152 \r
2153 /* Respond to board resize by dragging edge */\r
2154 VOID\r
2155 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2156 {\r
2157   BoardSize newSize = NUM_SIZES - 1;\r
2158   static int recurse = 0;\r
2159   if (IsIconic(hwndMain)) return;\r
2160   if (recurse > 0) return;\r
2161   recurse++;\r
2162   while (newSize > 0) {\r
2163         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2164         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2165            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2166     newSize--;\r
2167   } \r
2168   boardSize = newSize;\r
2169   InitDrawingSizes(boardSize, flags);\r
2170   recurse--;\r
2171 }\r
2172 \r
2173 \r
2174 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2175 \r
2176 VOID\r
2177 InitDrawingSizes(BoardSize boardSize, int flags)\r
2178 {\r
2179   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2180   ChessSquare piece;\r
2181   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2182   HDC hdc;\r
2183   SIZE clockSize, messageSize;\r
2184   HFONT oldFont;\r
2185   char buf[MSG_SIZ];\r
2186   char *str;\r
2187   HMENU hmenu = GetMenu(hwndMain);\r
2188   RECT crect, wrect, oldRect;\r
2189   int offby;\r
2190   LOGBRUSH logbrush;\r
2191 \r
2192   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2193   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2194 \r
2195   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2196   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2197 \r
2198   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2199   oldRect.top = wpMain.y;\r
2200   oldRect.right = wpMain.x + wpMain.width;\r
2201   oldRect.bottom = wpMain.y + wpMain.height;\r
2202 \r
2203   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2204   smallLayout = sizeInfo[boardSize].smallLayout;\r
2205   squareSize = sizeInfo[boardSize].squareSize;\r
2206   lineGap = sizeInfo[boardSize].lineGap;\r
2207   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2208 \r
2209   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2210       lineGap = appData.overrideLineGap;\r
2211   }\r
2212 \r
2213   if (tinyLayout != oldTinyLayout) {\r
2214     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
2215     if (tinyLayout) {\r
2216       style &= ~WS_SYSMENU;\r
2217       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2218                  "&Minimize\tCtrl+F4");\r
2219     } else {\r
2220       style |= WS_SYSMENU;\r
2221       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2222     }\r
2223     SetWindowLong(hwndMain, GWL_STYLE, style);\r
2224 \r
2225     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2226       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2227         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2228     }\r
2229     DrawMenuBar(hwndMain);\r
2230   }\r
2231 \r
2232   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2233   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2234 \r
2235   /* Get text area sizes */\r
2236   hdc = GetDC(hwndMain);\r
2237   if (appData.clockMode) {\r
2238     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2239   } else {\r
2240     snprintf(buf, MSG_SIZ, _("White"));\r
2241   }\r
2242   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2243   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2244   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2245   str = _("We only care about the height here");\r
2246   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2247   SelectObject(hdc, oldFont);\r
2248   ReleaseDC(hwndMain, hdc);\r
2249 \r
2250   /* Compute where everything goes */\r
2251   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2252         /* [HGM] logo: if either logo is on, reserve space for it */\r
2253         logoHeight =  2*clockSize.cy;\r
2254         leftLogoRect.left   = OUTER_MARGIN;\r
2255         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2256         leftLogoRect.top    = OUTER_MARGIN;\r
2257         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2258 \r
2259         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2260         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2261         rightLogoRect.top    = OUTER_MARGIN;\r
2262         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2263 \r
2264 \r
2265     whiteRect.left = leftLogoRect.right;\r
2266     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2267     whiteRect.top = OUTER_MARGIN;\r
2268     whiteRect.bottom = whiteRect.top + logoHeight;\r
2269 \r
2270     blackRect.right = rightLogoRect.left;\r
2271     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2272     blackRect.top = whiteRect.top;\r
2273     blackRect.bottom = whiteRect.bottom;\r
2274   } else {\r
2275     whiteRect.left = OUTER_MARGIN;\r
2276     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2277     whiteRect.top = OUTER_MARGIN;\r
2278     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2279 \r
2280     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2281     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2282     blackRect.top = whiteRect.top;\r
2283     blackRect.bottom = whiteRect.bottom;\r
2284 \r
2285     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2286   }\r
2287 \r
2288   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2289   if (appData.showButtonBar) {\r
2290     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2291       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2292   } else {\r
2293     messageRect.right = OUTER_MARGIN + boardWidth;\r
2294   }\r
2295   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2296   messageRect.bottom = messageRect.top + messageSize.cy;\r
2297 \r
2298   boardRect.left = OUTER_MARGIN;\r
2299   boardRect.right = boardRect.left + boardWidth;\r
2300   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2301   boardRect.bottom = boardRect.top + boardHeight;\r
2302 \r
2303   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2304   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2305   oldBoardSize = boardSize;\r
2306   oldTinyLayout = tinyLayout;\r
2307   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2308   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2309     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2310   winW *= 1 + twoBoards;\r
2311   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2312   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2313   wpMain.height = winH; //       without disturbing window attachments\r
2314   GetWindowRect(hwndMain, &wrect);\r
2315   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2316                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2317 \r
2318   // [HGM] placement: let attached windows follow size change.\r
2319   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2320   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2321   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2322   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2323   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2324 \r
2325   /* compensate if menu bar wrapped */\r
2326   GetClientRect(hwndMain, &crect);\r
2327   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2328   wpMain.height += offby;\r
2329   switch (flags) {\r
2330   case WMSZ_TOPLEFT:\r
2331     SetWindowPos(hwndMain, NULL, \r
2332                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2333                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2334     break;\r
2335 \r
2336   case WMSZ_TOPRIGHT:\r
2337   case WMSZ_TOP:\r
2338     SetWindowPos(hwndMain, NULL, \r
2339                  wrect.left, wrect.bottom - wpMain.height, \r
2340                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2341     break;\r
2342 \r
2343   case WMSZ_BOTTOMLEFT:\r
2344   case WMSZ_LEFT:\r
2345     SetWindowPos(hwndMain, NULL, \r
2346                  wrect.right - wpMain.width, wrect.top, \r
2347                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2348     break;\r
2349 \r
2350   case WMSZ_BOTTOMRIGHT:\r
2351   case WMSZ_BOTTOM:\r
2352   case WMSZ_RIGHT:\r
2353   default:\r
2354     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2355                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2356     break;\r
2357   }\r
2358 \r
2359   hwndPause = NULL;\r
2360   for (i = 0; i < N_BUTTONS; i++) {\r
2361     if (buttonDesc[i].hwnd != NULL) {\r
2362       DestroyWindow(buttonDesc[i].hwnd);\r
2363       buttonDesc[i].hwnd = NULL;\r
2364     }\r
2365     if (appData.showButtonBar) {\r
2366       buttonDesc[i].hwnd =\r
2367         CreateWindow("BUTTON", buttonDesc[i].label,\r
2368                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2369                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2370                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2371                      (HMENU) buttonDesc[i].id,\r
2372                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
2373       if (tinyLayout) {\r
2374         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2375                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2376                     MAKELPARAM(FALSE, 0));\r
2377       }\r
2378       if (buttonDesc[i].id == IDM_Pause)\r
2379         hwndPause = buttonDesc[i].hwnd;\r
2380       buttonDesc[i].wndproc = (WNDPROC)\r
2381         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
2382     }\r
2383   }\r
2384   if (gridPen != NULL) DeleteObject(gridPen);\r
2385   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2386   if (premovePen != NULL) DeleteObject(premovePen);\r
2387   if (lineGap != 0) {\r
2388     logbrush.lbStyle = BS_SOLID;\r
2389     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2390     gridPen =\r
2391       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2392                    lineGap, &logbrush, 0, NULL);\r
2393     logbrush.lbColor = highlightSquareColor;\r
2394     highlightPen =\r
2395       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2396                    lineGap, &logbrush, 0, NULL);\r
2397 \r
2398     logbrush.lbColor = premoveHighlightColor; \r
2399     premovePen =\r
2400       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2401                    lineGap, &logbrush, 0, NULL);\r
2402 \r
2403     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2404     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2405       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2406       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2407         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2408       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2409         BOARD_WIDTH * (squareSize + lineGap);\r
2410       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2411     }\r
2412     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2413       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2414       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2415         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2416         lineGap / 2 + (i * (squareSize + lineGap));\r
2417       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2418         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2419       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2420     }\r
2421   }\r
2422 \r
2423   /* [HGM] Licensing requirement */\r
2424 #ifdef GOTHIC\r
2425   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2426 #endif\r
2427 #ifdef FALCON\r
2428   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2429 #endif\r
2430   GothicPopUp( "", VariantNormal);\r
2431 \r
2432 \r
2433 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2434 \r
2435   /* Load piece bitmaps for this board size */\r
2436   for (i=0; i<=2; i++) {\r
2437     for (piece = WhitePawn;\r
2438          (int) piece < (int) BlackPawn;\r
2439          piece = (ChessSquare) ((int) piece + 1)) {\r
2440       if (pieceBitmap[i][piece] != NULL)\r
2441         DeleteObject(pieceBitmap[i][piece]);\r
2442     }\r
2443   }\r
2444 \r
2445   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2446   // Orthodox Chess pieces\r
2447   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2448   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2449   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2450   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2451   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2452   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2453   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2454   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2455   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2456   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2457   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2458   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2459   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2460   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2461   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2462   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2463     // in Shogi, Hijack the unused Queen for Lance\r
2464     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2465     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2466     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2467   } else {\r
2468     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2469     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2470     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2471   }\r
2472 \r
2473   if(squareSize <= 72 && squareSize >= 33) { \r
2474     /* A & C are available in most sizes now */\r
2475     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2476       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2477       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2478       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2479       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2480       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2481       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2482       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2483       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2484       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2485       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2486       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2487       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2488     } else { // Smirf-like\r
2489       if(gameInfo.variant == VariantSChess) {\r
2490         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2491         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2492         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2493       } else {\r
2494         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2495         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2496         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2497       }\r
2498     }\r
2499     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2500       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2501       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2502       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2503     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2504       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2505       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2506       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2507     } else { // WinBoard standard\r
2508       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2509       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2510       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2511     }\r
2512   }\r
2513 \r
2514 \r
2515   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2516     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2517     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2518     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2519     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2520     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2521     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2522     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2523     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2524     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2525     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2526     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2527     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2528     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2529     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2530     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2531     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2532     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2533     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2534     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2535     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2536     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2537     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2538     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2539     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2540     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2541     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2542     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2543     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2544     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2545     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2546 \r
2547     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2548       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2549       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2550       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2551       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2552       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2553       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2554       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2555       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2556       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2557       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2558       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2559       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2560     } else {\r
2561       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2562       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2563       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2564       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2565       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2566       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2567       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2568       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2569       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2570       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2571       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2572       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2573     }\r
2574 \r
2575   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2576     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2577     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2578     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2579     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2580     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2581     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2582     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2583     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2584     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2585     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2586     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2587     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2588     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2589     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2590   }\r
2591 \r
2592 \r
2593   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2594   /* special Shogi support in this size */\r
2595   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2596       for (piece = WhitePawn;\r
2597            (int) piece < (int) BlackPawn;\r
2598            piece = (ChessSquare) ((int) piece + 1)) {\r
2599         if (pieceBitmap[i][piece] != NULL)\r
2600           DeleteObject(pieceBitmap[i][piece]);\r
2601       }\r
2602     }\r
2603   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2604   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2605   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2606   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2607   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2608   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2609   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2610   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2611   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2612   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2613   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2614   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2615   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2616   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2617   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2618   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2619   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2620   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2621   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2622   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2623   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2624   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2625   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2626   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2627   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2628   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2629   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2630   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2631   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2632   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2633   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2634   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2635   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2636   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2637   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2638   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2639   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2640   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2641   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2642   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2643   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2644   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2645   minorSize = 0;\r
2646   }\r
2647 }\r
2648 \r
2649 HBITMAP\r
2650 PieceBitmap(ChessSquare p, int kind)\r
2651 {\r
2652   if ((int) p >= (int) BlackPawn)\r
2653     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2654 \r
2655   return pieceBitmap[kind][(int) p];\r
2656 }\r
2657 \r
2658 /***************************************************************/\r
2659 \r
2660 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2661 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2662 /*\r
2663 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2664 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2665 */\r
2666 \r
2667 VOID\r
2668 SquareToPos(int row, int column, int * x, int * y)\r
2669 {\r
2670   if (flipView) {\r
2671     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2672     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2673   } else {\r
2674     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2675     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2676   }\r
2677 }\r
2678 \r
2679 VOID\r
2680 DrawCoordsOnDC(HDC hdc)\r
2681 {\r
2682   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
2683   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
2684   char str[2] = { NULLCHAR, NULLCHAR };\r
2685   int oldMode, oldAlign, x, y, start, i;\r
2686   HFONT oldFont;\r
2687   HBRUSH oldBrush;\r
2688 \r
2689   if (!appData.showCoords)\r
2690     return;\r
2691 \r
2692   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2693 \r
2694   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2695   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2696   oldAlign = GetTextAlign(hdc);\r
2697   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2698 \r
2699   y = boardRect.top + lineGap;\r
2700   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2701 \r
2702   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2703   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2704     str[0] = files[start + i];\r
2705     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2706     y += squareSize + lineGap;\r
2707   }\r
2708 \r
2709   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2710 \r
2711   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2712   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2713     str[0] = ranks[start + i];\r
2714     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2715     x += squareSize + lineGap;\r
2716   }    \r
2717 \r
2718   SelectObject(hdc, oldBrush);\r
2719   SetBkMode(hdc, oldMode);\r
2720   SetTextAlign(hdc, oldAlign);\r
2721   SelectObject(hdc, oldFont);\r
2722 }\r
2723 \r
2724 VOID\r
2725 DrawGridOnDC(HDC hdc)\r
2726 {\r
2727   HPEN oldPen;\r
2728  \r
2729   if (lineGap != 0) {\r
2730     oldPen = SelectObject(hdc, gridPen);\r
2731     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2732     SelectObject(hdc, oldPen);\r
2733   }\r
2734 }\r
2735 \r
2736 #define HIGHLIGHT_PEN 0\r
2737 #define PREMOVE_PEN   1\r
2738 \r
2739 VOID\r
2740 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2741 {\r
2742   int x1, y1;\r
2743   HPEN oldPen, hPen;\r
2744   if (lineGap == 0) return;\r
2745   if (flipView) {\r
2746     x1 = boardRect.left +\r
2747       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2748     y1 = boardRect.top +\r
2749       lineGap/2 + y * (squareSize + lineGap);\r
2750   } else {\r
2751     x1 = boardRect.left +\r
2752       lineGap/2 + x * (squareSize + lineGap);\r
2753     y1 = boardRect.top +\r
2754       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2755   }\r
2756   hPen = pen ? premovePen : highlightPen;\r
2757   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2758   MoveToEx(hdc, x1, y1, NULL);\r
2759   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2760   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2761   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2762   LineTo(hdc, x1, y1);\r
2763   SelectObject(hdc, oldPen);\r
2764 }\r
2765 \r
2766 VOID\r
2767 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2768 {\r
2769   int i;\r
2770   for (i=0; i<2; i++) {\r
2771     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2772       DrawHighlightOnDC(hdc, TRUE,\r
2773                         h->sq[i].x, h->sq[i].y,\r
2774                         pen);\r
2775   }\r
2776 }\r
2777 \r
2778 /* Note: sqcolor is used only in monoMode */\r
2779 /* Note that this code is largely duplicated in woptions.c,\r
2780    function DrawSampleSquare, so that needs to be updated too */\r
2781 VOID\r
2782 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2783 {\r
2784   HBITMAP oldBitmap;\r
2785   HBRUSH oldBrush;\r
2786   int tmpSize;\r
2787 \r
2788   if (appData.blindfold) return;\r
2789 \r
2790   /* [AS] Use font-based pieces if needed */\r
2791   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2792     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2793     CreatePiecesFromFont();\r
2794 \r
2795     if( fontBitmapSquareSize == squareSize ) {\r
2796         int index = TranslatePieceToFontPiece(piece);\r
2797 \r
2798         SelectObject( tmphdc, hPieceMask[ index ] );\r
2799 \r
2800       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2801         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2802       else\r
2803         BitBlt( hdc,\r
2804             x, y,\r
2805             squareSize, squareSize,\r
2806             tmphdc,\r
2807             0, 0,\r
2808             SRCAND );\r
2809 \r
2810         SelectObject( tmphdc, hPieceFace[ index ] );\r
2811 \r
2812       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2813         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2814       else\r
2815         BitBlt( hdc,\r
2816             x, y,\r
2817             squareSize, squareSize,\r
2818             tmphdc,\r
2819             0, 0,\r
2820             SRCPAINT );\r
2821 \r
2822         return;\r
2823     }\r
2824   }\r
2825 \r
2826   if (appData.monoMode) {\r
2827     SelectObject(tmphdc, PieceBitmap(piece, \r
2828       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2829     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2830            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2831   } else {\r
2832     tmpSize = squareSize;\r
2833     if(minorSize &&\r
2834         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2835          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2836       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2837       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2838       x += (squareSize - minorSize)>>1;\r
2839       y += squareSize - minorSize - 2;\r
2840       tmpSize = minorSize;\r
2841     }\r
2842     if (color || appData.allWhite ) {\r
2843       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2844       if( color )\r
2845               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2846       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2847       if(appData.upsideDown && color==flipView)\r
2848         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2849       else\r
2850         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2851       /* Use black for outline of white pieces */\r
2852       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2853       if(appData.upsideDown && color==flipView)\r
2854         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2855       else\r
2856         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2857     } else {\r
2858       /* Use square color for details of black pieces */\r
2859       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2860       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2861       if(appData.upsideDown && !flipView)\r
2862         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2863       else\r
2864         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2865     }\r
2866     SelectObject(hdc, oldBrush);\r
2867     SelectObject(tmphdc, oldBitmap);\r
2868   }\r
2869 }\r
2870 \r
2871 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2872 int GetBackTextureMode( int algo )\r
2873 {\r
2874     int result = BACK_TEXTURE_MODE_DISABLED;\r
2875 \r
2876     switch( algo ) \r
2877     {\r
2878         case BACK_TEXTURE_MODE_PLAIN:\r
2879             result = 1; /* Always use identity map */\r
2880             break;\r
2881         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2882             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2883             break;\r
2884     }\r
2885 \r
2886     return result;\r
2887 }\r
2888 \r
2889 /* \r
2890     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2891     to handle redraws cleanly (as random numbers would always be different).\r
2892 */\r
2893 VOID RebuildTextureSquareInfo()\r
2894 {\r
2895     BITMAP bi;\r
2896     int lite_w = 0;\r
2897     int lite_h = 0;\r
2898     int dark_w = 0;\r
2899     int dark_h = 0;\r
2900     int row;\r
2901     int col;\r
2902 \r
2903     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2904 \r
2905     if( liteBackTexture != NULL ) {\r
2906         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2907             lite_w = bi.bmWidth;\r
2908             lite_h = bi.bmHeight;\r
2909         }\r
2910     }\r
2911 \r
2912     if( darkBackTexture != NULL ) {\r
2913         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2914             dark_w = bi.bmWidth;\r
2915             dark_h = bi.bmHeight;\r
2916         }\r
2917     }\r
2918 \r
2919     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2920         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2921             if( (col + row) & 1 ) {\r
2922                 /* Lite square */\r
2923                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2924                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2925                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2926                   else\r
2927                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2928                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2929                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2930                   else\r
2931                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2932                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2933                 }\r
2934             }\r
2935             else {\r
2936                 /* Dark square */\r
2937                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2938                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2939                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2940                   else\r
2941                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2942                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2943                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2944                   else\r
2945                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2946                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2947                 }\r
2948             }\r
2949         }\r
2950     }\r
2951 }\r
2952 \r
2953 /* [AS] Arrow highlighting support */\r
2954 \r
2955 static int A_WIDTH = 5; /* Width of arrow body */\r
2956 \r
2957 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2958 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2959 \r
2960 static double Sqr( double x )\r
2961 {\r
2962     return x*x;\r
2963 }\r
2964 \r
2965 static int Round( double x )\r
2966 {\r
2967     return (int) (x + 0.5);\r
2968 }\r
2969 \r
2970 /* Draw an arrow between two points using current settings */\r
2971 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2972 {\r
2973     POINT arrow[7];\r
2974     double dx, dy, j, k, x, y;\r
2975 \r
2976     if( d_x == s_x ) {\r
2977         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2978 \r
2979         arrow[0].x = s_x + A_WIDTH;\r
2980         arrow[0].y = s_y;\r
2981 \r
2982         arrow[1].x = s_x + A_WIDTH;\r
2983         arrow[1].y = d_y - h;\r
2984 \r
2985         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
2986         arrow[2].y = d_y - h;\r
2987 \r
2988         arrow[3].x = d_x;\r
2989         arrow[3].y = d_y;\r
2990 \r
2991         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
2992         arrow[4].y = d_y - h;\r
2993 \r
2994         arrow[5].x = s_x - A_WIDTH;\r
2995         arrow[5].y = d_y - h;\r
2996 \r
2997         arrow[6].x = s_x - A_WIDTH;\r
2998         arrow[6].y = s_y;\r
2999     }\r
3000     else if( d_y == s_y ) {\r
3001         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3002 \r
3003         arrow[0].x = s_x;\r
3004         arrow[0].y = s_y + A_WIDTH;\r
3005 \r
3006         arrow[1].x = d_x - w;\r
3007         arrow[1].y = s_y + A_WIDTH;\r
3008 \r
3009         arrow[2].x = d_x - w;\r
3010         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3011 \r
3012         arrow[3].x = d_x;\r
3013         arrow[3].y = d_y;\r
3014 \r
3015         arrow[4].x = d_x - w;\r
3016         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3017 \r
3018         arrow[5].x = d_x - w;\r
3019         arrow[5].y = s_y - A_WIDTH;\r
3020 \r
3021         arrow[6].x = s_x;\r
3022         arrow[6].y = s_y - A_WIDTH;\r
3023     }\r
3024     else {\r
3025         /* [AS] Needed a lot of paper for this! :-) */\r
3026         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3027         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3028   \r
3029         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3030 \r
3031         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3032 \r
3033         x = s_x;\r
3034         y = s_y;\r
3035 \r
3036         arrow[0].x = Round(x - j);\r
3037         arrow[0].y = Round(y + j*dx);\r
3038 \r
3039         arrow[1].x = Round(x + j);\r
3040         arrow[1].y = Round(y - j*dx);\r
3041 \r
3042         if( d_x > s_x ) {\r
3043             x = (double) d_x - k;\r
3044             y = (double) d_y - k*dy;\r
3045         }\r
3046         else {\r
3047             x = (double) d_x + k;\r
3048             y = (double) d_y + k*dy;\r
3049         }\r
3050 \r
3051         arrow[2].x = Round(x + j);\r
3052         arrow[2].y = Round(y - j*dx);\r
3053 \r
3054         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3055         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3056 \r
3057         arrow[4].x = d_x;\r
3058         arrow[4].y = d_y;\r
3059 \r
3060         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3061         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3062 \r
3063         arrow[6].x = Round(x - j);\r
3064         arrow[6].y = Round(y + j*dx);\r
3065     }\r
3066 \r
3067     Polygon( hdc, arrow, 7 );\r
3068 }\r
3069 \r
3070 /* [AS] Draw an arrow between two squares */\r
3071 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3072 {\r
3073     int s_x, s_y, d_x, d_y;\r
3074     HPEN hpen;\r
3075     HPEN holdpen;\r
3076     HBRUSH hbrush;\r
3077     HBRUSH holdbrush;\r
3078     LOGBRUSH stLB;\r
3079 \r
3080     if( s_col == d_col && s_row == d_row ) {\r
3081         return;\r
3082     }\r
3083 \r
3084     /* Get source and destination points */\r
3085     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3086     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3087 \r
3088     if( d_y > s_y ) {\r
3089         d_y += squareSize / 4;\r
3090     }\r
3091     else if( d_y < s_y ) {\r
3092         d_y += 3 * squareSize / 4;\r
3093     }\r
3094     else {\r
3095         d_y += squareSize / 2;\r
3096     }\r
3097 \r
3098     if( d_x > s_x ) {\r
3099         d_x += squareSize / 4;\r
3100     }\r
3101     else if( d_x < s_x ) {\r
3102         d_x += 3 * squareSize / 4;\r
3103     }\r
3104     else {\r
3105         d_x += squareSize / 2;\r
3106     }\r
3107 \r
3108     s_x += squareSize / 2;\r
3109     s_y += squareSize / 2;\r
3110 \r
3111     /* Adjust width */\r
3112     A_WIDTH = squareSize / 14;\r
3113 \r
3114     /* Draw */\r
3115     stLB.lbStyle = BS_SOLID;\r
3116     stLB.lbColor = appData.highlightArrowColor;\r
3117     stLB.lbHatch = 0;\r
3118 \r
3119     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3120     holdpen = SelectObject( hdc, hpen );\r
3121     hbrush = CreateBrushIndirect( &stLB );\r
3122     holdbrush = SelectObject( hdc, hbrush );\r
3123 \r
3124     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3125 \r
3126     SelectObject( hdc, holdpen );\r
3127     SelectObject( hdc, holdbrush );\r
3128     DeleteObject( hpen );\r
3129     DeleteObject( hbrush );\r
3130 }\r
3131 \r
3132 BOOL HasHighlightInfo()\r
3133 {\r
3134     BOOL result = FALSE;\r
3135 \r
3136     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3137         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3138     {\r
3139         result = TRUE;\r
3140     }\r
3141 \r
3142     return result;\r
3143 }\r
3144 \r
3145 BOOL IsDrawArrowEnabled()\r
3146 {\r
3147     BOOL result = FALSE;\r
3148 \r
3149     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3150         result = TRUE;\r
3151     }\r
3152 \r
3153     return result;\r
3154 }\r
3155 \r
3156 VOID DrawArrowHighlight( HDC hdc )\r
3157 {\r
3158     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3159         DrawArrowBetweenSquares( hdc,\r
3160             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3161             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3162     }\r
3163 }\r
3164 \r
3165 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3166 {\r
3167     HRGN result = NULL;\r
3168 \r
3169     if( HasHighlightInfo() ) {\r
3170         int x1, y1, x2, y2;\r
3171         int sx, sy, dx, dy;\r
3172 \r
3173         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3174         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3175 \r
3176         sx = MIN( x1, x2 );\r
3177         sy = MIN( y1, y2 );\r
3178         dx = MAX( x1, x2 ) + squareSize;\r
3179         dy = MAX( y1, y2 ) + squareSize;\r
3180 \r
3181         result = CreateRectRgn( sx, sy, dx, dy );\r
3182     }\r
3183 \r
3184     return result;\r
3185 }\r
3186 \r
3187 /*\r
3188     Warning: this function modifies the behavior of several other functions. \r
3189     \r
3190     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3191     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3192     repaint is scattered all over the place, which is not good for features such as\r
3193     "arrow highlighting" that require a full repaint of the board.\r
3194 \r
3195     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3196     user interaction, when speed is not so important) but especially to avoid errors\r
3197     in the displayed graphics.\r
3198 \r
3199     In such patched places, I always try refer to this function so there is a single\r
3200     place to maintain knowledge.\r
3201     \r
3202     To restore the original behavior, just return FALSE unconditionally.\r
3203 */\r
3204 BOOL IsFullRepaintPreferrable()\r
3205 {\r
3206     BOOL result = FALSE;\r
3207 \r
3208     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3209         /* Arrow may appear on the board */\r
3210         result = TRUE;\r
3211     }\r
3212 \r
3213     return result;\r
3214 }\r
3215 \r
3216 /* \r
3217     This function is called by DrawPosition to know whether a full repaint must\r
3218     be forced or not.\r
3219 \r
3220     Only DrawPosition may directly call this function, which makes use of \r
3221     some state information. Other function should call DrawPosition specifying \r
3222     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3223 */\r
3224 BOOL DrawPositionNeedsFullRepaint()\r
3225 {\r
3226     BOOL result = FALSE;\r
3227 \r
3228     /* \r
3229         Probably a slightly better policy would be to trigger a full repaint\r
3230         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3231         but animation is fast enough that it's difficult to notice.\r
3232     */\r
3233     if( animInfo.piece == EmptySquare ) {\r
3234         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3235             result = TRUE;\r
3236         }\r
3237     }\r
3238 \r
3239     return result;\r
3240 }\r
3241 \r
3242 VOID\r
3243 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3244 {\r
3245   int row, column, x, y, square_color, piece_color;\r
3246   ChessSquare piece;\r
3247   HBRUSH oldBrush;\r
3248   HDC texture_hdc = NULL;\r
3249 \r
3250   /* [AS] Initialize background textures if needed */\r
3251   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3252       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3253       if( backTextureSquareSize != squareSize \r
3254        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3255           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3256           backTextureSquareSize = squareSize;\r
3257           RebuildTextureSquareInfo();\r
3258       }\r
3259 \r
3260       texture_hdc = CreateCompatibleDC( hdc );\r
3261   }\r
3262 \r
3263   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3264     for (column = 0; column < BOARD_WIDTH; column++) {\r
3265   \r
3266       SquareToPos(row, column, &x, &y);\r
3267 \r
3268       piece = board[row][column];\r
3269 \r
3270       square_color = ((column + row) % 2) == 1;\r
3271       if( gameInfo.variant == VariantXiangqi ) {\r
3272           square_color = !InPalace(row, column);\r
3273           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3274           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3275       }\r
3276       piece_color = (int) piece < (int) BlackPawn;\r
3277 \r
3278 \r
3279       /* [HGM] holdings file: light square or black */\r
3280       if(column == BOARD_LEFT-2) {\r
3281             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3282                 square_color = 1;\r
3283             else {\r
3284                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3285                 continue;\r
3286             }\r
3287       } else\r
3288       if(column == BOARD_RGHT + 1 ) {\r
3289             if( row < gameInfo.holdingsSize )\r
3290                 square_color = 1;\r
3291             else {\r
3292                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3293                 continue;\r
3294             }\r
3295       }\r
3296       if(column == BOARD_LEFT-1 ) /* left align */\r
3297             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3298       else if( column == BOARD_RGHT) /* right align */\r
3299             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3300       else\r
3301       if (appData.monoMode) {\r
3302         if (piece == EmptySquare) {\r
3303           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3304                  square_color ? WHITENESS : BLACKNESS);\r
3305         } else {\r
3306           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3307         }\r
3308       } \r
3309       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3310           /* [AS] Draw the square using a texture bitmap */\r
3311           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3312           int r = row, c = column; // [HGM] do not flip board in flipView\r
3313           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3314 \r
3315           DrawTile( x, y, \r
3316               squareSize, squareSize, \r
3317               hdc, \r
3318               texture_hdc,\r
3319               backTextureSquareInfo[r][c].mode,\r
3320               backTextureSquareInfo[r][c].x,\r
3321               backTextureSquareInfo[r][c].y );\r
3322 \r
3323           SelectObject( texture_hdc, hbm );\r
3324 \r
3325           if (piece != EmptySquare) {\r
3326               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3327           }\r
3328       }\r
3329       else {\r
3330         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3331 \r
3332         oldBrush = SelectObject(hdc, brush );\r
3333         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3334         SelectObject(hdc, oldBrush);\r
3335         if (piece != EmptySquare)\r
3336           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3337       }\r
3338     }\r
3339   }\r
3340 \r
3341   if( texture_hdc != NULL ) {\r
3342     DeleteDC( texture_hdc );\r
3343   }\r
3344 }\r
3345 \r
3346 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3347 void fputDW(FILE *f, int x)\r
3348 {\r
3349         fputc(x     & 255, f);\r
3350         fputc(x>>8  & 255, f);\r
3351         fputc(x>>16 & 255, f);\r
3352         fputc(x>>24 & 255, f);\r
3353 }\r
3354 \r
3355 #define MAX_CLIPS 200   /* more than enough */\r
3356 \r
3357 VOID\r
3358 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3359 {\r
3360 //  HBITMAP bufferBitmap;\r
3361   BITMAP bi;\r
3362 //  RECT Rect;\r
3363   HDC tmphdc;\r
3364   HBITMAP hbm;\r
3365   int w = 100, h = 50;\r
3366 \r
3367   if(logo == NULL) return;\r
3368 //  GetClientRect(hwndMain, &Rect);\r
3369 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3370 //                                      Rect.bottom-Rect.top+1);\r
3371   tmphdc = CreateCompatibleDC(hdc);\r
3372   hbm = SelectObject(tmphdc, logo);\r
3373   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3374             w = bi.bmWidth;\r
3375             h = bi.bmHeight;\r
3376   }\r
3377   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3378                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3379   SelectObject(tmphdc, hbm);\r
3380   DeleteDC(tmphdc);\r
3381 }\r
3382 \r
3383 static HDC hdcSeek;\r
3384 \r
3385 // [HGM] seekgraph\r
3386 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3387 {\r
3388     POINT stPt;\r
3389     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3390     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3391     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3392     SelectObject( hdcSeek, hp );\r
3393 }\r
3394 \r
3395 // front-end wrapper for drawing functions to do rectangles\r
3396 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3397 {\r
3398     HPEN hp;\r
3399     RECT rc;\r
3400 \r
3401     if (hdcSeek == NULL) {\r
3402     hdcSeek = GetDC(hwndMain);\r
3403       if (!appData.monoMode) {\r
3404         SelectPalette(hdcSeek, hPal, FALSE);\r
3405         RealizePalette(hdcSeek);\r
3406       }\r
3407     }\r
3408     hp = SelectObject( hdcSeek, gridPen );\r
3409     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3410     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3411     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3412     SelectObject( hdcSeek, hp );\r
3413 }\r
3414 \r
3415 // front-end wrapper for putting text in graph\r
3416 void DrawSeekText(char *buf, int x, int y)\r
3417 {\r
3418         SIZE stSize;\r
3419         SetBkMode( hdcSeek, TRANSPARENT );\r
3420         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3421         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3422 }\r
3423 \r
3424 void DrawSeekDot(int x, int y, int color)\r
3425 {\r
3426         int square = color & 0x80;\r
3427         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3428                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3429         color &= 0x7F;\r
3430         if(square)\r
3431             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3432                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3433         else\r
3434             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3435                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3436             SelectObject(hdcSeek, oldBrush);\r
3437 }\r
3438 \r
3439 VOID\r
3440 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3441 {\r
3442   static Board lastReq[2], lastDrawn[2];\r
3443   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3444   static int lastDrawnFlipView = 0;\r
3445   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3446   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3447   HDC tmphdc;\r
3448   HDC hdcmem;\r
3449   HBITMAP bufferBitmap;\r
3450   HBITMAP oldBitmap;\r
3451   RECT Rect;\r
3452   HRGN clips[MAX_CLIPS];\r
3453   ChessSquare dragged_piece = EmptySquare;\r
3454   int nr = twoBoards*partnerUp;\r
3455 \r
3456   /* I'm undecided on this - this function figures out whether a full\r
3457    * repaint is necessary on its own, so there's no real reason to have the\r
3458    * caller tell it that.  I think this can safely be set to FALSE - but\r
3459    * if we trust the callers not to request full repaints unnessesarily, then\r
3460    * we could skip some clipping work.  In other words, only request a full\r
3461    * redraw when the majority of pieces have changed positions (ie. flip, \r
3462    * gamestart and similar)  --Hawk\r
3463    */\r
3464   Boolean fullrepaint = repaint;\r
3465 \r
3466   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3467 \r
3468   if( DrawPositionNeedsFullRepaint() ) {\r
3469       fullrepaint = TRUE;\r
3470   }\r
3471 \r
3472   if (board == NULL) {\r
3473     if (!lastReqValid[nr]) {\r
3474       return;\r
3475     }\r
3476     board = lastReq[nr];\r
3477   } else {\r
3478     CopyBoard(lastReq[nr], board);\r
3479     lastReqValid[nr] = 1;\r
3480   }\r
3481 \r
3482   if (doingSizing) {\r
3483     return;\r
3484   }\r
3485 \r
3486   if (IsIconic(hwndMain)) {\r
3487     return;\r
3488   }\r
3489 \r
3490   if (hdc == NULL) {\r
3491     hdc = GetDC(hwndMain);\r
3492     if (!appData.monoMode) {\r
3493       SelectPalette(hdc, hPal, FALSE);\r
3494       RealizePalette(hdc);\r
3495     }\r
3496     releaseDC = TRUE;\r
3497   } else {\r
3498     releaseDC = FALSE;\r
3499   }\r
3500 \r
3501   /* Create some work-DCs */\r
3502   hdcmem = CreateCompatibleDC(hdc);\r
3503   tmphdc = CreateCompatibleDC(hdc);\r
3504 \r
3505   /* If dragging is in progress, we temporarely remove the piece */\r
3506   /* [HGM] or temporarily decrease count if stacked              */\r
3507   /*       !! Moved to before board compare !!                   */\r
3508   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3509     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3510     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3511             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3512         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3513     } else \r
3514     if(dragInfo.from.x == BOARD_RGHT+1) {\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         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3519   }\r
3520 \r
3521   /* Figure out which squares need updating by comparing the \r
3522    * newest board with the last drawn board and checking if\r
3523    * flipping has changed.\r
3524    */\r
3525   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3526     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3527       for (column = 0; column < BOARD_WIDTH; column++) {\r
3528         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3529           SquareToPos(row, column, &x, &y);\r
3530           clips[num_clips++] =\r
3531             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3532         }\r
3533       }\r
3534     }\r
3535    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3536     for (i=0; i<2; i++) {\r
3537       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3538           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3539         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3540             lastDrawnHighlight.sq[i].y >= 0) {\r
3541           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3542                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3543           clips[num_clips++] =\r
3544             CreateRectRgn(x - lineGap, y - lineGap, \r
3545                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3546         }\r
3547         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3548           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3549           clips[num_clips++] =\r
3550             CreateRectRgn(x - lineGap, y - lineGap, \r
3551                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3552         }\r
3553       }\r
3554     }\r
3555     for (i=0; i<2; i++) {\r
3556       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3557           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3558         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3559             lastDrawnPremove.sq[i].y >= 0) {\r
3560           SquareToPos(lastDrawnPremove.sq[i].y,\r
3561                       lastDrawnPremove.sq[i].x, &x, &y);\r
3562           clips[num_clips++] =\r
3563             CreateRectRgn(x - lineGap, y - lineGap, \r
3564                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3565         }\r
3566         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3567             premoveHighlightInfo.sq[i].y >= 0) {\r
3568           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3569                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3570           clips[num_clips++] =\r
3571             CreateRectRgn(x - lineGap, y - lineGap, \r
3572                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3573         }\r
3574       }\r
3575     }\r
3576    } else { // nr == 1\r
3577         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3578         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3579         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3580         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3581       for (i=0; i<2; i++) {\r
3582         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3583             partnerHighlightInfo.sq[i].y >= 0) {\r
3584           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3585                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3586           clips[num_clips++] =\r
3587             CreateRectRgn(x - lineGap, y - lineGap, \r
3588                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3589         }\r
3590         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3591             oldPartnerHighlight.sq[i].y >= 0) {\r
3592           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3593                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3594           clips[num_clips++] =\r
3595             CreateRectRgn(x - lineGap, y - lineGap, \r
3596                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3597         }\r
3598       }\r
3599    }\r
3600   } else {\r
3601     fullrepaint = TRUE;\r
3602   }\r
3603 \r
3604   /* Create a buffer bitmap - this is the actual bitmap\r
3605    * being written to.  When all the work is done, we can\r
3606    * copy it to the real DC (the screen).  This avoids\r
3607    * the problems with flickering.\r
3608    */\r
3609   GetClientRect(hwndMain, &Rect);\r
3610   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3611                                         Rect.bottom-Rect.top+1);\r
3612   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3613   if (!appData.monoMode) {\r
3614     SelectPalette(hdcmem, hPal, FALSE);\r
3615   }\r
3616 \r
3617   /* Create clips for dragging */\r
3618   if (!fullrepaint) {\r
3619     if (dragInfo.from.x >= 0) {\r
3620       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3621       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3622     }\r
3623     if (dragInfo.start.x >= 0) {\r
3624       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3625       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3626     }\r
3627     if (dragInfo.pos.x >= 0) {\r
3628       x = dragInfo.pos.x - squareSize / 2;\r
3629       y = dragInfo.pos.y - squareSize / 2;\r
3630       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3631     }\r
3632     if (dragInfo.lastpos.x >= 0) {\r
3633       x = dragInfo.lastpos.x - squareSize / 2;\r
3634       y = dragInfo.lastpos.y - squareSize / 2;\r
3635       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3636     }\r
3637   }\r
3638 \r
3639   /* Are we animating a move?  \r
3640    * If so, \r
3641    *   - remove the piece from the board (temporarely)\r
3642    *   - calculate the clipping region\r
3643    */\r
3644   if (!fullrepaint) {\r
3645     if (animInfo.piece != EmptySquare) {\r
3646       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3647       x = boardRect.left + animInfo.lastpos.x;\r
3648       y = boardRect.top + animInfo.lastpos.y;\r
3649       x2 = boardRect.left + animInfo.pos.x;\r
3650       y2 = boardRect.top + animInfo.pos.y;\r
3651       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3652       /* Slight kludge.  The real problem is that after AnimateMove is\r
3653          done, the position on the screen does not match lastDrawn.\r
3654          This currently causes trouble only on e.p. captures in\r
3655          atomic, where the piece moves to an empty square and then\r
3656          explodes.  The old and new positions both had an empty square\r
3657          at the destination, but animation has drawn a piece there and\r
3658          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3659       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3660     }\r
3661   }\r
3662 \r
3663   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3664   if (num_clips == 0)\r
3665     fullrepaint = TRUE;\r
3666 \r
3667   /* Set clipping on the memory DC */\r
3668   if (!fullrepaint) {\r
3669     SelectClipRgn(hdcmem, clips[0]);\r
3670     for (x = 1; x < num_clips; x++) {\r
3671       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3672         abort();  // this should never ever happen!\r
3673     }\r
3674   }\r
3675 \r
3676   /* Do all the drawing to the memory DC */\r
3677   if(explodeInfo.radius) { // [HGM] atomic\r
3678         HBRUSH oldBrush;\r
3679         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3680         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3681         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3682         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3683         x += squareSize/2;\r
3684         y += squareSize/2;\r
3685         if(!fullrepaint) {\r
3686           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3687           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3688         }\r
3689         DrawGridOnDC(hdcmem);\r
3690         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3691         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3692         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3693         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3694         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3695         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3696         SelectObject(hdcmem, oldBrush);\r
3697   } else {\r
3698     DrawGridOnDC(hdcmem);\r
3699     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3700         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3701         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3702     } else {\r
3703         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3704         oldPartnerHighlight = partnerHighlightInfo;\r
3705     }\r
3706     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3707   }\r
3708   if(nr == 0) // [HGM] dual: markers only on left board\r
3709   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3710     for (column = 0; column < BOARD_WIDTH; column++) {\r
3711         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3712             HBRUSH oldBrush = SelectObject(hdcmem, \r
3713                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3714             SquareToPos(row, column, &x, &y);\r
3715             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3716                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3717             SelectObject(hdcmem, oldBrush);\r
3718         }\r
3719     }\r
3720   }\r
3721   if(logoHeight) {\r
3722         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3723         if(appData.autoLogo) {\r
3724           \r
3725           switch(gameMode) { // pick logos based on game mode\r
3726             case IcsObserving:\r
3727                 whiteLogo = second.programLogo; // ICS logo\r
3728                 blackLogo = second.programLogo;\r
3729             default:\r
3730                 break;\r
3731             case IcsPlayingWhite:\r
3732                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3733                 blackLogo = second.programLogo; // ICS logo\r
3734                 break;\r
3735             case IcsPlayingBlack:\r
3736                 whiteLogo = second.programLogo; // ICS logo\r
3737                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3738                 break;\r
3739             case TwoMachinesPlay:\r
3740                 if(first.twoMachinesColor[0] == 'b') {\r
3741                     whiteLogo = second.programLogo;\r
3742                     blackLogo = first.programLogo;\r
3743                 }\r
3744                 break;\r
3745             case MachinePlaysWhite:\r
3746                 blackLogo = userLogo;\r
3747                 break;\r
3748             case MachinePlaysBlack:\r
3749                 whiteLogo = userLogo;\r
3750                 blackLogo = first.programLogo;\r
3751           }\r
3752         }\r
3753         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3754         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3755   }\r
3756 \r
3757   if( appData.highlightMoveWithArrow ) {\r
3758     DrawArrowHighlight(hdcmem);\r
3759   }\r
3760 \r
3761   DrawCoordsOnDC(hdcmem);\r
3762 \r
3763   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3764                  /* to make sure lastDrawn contains what is actually drawn */\r
3765 \r
3766   /* Put the dragged piece back into place and draw it (out of place!) */\r
3767     if (dragged_piece != EmptySquare) {\r
3768     /* [HGM] or restack */\r
3769     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3770                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3771     else\r
3772     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3773                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3774     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3775     x = dragInfo.pos.x - squareSize / 2;\r
3776     y = dragInfo.pos.y - squareSize / 2;\r
3777     DrawPieceOnDC(hdcmem, dragged_piece,\r
3778                   ((int) dragged_piece < (int) BlackPawn), \r
3779                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3780   }   \r
3781   \r
3782   /* Put the animated piece back into place and draw it */\r
3783   if (animInfo.piece != EmptySquare) {\r
3784     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3785     x = boardRect.left + animInfo.pos.x;\r
3786     y = boardRect.top + animInfo.pos.y;\r
3787     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3788                   ((int) animInfo.piece < (int) BlackPawn),\r
3789                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3790   }\r
3791 \r
3792   /* Release the bufferBitmap by selecting in the old bitmap \r
3793    * and delete the memory DC\r
3794    */\r
3795   SelectObject(hdcmem, oldBitmap);\r
3796   DeleteDC(hdcmem);\r
3797 \r
3798   /* Set clipping on the target DC */\r
3799   if (!fullrepaint) {\r
3800     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3801         RECT rect;\r
3802         GetRgnBox(clips[x], &rect);\r
3803         DeleteObject(clips[x]);\r
3804         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3805                           rect.right + wpMain.width/2, rect.bottom);\r
3806     }\r
3807     SelectClipRgn(hdc, clips[0]);\r
3808     for (x = 1; x < num_clips; x++) {\r
3809       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3810         abort();   // this should never ever happen!\r
3811     } \r
3812   }\r
3813 \r
3814   /* Copy the new bitmap onto the screen in one go.\r
3815    * This way we avoid any flickering\r
3816    */\r
3817   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3818   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3819          boardRect.right - boardRect.left,\r
3820          boardRect.bottom - boardRect.top,\r
3821          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3822   if(saveDiagFlag) { \r
3823     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
3824     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3825 \r
3826     GetObject(bufferBitmap, sizeof(b), &b);\r
3827     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
3828         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3829         bih.biWidth = b.bmWidth;\r
3830         bih.biHeight = b.bmHeight;\r
3831         bih.biPlanes = 1;\r
3832         bih.biBitCount = b.bmBitsPixel;\r
3833         bih.biCompression = 0;\r
3834         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3835         bih.biXPelsPerMeter = 0;\r
3836         bih.biYPelsPerMeter = 0;\r
3837         bih.biClrUsed = 0;\r
3838         bih.biClrImportant = 0;\r
3839 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3840 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3841         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3842 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3843 \r
3844         wb = b.bmWidthBytes;\r
3845         // count colors\r
3846         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3847                 int k = ((int*) pData)[i];\r
3848                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3849                 if(j >= 16) break;\r
3850                 color[j] = k;\r
3851                 if(j >= nrColors) nrColors = j+1;\r
3852         }\r
3853         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3854                 INT p = 0;\r
3855                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3856                     for(w=0; w<(wb>>2); w+=2) {\r
3857                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3858                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3859                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3860                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3861                         pData[p++] = m | j<<4;\r
3862                     }\r
3863                     while(p&3) pData[p++] = 0;\r
3864                 }\r
3865                 fac = 3;\r
3866                 wb = ((wb+31)>>5)<<2;\r
3867         }\r
3868         // write BITMAPFILEHEADER\r
3869         fprintf(diagFile, "BM");\r
3870         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3871         fputDW(diagFile, 0);\r
3872         fputDW(diagFile, 0x36 + (fac?64:0));\r
3873         // write BITMAPINFOHEADER\r
3874         fputDW(diagFile, 40);\r
3875         fputDW(diagFile, b.bmWidth);\r
3876         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3877         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3878         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3879         fputDW(diagFile, 0);\r
3880         fputDW(diagFile, 0);\r
3881         fputDW(diagFile, 0);\r
3882         fputDW(diagFile, 0);\r
3883         fputDW(diagFile, 0);\r
3884         fputDW(diagFile, 0);\r
3885         // write color table\r
3886         if(fac)\r
3887         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3888         // write bitmap data\r
3889         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3890                 fputc(pData[i], diagFile);\r
3891      }\r
3892   }\r
3893 \r
3894   SelectObject(tmphdc, oldBitmap);\r
3895 \r
3896   /* Massive cleanup */\r
3897   for (x = 0; x < num_clips; x++)\r
3898     DeleteObject(clips[x]);\r
3899 \r
3900   DeleteDC(tmphdc);\r
3901   DeleteObject(bufferBitmap);\r
3902 \r
3903   if (releaseDC) \r
3904     ReleaseDC(hwndMain, hdc);\r
3905   \r
3906   if (lastDrawnFlipView != flipView && nr == 0) {\r
3907     if (flipView)\r
3908       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3909     else\r
3910       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3911   }\r
3912 \r
3913 /*  CopyBoard(lastDrawn, board);*/\r
3914   lastDrawnHighlight = highlightInfo;\r
3915   lastDrawnPremove   = premoveHighlightInfo;\r
3916   lastDrawnFlipView = flipView;\r
3917   lastDrawnValid[nr] = 1;\r
3918 }\r
3919 \r
3920 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3921 int\r
3922 SaveDiagram(f)\r
3923      FILE *f;\r
3924 {\r
3925     saveDiagFlag = 1; diagFile = f;\r
3926     HDCDrawPosition(NULL, TRUE, NULL);\r
3927 \r
3928     saveDiagFlag = 0;\r
3929 \r
3930 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
3931     \r
3932     fclose(f);\r
3933     return TRUE;\r
3934 }\r
3935 \r
3936 \r
3937 /*---------------------------------------------------------------------------*\\r
3938 | CLIENT PAINT PROCEDURE\r
3939 |   This is the main event-handler for the WM_PAINT message.\r
3940 |\r
3941 \*---------------------------------------------------------------------------*/\r
3942 VOID\r
3943 PaintProc(HWND hwnd)\r
3944 {\r
3945   HDC         hdc;\r
3946   PAINTSTRUCT ps;\r
3947   HFONT       oldFont;\r
3948 \r
3949   if((hdc = BeginPaint(hwnd, &ps))) {\r
3950     if (IsIconic(hwnd)) {\r
3951       DrawIcon(hdc, 2, 2, iconCurrent);\r
3952     } else {\r
3953       if (!appData.monoMode) {\r
3954         SelectPalette(hdc, hPal, FALSE);\r
3955         RealizePalette(hdc);\r
3956       }\r
3957       HDCDrawPosition(hdc, 1, NULL);\r
3958       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3959         flipView = !flipView; partnerUp = !partnerUp;\r
3960         HDCDrawPosition(hdc, 1, NULL);\r
3961         flipView = !flipView; partnerUp = !partnerUp;\r
3962       }\r
3963       oldFont =\r
3964         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3965       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3966                  ETO_CLIPPED|ETO_OPAQUE,\r
3967                  &messageRect, messageText, strlen(messageText), NULL);\r
3968       SelectObject(hdc, oldFont);\r
3969       DisplayBothClocks();\r
3970     }\r
3971     EndPaint(hwnd,&ps);\r
3972   }\r
3973 \r
3974   return;\r
3975 }\r
3976 \r
3977 \r
3978 /*\r
3979  * If the user selects on a border boundary, return -1; if off the board,\r
3980  *   return -2.  Otherwise map the event coordinate to the square.\r
3981  * The offset boardRect.left or boardRect.top must already have been\r
3982  *   subtracted from x.\r
3983  */\r
3984 int EventToSquare(x, limit)\r
3985      int x, limit;\r
3986 {\r
3987   if (x <= 0)\r
3988     return -2;\r
3989   if (x < lineGap)\r
3990     return -1;\r
3991   x -= lineGap;\r
3992   if ((x % (squareSize + lineGap)) >= squareSize)\r
3993     return -1;\r
3994   x /= (squareSize + lineGap);\r
3995     if (x >= limit)\r
3996     return -2;\r
3997   return x;\r
3998 }\r
3999 \r
4000 typedef struct {\r
4001   char piece;\r
4002   int command;\r
4003   char* name;\r
4004 } DropEnable;\r
4005 \r
4006 DropEnable dropEnables[] = {\r
4007   { 'P', DP_Pawn, N_("Pawn") },\r
4008   { 'N', DP_Knight, N_("Knight") },\r
4009   { 'B', DP_Bishop, N_("Bishop") },\r
4010   { 'R', DP_Rook, N_("Rook") },\r
4011   { 'Q', DP_Queen, N_("Queen") },\r
4012 };\r
4013 \r
4014 VOID\r
4015 SetupDropMenu(HMENU hmenu)\r
4016 {\r
4017   int i, count, enable;\r
4018   char *p;\r
4019   extern char white_holding[], black_holding[];\r
4020   char item[MSG_SIZ];\r
4021 \r
4022   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4023     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4024                dropEnables[i].piece);\r
4025     count = 0;\r
4026     while (p && *p++ == dropEnables[i].piece) count++;\r
4027       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4028     enable = count > 0 || !appData.testLegality\r
4029       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4030                       && !appData.icsActive);\r
4031     ModifyMenu(hmenu, dropEnables[i].command,\r
4032                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4033                dropEnables[i].command, item);\r
4034   }\r
4035 }\r
4036 \r
4037 void DragPieceBegin(int x, int y)\r
4038 {\r
4039       dragInfo.lastpos.x = boardRect.left + x;\r
4040       dragInfo.lastpos.y = boardRect.top + y;\r
4041       dragInfo.from.x = fromX;\r
4042       dragInfo.from.y = fromY;\r
4043       dragInfo.start = dragInfo.from;\r
4044       SetCapture(hwndMain);\r
4045 }\r
4046 \r
4047 void DragPieceEnd(int x, int y)\r
4048 {\r
4049     ReleaseCapture();\r
4050     dragInfo.start.x = dragInfo.start.y = -1;\r
4051     dragInfo.from = dragInfo.start;\r
4052     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4053 }\r
4054 \r
4055 /* Event handler for mouse messages */\r
4056 VOID\r
4057 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4058 {\r
4059   int x, y, menuNr;\r
4060   POINT pt;\r
4061   static int recursive = 0;\r
4062   HMENU hmenu;\r
4063   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4064 \r
4065   if (recursive) {\r
4066     if (message == WM_MBUTTONUP) {\r
4067       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4068          to the middle button: we simulate pressing the left button too!\r
4069          */\r
4070       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4071       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4072     }\r
4073     return;\r
4074   }\r
4075   recursive++;\r
4076   \r
4077   pt.x = LOWORD(lParam);\r
4078   pt.y = HIWORD(lParam);\r
4079   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4080   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4081   if (!flipView && y >= 0) {\r
4082     y = BOARD_HEIGHT - 1 - y;\r
4083   }\r
4084   if (flipView && x >= 0) {\r
4085     x = BOARD_WIDTH - 1 - x;\r
4086   }\r
4087 \r
4088   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4089 \r
4090   switch (message) {\r
4091   case WM_LBUTTONDOWN:\r
4092       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4093         if (gameMode == EditPosition) {\r
4094           SetWhiteToPlayEvent();\r
4095         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4096           AdjustClock(flipClock, -1);\r
4097         } else if (gameMode == IcsPlayingBlack ||\r
4098                    gameMode == MachinePlaysWhite) {\r
4099           CallFlagEvent();\r
4100         }\r
4101       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4102         if (gameMode == EditPosition) {\r
4103           SetBlackToPlayEvent();\r
4104         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4105           AdjustClock(!flipClock, -1);\r
4106         } else if (gameMode == IcsPlayingWhite ||\r
4107                    gameMode == MachinePlaysBlack) {\r
4108           CallFlagEvent();\r
4109         }\r
4110       }\r
4111       dragInfo.start.x = dragInfo.start.y = -1;\r
4112       dragInfo.from = dragInfo.start;\r
4113     if(fromX == -1 && frozen) { // not sure where this is for\r
4114                 fromX = fromY = -1; \r
4115       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4116       break;\r
4117     }\r
4118       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4119       DrawPosition(TRUE, NULL);\r
4120     break;\r
4121 \r
4122   case WM_LBUTTONUP:\r
4123       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4124       DrawPosition(TRUE, NULL);\r
4125     break;\r
4126 \r
4127   case WM_MOUSEMOVE:\r
4128     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4129     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4130     if ((appData.animateDragging || appData.highlightDragging)\r
4131         && (wParam & MK_LBUTTON)\r
4132         && dragInfo.from.x >= 0) \r
4133     {\r
4134       BOOL full_repaint = FALSE;\r
4135 \r
4136       if (appData.animateDragging) {\r
4137         dragInfo.pos = pt;\r
4138       }\r
4139       if (appData.highlightDragging) {\r
4140         SetHighlights(fromX, fromY, x, y);\r
4141         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4142             full_repaint = TRUE;\r
4143         }\r
4144       }\r
4145       \r
4146       DrawPosition( full_repaint, NULL);\r
4147       \r
4148       dragInfo.lastpos = dragInfo.pos;\r
4149     }\r
4150     break;\r
4151 \r
4152   case WM_MOUSEWHEEL: // [DM]\r
4153     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4154        /* Mouse Wheel is being rolled forward\r
4155         * Play moves forward\r
4156         */\r
4157        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4158                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4159        /* Mouse Wheel is being rolled backward\r
4160         * Play moves backward\r
4161         */\r
4162        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4163                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4164     }\r
4165     break;\r
4166 \r
4167   case WM_MBUTTONUP:\r
4168   case WM_RBUTTONUP:\r
4169     ReleaseCapture();\r
4170     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4171     break;\r
4172  \r
4173   case WM_MBUTTONDOWN:\r
4174   case WM_RBUTTONDOWN:\r
4175     ErrorPopDown();\r
4176     ReleaseCapture();\r
4177     fromX = fromY = -1;\r
4178     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4179     dragInfo.start.x = dragInfo.start.y = -1;\r
4180     dragInfo.from = dragInfo.start;\r
4181     dragInfo.lastpos = dragInfo.pos;\r
4182     if (appData.highlightDragging) {\r
4183       ClearHighlights();\r
4184     }\r
4185     if(y == -2) {\r
4186       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4187       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4188           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4189       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4190           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4191       }\r
4192       break;\r
4193     }\r
4194     DrawPosition(TRUE, NULL);\r
4195 \r
4196     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4197     switch (menuNr) {\r
4198     case 0:\r
4199       if (message == WM_MBUTTONDOWN) {\r
4200         buttonCount = 3;  /* even if system didn't think so */\r
4201         if (wParam & MK_SHIFT) \r
4202           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4203         else\r
4204           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4205       } else { /* message == WM_RBUTTONDOWN */\r
4206         /* Just have one menu, on the right button.  Windows users don't\r
4207            think to try the middle one, and sometimes other software steals\r
4208            it, or it doesn't really exist. */\r
4209         if(gameInfo.variant != VariantShogi)\r
4210             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4211         else\r
4212             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4213       }\r
4214       break;\r
4215     case 2:\r
4216       SetCapture(hwndMain);
4217       break;\r
4218     case 1:\r
4219       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4220       SetupDropMenu(hmenu);\r
4221       MenuPopup(hwnd, pt, hmenu, -1);\r
4222     default:\r
4223       break;\r
4224     }\r
4225     break;\r
4226   }\r
4227 \r
4228   recursive--;\r
4229 }\r
4230 \r
4231 /* Preprocess messages for buttons in main window */\r
4232 LRESULT CALLBACK\r
4233 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4234 {\r
4235   int id = GetWindowLong(hwnd, GWL_ID);\r
4236   int i, dir;\r
4237 \r
4238   for (i=0; i<N_BUTTONS; i++) {\r
4239     if (buttonDesc[i].id == id) break;\r
4240   }\r
4241   if (i == N_BUTTONS) return 0;\r
4242   switch (message) {\r
4243   case WM_KEYDOWN:\r
4244     switch (wParam) {\r
4245     case VK_LEFT:\r
4246     case VK_RIGHT:\r
4247       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4248       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4249       return TRUE;\r
4250     }\r
4251     break;\r
4252   case WM_CHAR:\r
4253     switch (wParam) {\r
4254     case '\r':\r
4255       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4256       return TRUE;\r
4257     default:\r
4258       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4259         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4260         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4261         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4262         SetFocus(h);\r
4263         SendMessage(h, WM_CHAR, wParam, lParam);\r
4264         return TRUE;\r
4265       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4266         PopUpMoveDialog((char)wParam);\r
4267       }\r
4268       break;\r
4269     }\r
4270     break;\r
4271   }\r
4272   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4273 }\r
4274 \r
4275 /* Process messages for Promotion dialog box */\r
4276 LRESULT CALLBACK\r
4277 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4278 {\r
4279   char promoChar;\r
4280 \r
4281   switch (message) {\r
4282   case WM_INITDIALOG: /* message: initialize dialog box */\r
4283     /* Center the dialog over the application window */\r
4284     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4285     Translate(hDlg, DLG_PromotionKing);\r
4286     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4287       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4288        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4289                SW_SHOW : SW_HIDE);\r
4290     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4291     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4292        ((PieceToChar(WhiteAngel) >= 'A' &&\r
4293          PieceToChar(WhiteAngel) != '~') ||\r
4294         (PieceToChar(BlackAngel) >= 'A' &&\r
4295          PieceToChar(BlackAngel) != '~')   ) ?\r
4296                SW_SHOW : SW_HIDE);\r
4297     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4298        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
4299          PieceToChar(WhiteMarshall) != '~') ||\r
4300         (PieceToChar(BlackMarshall) >= 'A' &&\r
4301          PieceToChar(BlackMarshall) != '~')   ) ?\r
4302                SW_SHOW : SW_HIDE);\r
4303     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4304     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4305        gameInfo.variant != VariantShogi ?\r
4306                SW_SHOW : SW_HIDE);\r
4307     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4308        gameInfo.variant != VariantShogi ?\r
4309                SW_SHOW : SW_HIDE);\r
4310     if(gameInfo.variant == VariantShogi) {\r
4311         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4312         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4313         SetWindowText(hDlg, "Promote?");\r
4314     }\r
4315     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4316        gameInfo.variant == VariantSuper ?\r
4317                SW_SHOW : SW_HIDE);\r
4318     return TRUE;\r
4319 \r
4320   case WM_COMMAND: /* message: received a command */\r
4321     switch (LOWORD(wParam)) {\r
4322     case IDCANCEL:\r
4323       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4324       ClearHighlights();\r
4325       DrawPosition(FALSE, NULL);\r
4326       return TRUE;\r
4327     case PB_King:\r
4328       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4329       break;\r
4330     case PB_Queen:\r
4331       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
4332       break;\r
4333     case PB_Rook:\r
4334       promoChar = PieceToChar(BlackRook);\r
4335       break;\r
4336     case PB_Bishop:\r
4337       promoChar = PieceToChar(BlackBishop);\r
4338       break;\r
4339     case PB_Chancellor:\r
4340       promoChar = PieceToChar(BlackMarshall);\r
4341       break;\r
4342     case PB_Archbishop:\r
4343       promoChar = PieceToChar(BlackAngel);\r
4344       break;\r
4345     case PB_Knight:\r
4346       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
4347       break;\r
4348     default:\r
4349       return FALSE;\r
4350     }\r
4351     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4352     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
4353        only show the popup when we are already sure the move is valid or\r
4354        legal. We pass a faulty move type, but the kludge is that FinishMove\r
4355        will figure out it is a promotion from the promoChar. */\r
4356     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4357     fromX = fromY = -1;\r
4358     if (!appData.highlightLastMove) {\r
4359       ClearHighlights();\r
4360       DrawPosition(FALSE, NULL);\r
4361     }\r
4362     return TRUE;\r
4363   }\r
4364   return FALSE;\r
4365 }\r
4366 \r
4367 /* Pop up promotion dialog */\r
4368 VOID\r
4369 PromotionPopup(HWND hwnd)\r
4370 {\r
4371   FARPROC lpProc;\r
4372 \r
4373   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4374   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4375     hwnd, (DLGPROC)lpProc);\r
4376   FreeProcInstance(lpProc);\r
4377 }\r
4378 \r
4379 void\r
4380 PromotionPopUp()\r
4381 {\r
4382   DrawPosition(TRUE, NULL);\r
4383   PromotionPopup(hwndMain);\r
4384 }\r
4385 \r
4386 /* Toggle ShowThinking */\r
4387 VOID\r
4388 ToggleShowThinking()\r
4389 {\r
4390   appData.showThinking = !appData.showThinking;\r
4391   ShowThinkingEvent();\r
4392 }\r
4393 \r
4394 VOID\r
4395 LoadGameDialog(HWND hwnd, char* title)\r
4396 {\r
4397   UINT number = 0;\r
4398   FILE *f;\r
4399   char fileTitle[MSG_SIZ];\r
4400   f = OpenFileDialog(hwnd, "rb", "",\r
4401                      appData.oldSaveStyle ? "gam" : "pgn",\r
4402                      GAME_FILT,\r
4403                      title, &number, fileTitle, NULL);\r
4404   if (f != NULL) {\r
4405     cmailMsgLoaded = FALSE;\r
4406     if (number == 0) {\r
4407       int error = GameListBuild(f);\r
4408       if (error) {\r
4409         DisplayError(_("Cannot build game list"), error);\r
4410       } else if (!ListEmpty(&gameList) &&\r
4411                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4412         GameListPopUp(f, fileTitle);\r
4413         return;\r
4414       }\r
4415       GameListDestroy();\r
4416       number = 1;\r
4417     }\r
4418     LoadGame(f, number, fileTitle, FALSE);\r
4419   }\r
4420 }\r
4421 \r
4422 int get_term_width()\r
4423 {\r
4424     HDC hdc;\r
4425     TEXTMETRIC tm;\r
4426     RECT rc;\r
4427     HFONT hfont, hold_font;\r
4428     LOGFONT lf;\r
4429     HWND hText;\r
4430 \r
4431     if (hwndConsole)\r
4432         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4433     else\r
4434         return 79;\r
4435 \r
4436     // get the text metrics\r
4437     hdc = GetDC(hText);\r
4438     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4439     if (consoleCF.dwEffects & CFE_BOLD)\r
4440         lf.lfWeight = FW_BOLD;\r
4441     if (consoleCF.dwEffects & CFE_ITALIC)\r
4442         lf.lfItalic = TRUE;\r
4443     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4444         lf.lfStrikeOut = TRUE;\r
4445     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4446         lf.lfUnderline = TRUE;\r
4447     hfont = CreateFontIndirect(&lf);\r
4448     hold_font = SelectObject(hdc, hfont);\r
4449     GetTextMetrics(hdc, &tm);\r
4450     SelectObject(hdc, hold_font);\r
4451     DeleteObject(hfont);\r
4452     ReleaseDC(hText, hdc);\r
4453 \r
4454     // get the rectangle\r
4455     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4456 \r
4457     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4458 }\r
4459 \r
4460 void UpdateICSWidth(HWND hText)\r
4461 {\r
4462     LONG old_width, new_width;\r
4463 \r
4464     new_width = get_term_width(hText, FALSE);\r
4465     old_width = GetWindowLong(hText, GWL_USERDATA);\r
4466     if (new_width != old_width)\r
4467     {\r
4468         ics_update_width(new_width);\r
4469         SetWindowLong(hText, GWL_USERDATA, new_width);\r
4470     }\r
4471 }\r
4472 \r
4473 VOID\r
4474 ChangedConsoleFont()\r
4475 {\r
4476   CHARFORMAT cfmt;\r
4477   CHARRANGE tmpsel, sel;\r
4478   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4479   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4480   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4481   PARAFORMAT paraf;\r
4482 \r
4483   cfmt.cbSize = sizeof(CHARFORMAT);\r
4484   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4485     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4486                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4487   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4488    * size.  This was undocumented in the version of MSVC++ that I had\r
4489    * when I wrote the code, but is apparently documented now.\r
4490    */\r
4491   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4492   cfmt.bCharSet = f->lf.lfCharSet;\r
4493   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4494   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4495   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4496   /* Why are the following seemingly needed too? */\r
4497   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4498   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4499   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4500   tmpsel.cpMin = 0;\r
4501   tmpsel.cpMax = -1; /*999999?*/\r
4502   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4503   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4504   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4505    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4506    */\r
4507   paraf.cbSize = sizeof(paraf);\r
4508   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4509   paraf.dxStartIndent = 0;\r
4510   paraf.dxOffset = WRAP_INDENT;\r
4511   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4512   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4513   UpdateICSWidth(hText);\r
4514 }\r
4515 \r
4516 /*---------------------------------------------------------------------------*\\r
4517  *\r
4518  * Window Proc for main window\r
4519  *\r
4520 \*---------------------------------------------------------------------------*/\r
4521 \r
4522 /* Process messages for main window, etc. */\r
4523 LRESULT CALLBACK\r
4524 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4525 {\r
4526   FARPROC lpProc;\r
4527   int wmId, wmEvent;\r
4528   char *defName;\r
4529   FILE *f;\r
4530   UINT number;\r
4531   char fileTitle[MSG_SIZ];\r
4532   char buf[MSG_SIZ];\r
4533   static SnapData sd;\r
4534 \r
4535   switch (message) {\r
4536 \r
4537   case WM_PAINT: /* message: repaint portion of window */\r
4538     PaintProc(hwnd);\r
4539     break;\r
4540 \r
4541   case WM_ERASEBKGND:\r
4542     if (IsIconic(hwnd)) {\r
4543       /* Cheat; change the message */\r
4544       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4545     } else {\r
4546       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4547     }\r
4548     break;\r
4549 \r
4550   case WM_LBUTTONDOWN:\r
4551   case WM_MBUTTONDOWN:\r
4552   case WM_RBUTTONDOWN:\r
4553   case WM_LBUTTONUP:\r
4554   case WM_MBUTTONUP:\r
4555   case WM_RBUTTONUP:\r
4556   case WM_MOUSEMOVE:\r
4557   case WM_MOUSEWHEEL:\r
4558     MouseEvent(hwnd, message, wParam, lParam);\r
4559     break;\r
4560 \r
4561   JAWS_KB_NAVIGATION\r
4562 \r
4563   case WM_CHAR:\r
4564     \r
4565     JAWS_ALT_INTERCEPT\r
4566 \r
4567     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4568         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4569         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4570         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4571         SetFocus(h);\r
4572         SendMessage(h, message, wParam, lParam);\r
4573     } else if(lParam != KF_REPEAT) {\r
4574         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4575                 PopUpMoveDialog((char)wParam);\r
4576         } else if((char)wParam == 003) CopyGameToClipboard();\r
4577          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4578     }\r
4579 \r
4580     break;\r
4581 \r
4582   case WM_PALETTECHANGED:\r
4583     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4584       int nnew;\r
4585       HDC hdc = GetDC(hwndMain);\r
4586       SelectPalette(hdc, hPal, TRUE);\r
4587       nnew = RealizePalette(hdc);\r
4588       if (nnew > 0) {\r
4589         paletteChanged = TRUE;\r
4590         InvalidateRect(hwnd, &boardRect, FALSE);\r
4591       }\r
4592       ReleaseDC(hwnd, hdc);\r
4593     }\r
4594     break;\r
4595 \r
4596   case WM_QUERYNEWPALETTE:\r
4597     if (!appData.monoMode /*&& paletteChanged*/) {\r
4598       int nnew;\r
4599       HDC hdc = GetDC(hwndMain);\r
4600       paletteChanged = FALSE;\r
4601       SelectPalette(hdc, hPal, FALSE);\r
4602       nnew = RealizePalette(hdc);\r
4603       if (nnew > 0) {\r
4604         InvalidateRect(hwnd, &boardRect, FALSE);\r
4605       }\r
4606       ReleaseDC(hwnd, hdc);\r
4607       return TRUE;\r
4608     }\r
4609     return FALSE;\r
4610 \r
4611   case WM_COMMAND: /* message: command from application menu */\r
4612     wmId    = LOWORD(wParam);\r
4613     wmEvent = HIWORD(wParam);\r
4614 \r
4615     switch (wmId) {\r
4616     case IDM_NewGame:\r
4617       ResetGameEvent();\r
4618       SAY("new game enter a move to play against the computer with white");\r
4619       break;\r
4620 \r
4621     case IDM_NewGameFRC:\r
4622       if( NewGameFRC() == 0 ) {\r
4623         ResetGameEvent();\r
4624       }\r
4625       break;\r
4626 \r
4627     case IDM_NewVariant:\r
4628       NewVariantPopup(hwnd);\r
4629       break;\r
4630 \r
4631     case IDM_LoadGame:\r
4632       LoadGameDialog(hwnd, _("Load Game from File"));\r
4633       break;\r
4634 \r
4635     case IDM_LoadNextGame:\r
4636       ReloadGame(1);\r
4637       break;\r
4638 \r
4639     case IDM_LoadPrevGame:\r
4640       ReloadGame(-1);\r
4641       break;\r
4642 \r
4643     case IDM_ReloadGame:\r
4644       ReloadGame(0);\r
4645       break;\r
4646 \r
4647     case IDM_LoadPosition:\r
4648       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4649         Reset(FALSE, TRUE);\r
4650       }\r
4651       number = 1;\r
4652       f = OpenFileDialog(hwnd, "rb", "",\r
4653                          appData.oldSaveStyle ? "pos" : "fen",\r
4654                          POSITION_FILT,\r
4655                          _("Load Position from File"), &number, fileTitle, NULL);\r
4656       if (f != NULL) {\r
4657         LoadPosition(f, number, fileTitle);\r
4658       }\r
4659       break;\r
4660 \r
4661     case IDM_LoadNextPosition:\r
4662       ReloadPosition(1);\r
4663       break;\r
4664 \r
4665     case IDM_LoadPrevPosition:\r
4666       ReloadPosition(-1);\r
4667       break;\r
4668 \r
4669     case IDM_ReloadPosition:\r
4670       ReloadPosition(0);\r
4671       break;\r
4672 \r
4673     case IDM_SaveGame:\r
4674       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4675       f = OpenFileDialog(hwnd, "a", defName,\r
4676                          appData.oldSaveStyle ? "gam" : "pgn",\r
4677                          GAME_FILT,\r
4678                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4679       if (f != NULL) {\r
4680         SaveGame(f, 0, "");\r
4681       }\r
4682       break;\r
4683 \r
4684     case IDM_SavePosition:\r
4685       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4686       f = OpenFileDialog(hwnd, "a", defName,\r
4687                          appData.oldSaveStyle ? "pos" : "fen",\r
4688                          POSITION_FILT,\r
4689                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4690       if (f != NULL) {\r
4691         SavePosition(f, 0, "");\r
4692       }\r
4693       break;\r
4694 \r
4695     case IDM_SaveDiagram:\r
4696       defName = "diagram";\r
4697       f = OpenFileDialog(hwnd, "wb", defName,\r
4698                          "bmp",\r
4699                          DIAGRAM_FILT,\r
4700                          "Save Diagram to File", NULL, fileTitle, NULL);\r
4701       if (f != NULL) {\r
4702         SaveDiagram(f);\r
4703       }\r
4704       break;\r
4705 \r
4706     case IDM_CopyGame:\r
4707       CopyGameToClipboard();\r
4708       break;\r
4709 \r
4710     case IDM_PasteGame:\r
4711       PasteGameFromClipboard();\r
4712       break;\r
4713 \r
4714     case IDM_CopyGameListToClipboard:\r
4715       CopyGameListToClipboard();\r
4716       break;\r
4717 \r
4718     /* [AS] Autodetect FEN or PGN data */\r
4719     case IDM_PasteAny:\r
4720       PasteGameOrFENFromClipboard();\r
4721       break;\r
4722 \r
4723     /* [AS] Move history */\r
4724     case IDM_ShowMoveHistory:\r
4725         if( MoveHistoryIsUp() ) {\r
4726             MoveHistoryPopDown();\r
4727         }\r
4728         else {\r
4729             MoveHistoryPopUp();\r
4730         }\r
4731         break;\r
4732 \r
4733     /* [AS] Eval graph */\r
4734     case IDM_ShowEvalGraph:\r
4735         if( EvalGraphIsUp() ) {\r
4736             EvalGraphPopDown();\r
4737         }\r
4738         else {\r
4739             EvalGraphPopUp();\r
4740             SetFocus(hwndMain);\r
4741         }\r
4742         break;\r
4743 \r
4744     /* [AS] Engine output */\r
4745     case IDM_ShowEngineOutput:\r
4746         if( EngineOutputIsUp() ) {\r
4747             EngineOutputPopDown();\r
4748         }\r
4749         else {\r
4750             EngineOutputPopUp();\r
4751         }\r
4752         break;\r
4753 \r
4754     /* [AS] User adjudication */\r
4755     case IDM_UserAdjudication_White:\r
4756         UserAdjudicationEvent( +1 );\r
4757         break;\r
4758 \r
4759     case IDM_UserAdjudication_Black:\r
4760         UserAdjudicationEvent( -1 );\r
4761         break;\r
4762 \r
4763     case IDM_UserAdjudication_Draw:\r
4764         UserAdjudicationEvent( 0 );\r
4765         break;\r
4766 \r
4767     /* [AS] Game list options dialog */\r
4768     case IDM_GameListOptions:\r
4769       GameListOptions();\r
4770       break;\r
4771 \r
4772     case IDM_NewChat:\r
4773       ChatPopUp(NULL);\r
4774       break;\r
4775 \r
4776     case IDM_CopyPosition:\r
4777       CopyFENToClipboard();\r
4778       break;\r
4779 \r
4780     case IDM_PastePosition:\r
4781       PasteFENFromClipboard();\r
4782       break;\r
4783 \r
4784     case IDM_MailMove:\r
4785       MailMoveEvent();\r
4786       break;\r
4787 \r
4788     case IDM_ReloadCMailMsg:\r
4789       Reset(TRUE, TRUE);\r
4790       ReloadCmailMsgEvent(FALSE);\r
4791       break;\r
4792 \r
4793     case IDM_Minimize:\r
4794       ShowWindow(hwnd, SW_MINIMIZE);\r
4795       break;\r
4796 \r
4797     case IDM_Exit:\r
4798       ExitEvent(0);\r
4799       break;\r
4800 \r
4801     case IDM_MachineWhite:\r
4802       MachineWhiteEvent();\r
4803       /*\r
4804        * refresh the tags dialog only if it's visible\r
4805        */\r
4806       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4807           char *tags;\r
4808           tags = PGNTags(&gameInfo);\r
4809           TagsPopUp(tags, CmailMsg());\r
4810           free(tags);\r
4811       }\r
4812       SAY("computer starts playing white");\r
4813       break;\r
4814 \r
4815     case IDM_MachineBlack:\r
4816       MachineBlackEvent();\r
4817       /*\r
4818        * refresh the tags dialog only if it's visible\r
4819        */\r
4820       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4821           char *tags;\r
4822           tags = PGNTags(&gameInfo);\r
4823           TagsPopUp(tags, CmailMsg());\r
4824           free(tags);\r
4825       }\r
4826       SAY("computer starts playing black");\r
4827       break;\r
4828 \r
4829     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4830       if(gameMode != BeginningOfGame) { // allow menu item to remain enabled for better mode highligting\r
4831         DisplayError(_("You can only start a match from the initial position."), 0); break;\r
4832       }\r
4833       matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)\r
4834       appData.matchGames = appData.defaultMatchGames;\r
4835       matchGame = 1;\r
4836 \r
4837     case IDM_TwoMachines:\r
4838       TwoMachinesEvent();\r
4839       /*\r
4840        * refresh the tags dialog only if it's visible\r
4841        */\r
4842       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4843           char *tags;\r
4844           tags = PGNTags(&gameInfo);\r
4845           TagsPopUp(tags, CmailMsg());\r
4846           free(tags);\r
4847       }\r
4848       SAY("computer starts playing both sides");\r
4849       break;\r
4850 \r
4851     case IDM_AnalysisMode:\r
4852       if (!first.analysisSupport) {\r
4853         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4854         DisplayError(buf, 0);\r
4855       } else {\r
4856         SAY("analyzing current position");\r
4857         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4858         if (appData.icsActive) {\r
4859                if (gameMode != IcsObserving) {\r
4860                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4861                        DisplayError(buf, 0);\r
4862                        /* secure check */\r
4863                        if (appData.icsEngineAnalyze) {\r
4864                                if (appData.debugMode) \r
4865                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4866                                ExitAnalyzeMode();\r
4867                                ModeHighlight();\r
4868                                break;\r
4869                        }\r
4870                        break;\r
4871                } else {\r
4872                        /* if enable, user want disable icsEngineAnalyze */\r
4873                        if (appData.icsEngineAnalyze) {\r
4874                                ExitAnalyzeMode();\r
4875                                ModeHighlight();\r
4876                                break;\r
4877                        }\r
4878                        appData.icsEngineAnalyze = TRUE;\r
4879                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4880                }\r
4881         } \r
4882         if (!appData.showThinking) ToggleShowThinking();\r
4883         AnalyzeModeEvent();\r
4884       }\r
4885       break;\r
4886 \r
4887     case IDM_AnalyzeFile:\r
4888       if (!first.analysisSupport) {\r
4889         char buf[MSG_SIZ];\r
4890           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4891         DisplayError(buf, 0);\r
4892       } else {\r
4893         if (!appData.showThinking) ToggleShowThinking();\r
4894         AnalyzeFileEvent();\r
4895         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4896         AnalysisPeriodicEvent(1);\r
4897       }\r
4898       break;\r
4899 \r
4900     case IDM_IcsClient:\r
4901       IcsClientEvent();\r
4902       break;\r
4903 \r
4904     case IDM_EditGame:\r
4905       EditGameEvent();\r
4906       SAY("edit game");\r
4907       break;\r
4908 \r
4909     case IDM_EditPosition:\r
4910       EditPositionEvent();\r
4911       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4912       break;\r
4913 \r
4914     case IDM_Training:\r
4915       TrainingEvent();\r
4916       break;\r
4917 \r
4918     case IDM_ShowGameList:\r
4919       ShowGameListProc();\r
4920       break;\r
4921 \r
4922     case IDM_EditTags:\r
4923     case IDM_Tags:\r
4924       EditTagsProc();\r
4925       break;\r
4926 \r
4927     case IDM_EditComment:\r
4928     case IDM_Comment:\r
4929       if (commentUp && editComment) {\r
4930         CommentPopDown();\r
4931       } else {\r
4932         EditCommentEvent();\r
4933       }\r
4934       break;\r
4935 \r
4936     case IDM_Pause:\r
4937       PauseEvent();\r
4938       break;\r
4939 \r
4940     case IDM_Accept:\r
4941       AcceptEvent();\r
4942       break;\r
4943 \r
4944     case IDM_Decline:\r
4945       DeclineEvent();\r
4946       break;\r
4947 \r
4948     case IDM_Rematch:\r
4949       RematchEvent();\r
4950       break;\r
4951 \r
4952     case IDM_CallFlag:\r
4953       CallFlagEvent();\r
4954       break;\r
4955 \r
4956     case IDM_Draw:\r
4957       DrawEvent();\r
4958       break;\r
4959 \r
4960     case IDM_Adjourn:\r
4961       AdjournEvent();\r
4962       break;\r
4963 \r
4964     case IDM_Abort:\r
4965       AbortEvent();\r
4966       break;\r
4967 \r
4968     case IDM_Resign:\r
4969       ResignEvent();\r
4970       break;\r
4971 \r
4972     case IDM_StopObserving:\r
4973       StopObservingEvent();\r
4974       break;\r
4975 \r
4976     case IDM_StopExamining:\r
4977       StopExaminingEvent();\r
4978       break;\r
4979 \r
4980     case IDM_Upload:\r
4981       UploadGameEvent();\r
4982       break;\r
4983 \r
4984     case IDM_TypeInMove:\r
4985       PopUpMoveDialog('\000');\r
4986       break;\r
4987 \r
4988     case IDM_TypeInName:\r
4989       PopUpNameDialog('\000');\r
4990       break;\r
4991 \r
4992     case IDM_Backward:\r
4993       BackwardEvent();\r
4994       SetFocus(hwndMain);\r
4995       break;\r
4996 \r
4997     JAWS_MENU_ITEMS\r
4998 \r
4999     case IDM_Forward:\r
5000       ForwardEvent();\r
5001       SetFocus(hwndMain);\r
5002       break;\r
5003 \r
5004     case IDM_ToStart:\r
5005       ToStartEvent();\r
5006       SetFocus(hwndMain);\r
5007       break;\r
5008 \r
5009     case IDM_ToEnd:\r
5010       ToEndEvent();\r
5011       SetFocus(hwndMain);\r
5012       break;\r
5013 \r
5014     case IDM_Revert:\r
5015       RevertEvent(FALSE);\r
5016       break;\r
5017 \r
5018     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5019       RevertEvent(TRUE);\r
5020       break;\r
5021 \r
5022     case IDM_TruncateGame:\r
5023       TruncateGameEvent();\r
5024       break;\r
5025 \r
5026     case IDM_MoveNow:\r
5027       MoveNowEvent();\r
5028       break;\r
5029 \r
5030     case IDM_RetractMove:\r
5031       RetractMoveEvent();\r
5032       break;\r
5033 \r
5034     case IDM_FlipView:\r
5035       flipView = !flipView;\r
5036       DrawPosition(FALSE, NULL);\r
5037       break;\r
5038 \r
5039     case IDM_FlipClock:\r
5040       flipClock = !flipClock;\r
5041       DisplayBothClocks();\r
5042       DrawPosition(FALSE, NULL);\r
5043       break;\r
5044 \r
5045     case IDM_MuteSounds:\r
5046       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5047       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5048                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5049       break;\r
5050 \r
5051     case IDM_GeneralOptions:\r
5052       GeneralOptionsPopup(hwnd);\r
5053       DrawPosition(TRUE, NULL);\r
5054       break;\r
5055 \r
5056     case IDM_BoardOptions:\r
5057       BoardOptionsPopup(hwnd);\r
5058       break;\r
5059 \r
5060     case IDM_EnginePlayOptions:\r
5061       EnginePlayOptionsPopup(hwnd);\r
5062       break;\r
5063 \r
5064     case IDM_Engine1Options:\r
5065       EngineOptionsPopup(hwnd, &first);\r
5066       break;\r
5067 \r
5068     case IDM_Engine2Options:\r
5069       savedHwnd = hwnd;\r
5070       if(WaitForSecond(SettingsMenuIfReady)) break;\r
5071       EngineOptionsPopup(hwnd, &second);\r
5072       break;\r
5073 \r
5074     case IDM_OptionsUCI:\r
5075       UciOptionsPopup(hwnd);\r
5076       break;\r
5077 \r
5078     case IDM_IcsOptions:\r
5079       IcsOptionsPopup(hwnd);\r
5080       break;\r
5081 \r
5082     case IDM_Fonts:\r
5083       FontsOptionsPopup(hwnd);\r
5084       break;\r
5085 \r
5086     case IDM_Sounds:\r
5087       SoundOptionsPopup(hwnd);\r
5088       break;\r
5089 \r
5090     case IDM_CommPort:\r
5091       CommPortOptionsPopup(hwnd);\r
5092       break;\r
5093 \r
5094     case IDM_LoadOptions:\r
5095       LoadOptionsPopup(hwnd);\r
5096       break;\r
5097 \r
5098     case IDM_SaveOptions:\r
5099       SaveOptionsPopup(hwnd);\r
5100       break;\r
5101 \r
5102     case IDM_TimeControl:\r
5103       TimeControlOptionsPopup(hwnd);\r
5104       break;\r
5105 \r
5106     case IDM_SaveSettings:\r
5107       SaveSettings(settingsFileName);\r
5108       break;\r
5109 \r
5110     case IDM_SaveSettingsOnExit:\r
5111       saveSettingsOnExit = !saveSettingsOnExit;\r
5112       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5113                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5114                                          MF_CHECKED : MF_UNCHECKED));\r
5115       break;\r
5116 \r
5117     case IDM_Hint:\r
5118       HintEvent();\r
5119       break;\r
5120 \r
5121     case IDM_Book:\r
5122       BookEvent();\r
5123       break;\r
5124 \r
5125     case IDM_AboutGame:\r
5126       AboutGameEvent();\r
5127       break;\r
5128 \r
5129     case IDM_Debug:\r
5130       appData.debugMode = !appData.debugMode;\r
5131       if (appData.debugMode) {\r
5132         char dir[MSG_SIZ];\r
5133         GetCurrentDirectory(MSG_SIZ, dir);\r
5134         SetCurrentDirectory(installDir);\r
5135         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5136         SetCurrentDirectory(dir);\r
5137         setbuf(debugFP, NULL);\r
5138       } else {\r
5139         fclose(debugFP);\r
5140         debugFP = NULL;\r
5141       }\r
5142       break;\r
5143 \r
5144     case IDM_HELPCONTENTS:\r
5145       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5146           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5147           MessageBox (GetFocus(),\r
5148                     _("Unable to activate help"),\r
5149                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5150       }\r
5151       break;\r
5152 \r
5153     case IDM_HELPSEARCH:\r
5154         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5155             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5156         MessageBox (GetFocus(),\r
5157                     _("Unable to activate help"),\r
5158                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5159       }\r
5160       break;\r
5161 \r
5162     case IDM_HELPHELP:\r
5163       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5164         MessageBox (GetFocus(),\r
5165                     _("Unable to activate help"),\r
5166                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5167       }\r
5168       break;\r
5169 \r
5170     case IDM_ABOUT:\r
5171       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5172       DialogBox(hInst, \r
5173         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5174         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5175       FreeProcInstance(lpProc);\r
5176       break;\r
5177 \r
5178     case IDM_DirectCommand1:\r
5179       AskQuestionEvent(_("Direct Command"),\r
5180                        _("Send to chess program:"), "", "1");\r
5181       break;\r
5182     case IDM_DirectCommand2:\r
5183       AskQuestionEvent(_("Direct Command"),\r
5184                        _("Send to second chess program:"), "", "2");\r
5185       break;\r
5186 \r
5187     case EP_WhitePawn:\r
5188       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5189       fromX = fromY = -1;\r
5190       break;\r
5191 \r
5192     case EP_WhiteKnight:\r
5193       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5194       fromX = fromY = -1;\r
5195       break;\r
5196 \r
5197     case EP_WhiteBishop:\r
5198       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5199       fromX = fromY = -1;\r
5200       break;\r
5201 \r
5202     case EP_WhiteRook:\r
5203       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5204       fromX = fromY = -1;\r
5205       break;\r
5206 \r
5207     case EP_WhiteQueen:\r
5208       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5209       fromX = fromY = -1;\r
5210       break;\r
5211 \r
5212     case EP_WhiteFerz:\r
5213       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5214       fromX = fromY = -1;\r
5215       break;\r
5216 \r
5217     case EP_WhiteWazir:\r
5218       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5219       fromX = fromY = -1;\r
5220       break;\r
5221 \r
5222     case EP_WhiteAlfil:\r
5223       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5224       fromX = fromY = -1;\r
5225       break;\r
5226 \r
5227     case EP_WhiteCannon:\r
5228       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5229       fromX = fromY = -1;\r
5230       break;\r
5231 \r
5232     case EP_WhiteCardinal:\r
5233       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5234       fromX = fromY = -1;\r
5235       break;\r
5236 \r
5237     case EP_WhiteMarshall:\r
5238       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5239       fromX = fromY = -1;\r
5240       break;\r
5241 \r
5242     case EP_WhiteKing:\r
5243       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5244       fromX = fromY = -1;\r
5245       break;\r
5246 \r
5247     case EP_BlackPawn:\r
5248       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5249       fromX = fromY = -1;\r
5250       break;\r
5251 \r
5252     case EP_BlackKnight:\r
5253       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5254       fromX = fromY = -1;\r
5255       break;\r
5256 \r
5257     case EP_BlackBishop:\r
5258       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5259       fromX = fromY = -1;\r
5260       break;\r
5261 \r
5262     case EP_BlackRook:\r
5263       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5264       fromX = fromY = -1;\r
5265       break;\r
5266 \r
5267     case EP_BlackQueen:\r
5268       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5269       fromX = fromY = -1;\r
5270       break;\r
5271 \r
5272     case EP_BlackFerz:\r
5273       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5274       fromX = fromY = -1;\r
5275       break;\r
5276 \r
5277     case EP_BlackWazir:\r
5278       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5279       fromX = fromY = -1;\r
5280       break;\r
5281 \r
5282     case EP_BlackAlfil:\r
5283       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5284       fromX = fromY = -1;\r
5285       break;\r
5286 \r
5287     case EP_BlackCannon:\r
5288       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5289       fromX = fromY = -1;\r
5290       break;\r
5291 \r
5292     case EP_BlackCardinal:\r
5293       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5294       fromX = fromY = -1;\r
5295       break;\r
5296 \r
5297     case EP_BlackMarshall:\r
5298       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5299       fromX = fromY = -1;\r
5300       break;\r
5301 \r
5302     case EP_BlackKing:\r
5303       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5304       fromX = fromY = -1;\r
5305       break;\r
5306 \r
5307     case EP_EmptySquare:\r
5308       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5309       fromX = fromY = -1;\r
5310       break;\r
5311 \r
5312     case EP_ClearBoard:\r
5313       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5314       fromX = fromY = -1;\r
5315       break;\r
5316 \r
5317     case EP_White:\r
5318       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5319       fromX = fromY = -1;\r
5320       break;\r
5321 \r
5322     case EP_Black:\r
5323       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5324       fromX = fromY = -1;\r
5325       break;\r
5326 \r
5327     case EP_Promote:\r
5328       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5329       fromX = fromY = -1;\r
5330       break;\r
5331 \r
5332     case EP_Demote:\r
5333       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5334       fromX = fromY = -1;\r
5335       break;\r
5336 \r
5337     case DP_Pawn:\r
5338       DropMenuEvent(WhitePawn, fromX, fromY);\r
5339       fromX = fromY = -1;\r
5340       break;\r
5341 \r
5342     case DP_Knight:\r
5343       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5344       fromX = fromY = -1;\r
5345       break;\r
5346 \r
5347     case DP_Bishop:\r
5348       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5349       fromX = fromY = -1;\r
5350       break;\r
5351 \r
5352     case DP_Rook:\r
5353       DropMenuEvent(WhiteRook, fromX, fromY);\r
5354       fromX = fromY = -1;\r
5355       break;\r
5356 \r
5357     case DP_Queen:\r
5358       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5359       fromX = fromY = -1;\r
5360       break;\r
5361 \r
5362     case IDM_English:\r
5363       barbaric = 0;\r
5364       TranslateMenus(0);\r
5365       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5366       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5367       lastChecked = wmId;\r
5368       break;\r
5369 \r
5370     default:\r
5371       if(wmId > IDM_English && wmId < IDM_English+5) {\r
5372           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5373           TranslateMenus(0);\r
5374           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5375           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5376           lastChecked = wmId;\r
5377           break;\r
5378       }\r
5379       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5380     }\r
5381     break;\r
5382 \r
5383   case WM_TIMER:\r
5384     switch (wParam) {\r
5385     case CLOCK_TIMER_ID:\r
5386       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5387       clockTimerEvent = 0;\r
5388       DecrementClocks(); /* call into back end */\r
5389       break;\r
5390     case LOAD_GAME_TIMER_ID:\r
5391       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5392       loadGameTimerEvent = 0;\r
5393       AutoPlayGameLoop(); /* call into back end */\r
5394       break;\r
5395     case ANALYSIS_TIMER_ID:\r
5396       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5397                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5398         AnalysisPeriodicEvent(0);\r
5399       } else {\r
5400         KillTimer(hwnd, analysisTimerEvent);\r
5401         analysisTimerEvent = 0;\r
5402       }\r
5403       break;\r
5404     case DELAYED_TIMER_ID:\r
5405       KillTimer(hwnd, delayedTimerEvent);\r
5406       delayedTimerEvent = 0;\r
5407       delayedTimerCallback();\r
5408       break;\r
5409     }\r
5410     break;\r
5411 \r
5412   case WM_USER_Input:\r
5413     InputEvent(hwnd, message, wParam, lParam);\r
5414     break;\r
5415 \r
5416   /* [AS] Also move "attached" child windows */\r
5417   case WM_WINDOWPOSCHANGING:\r
5418 \r
5419     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5420         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5421 \r
5422         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5423             /* Window is moving */\r
5424             RECT rcMain;\r
5425 \r
5426 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5427             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5428             rcMain.right  = wpMain.x + wpMain.width;\r
5429             rcMain.top    = wpMain.y;\r
5430             rcMain.bottom = wpMain.y + wpMain.height;\r
5431             \r
5432             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5433             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5434             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5435             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5436             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5437             wpMain.x = lpwp->x;\r
5438             wpMain.y = lpwp->y;\r
5439         }\r
5440     }\r
5441     break;\r
5442 \r
5443   /* [AS] Snapping */\r
5444   case WM_ENTERSIZEMOVE:\r
5445     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5446     if (hwnd == hwndMain) {\r
5447       doingSizing = TRUE;\r
5448       lastSizing = 0;\r
5449     }\r
5450     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5451     break;\r
5452 \r
5453   case WM_SIZING:\r
5454     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5455     if (hwnd == hwndMain) {\r
5456       lastSizing = wParam;\r
5457     }\r
5458     break;\r
5459 \r
5460   case WM_MOVING:\r
5461     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5462       return OnMoving( &sd, hwnd, wParam, lParam );\r
5463 \r
5464   case WM_EXITSIZEMOVE:\r
5465     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5466     if (hwnd == hwndMain) {\r
5467       RECT client;\r
5468       doingSizing = FALSE;\r
5469       InvalidateRect(hwnd, &boardRect, FALSE);\r
5470       GetClientRect(hwnd, &client);\r
5471       ResizeBoard(client.right, client.bottom, lastSizing);\r
5472       lastSizing = 0;\r
5473       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5474     }\r
5475     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5476     break;\r
5477 \r
5478   case WM_DESTROY: /* message: window being destroyed */\r
5479     PostQuitMessage(0);\r
5480     break;\r
5481 \r
5482   case WM_CLOSE:\r
5483     if (hwnd == hwndMain) {\r
5484       ExitEvent(0);\r
5485     }\r
5486     break;\r
5487 \r
5488   default:      /* Passes it on if unprocessed */\r
5489     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5490   }\r
5491   return 0;\r
5492 }\r
5493 \r
5494 /*---------------------------------------------------------------------------*\\r
5495  *\r
5496  * Misc utility routines\r
5497  *\r
5498 \*---------------------------------------------------------------------------*/\r
5499 \r
5500 /*\r
5501  * Decent random number generator, at least not as bad as Windows\r
5502  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5503  */\r
5504 unsigned int randstate;\r
5505 \r
5506 int\r
5507 myrandom(void)\r
5508 {\r
5509   randstate = randstate * 1664525 + 1013904223;\r
5510   return (int) randstate & 0x7fffffff;\r
5511 }\r
5512 \r
5513 void\r
5514 mysrandom(unsigned int seed)\r
5515 {\r
5516   randstate = seed;\r
5517 }\r
5518 \r
5519 \r
5520 /* \r
5521  * returns TRUE if user selects a different color, FALSE otherwise \r
5522  */\r
5523 \r
5524 BOOL\r
5525 ChangeColor(HWND hwnd, COLORREF *which)\r
5526 {\r
5527   static BOOL firstTime = TRUE;\r
5528   static DWORD customColors[16];\r
5529   CHOOSECOLOR cc;\r
5530   COLORREF newcolor;\r
5531   int i;\r
5532   ColorClass ccl;\r
5533 \r
5534   if (firstTime) {\r
5535     /* Make initial colors in use available as custom colors */\r
5536     /* Should we put the compiled-in defaults here instead? */\r
5537     i = 0;\r
5538     customColors[i++] = lightSquareColor & 0xffffff;\r
5539     customColors[i++] = darkSquareColor & 0xffffff;\r
5540     customColors[i++] = whitePieceColor & 0xffffff;\r
5541     customColors[i++] = blackPieceColor & 0xffffff;\r
5542     customColors[i++] = highlightSquareColor & 0xffffff;\r
5543     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5544 \r
5545     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5546       customColors[i++] = textAttribs[ccl].color;\r
5547     }\r
5548     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5549     firstTime = FALSE;\r
5550   }\r
5551 \r
5552   cc.lStructSize = sizeof(cc);\r
5553   cc.hwndOwner = hwnd;\r
5554   cc.hInstance = NULL;\r
5555   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5556   cc.lpCustColors = (LPDWORD) customColors;\r
5557   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5558 \r
5559   if (!ChooseColor(&cc)) return FALSE;\r
5560 \r
5561   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5562   if (newcolor == *which) return FALSE;\r
5563   *which = newcolor;\r
5564   return TRUE;\r
5565 \r
5566   /*\r
5567   InitDrawingColors();\r
5568   InvalidateRect(hwnd, &boardRect, FALSE);\r
5569   */\r
5570 }\r
5571 \r
5572 BOOLEAN\r
5573 MyLoadSound(MySound *ms)\r
5574 {\r
5575   BOOL ok = FALSE;\r
5576   struct stat st;\r
5577   FILE *f;\r
5578 \r
5579   if (ms->data) free(ms->data);\r
5580   ms->data = NULL;\r
5581 \r
5582   switch (ms->name[0]) {\r
5583   case NULLCHAR:\r
5584     /* Silence */\r
5585     ok = TRUE;\r
5586     break;\r
5587   case '$':\r
5588     /* System sound from Control Panel.  Don't preload here. */\r
5589     ok = TRUE;\r
5590     break;\r
5591   case '!':\r
5592     if (ms->name[1] == NULLCHAR) {\r
5593       /* "!" alone = silence */\r
5594       ok = TRUE;\r
5595     } else {\r
5596       /* Builtin wave resource.  Error if not found. */\r
5597       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5598       if (h == NULL) break;\r
5599       ms->data = (void *)LoadResource(hInst, h);\r
5600       if (h == NULL) break;\r
5601       ok = TRUE;\r
5602     }\r
5603     break;\r
5604   default:\r
5605     /* .wav file.  Error if not found. */\r
5606     f = fopen(ms->name, "rb");\r
5607     if (f == NULL) break;\r
5608     if (fstat(fileno(f), &st) < 0) break;\r
5609     ms->data = malloc(st.st_size);\r
5610     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5611     fclose(f);\r
5612     ok = TRUE;\r
5613     break;\r
5614   }\r
5615   if (!ok) {\r
5616     char buf[MSG_SIZ];\r
5617       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5618     DisplayError(buf, GetLastError());\r
5619   }\r
5620   return ok;\r
5621 }\r
5622 \r
5623 BOOLEAN\r
5624 MyPlaySound(MySound *ms)\r
5625 {\r
5626   BOOLEAN ok = FALSE;\r
5627 \r
5628   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5629   switch (ms->name[0]) {\r
5630   case NULLCHAR:\r
5631         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5632     /* Silence */\r
5633     ok = TRUE;\r
5634     break;\r
5635   case '$':\r
5636     /* System sound from Control Panel (deprecated feature).\r
5637        "$" alone or an unset sound name gets default beep (still in use). */\r
5638     if (ms->name[1]) {\r
5639       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5640     }\r
5641     if (!ok) ok = MessageBeep(MB_OK);\r
5642     break; \r
5643   case '!':\r
5644     /* Builtin wave resource, or "!" alone for silence */\r
5645     if (ms->name[1]) {\r
5646       if (ms->data == NULL) return FALSE;\r
5647       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5648     } else {\r
5649       ok = TRUE;\r
5650     }\r
5651     break;\r
5652   default:\r
5653     /* .wav file.  Error if not found. */\r
5654     if (ms->data == NULL) return FALSE;\r
5655     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5656     break;\r
5657   }\r
5658   /* Don't print an error: this can happen innocently if the sound driver\r
5659      is busy; for instance, if another instance of WinBoard is playing\r
5660      a sound at about the same time. */\r
5661   return ok;\r
5662 }\r
5663 \r
5664 \r
5665 LRESULT CALLBACK\r
5666 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5667 {\r
5668   BOOL ok;\r
5669   OPENFILENAME *ofn;\r
5670   static UINT *number; /* gross that this is static */\r
5671 \r
5672   switch (message) {\r
5673   case WM_INITDIALOG: /* message: initialize dialog box */\r
5674     /* Center the dialog over the application window */\r
5675     ofn = (OPENFILENAME *) lParam;\r
5676     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5677       number = (UINT *) ofn->lCustData;\r
5678       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5679     } else {\r
5680       number = NULL;\r
5681     }\r
5682     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5683     Translate(hDlg, 1536);\r
5684     return FALSE;  /* Allow for further processing */\r
5685 \r
5686   case WM_COMMAND:\r
5687     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5688       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5689     }\r
5690     return FALSE;  /* Allow for further processing */\r
5691   }\r
5692   return FALSE;\r
5693 }\r
5694 \r
5695 UINT APIENTRY\r
5696 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5697 {\r
5698   static UINT *number;\r
5699   OPENFILENAME *ofname;\r
5700   OFNOTIFY *ofnot;\r
5701   switch (uiMsg) {\r
5702   case WM_INITDIALOG:\r
5703     Translate(hdlg, DLG_IndexNumber);\r
5704     ofname = (OPENFILENAME *)lParam;\r
5705     number = (UINT *)(ofname->lCustData);\r
5706     break;\r
5707   case WM_NOTIFY:\r
5708     ofnot = (OFNOTIFY *)lParam;\r
5709     if (ofnot->hdr.code == CDN_FILEOK) {\r
5710       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5711     }\r
5712     break;\r
5713   }\r
5714   return 0;\r
5715 }\r
5716 \r
5717 \r
5718 FILE *\r
5719 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5720                char *nameFilt, char *dlgTitle, UINT *number,\r
5721                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5722 {\r
5723   OPENFILENAME openFileName;\r
5724   char buf1[MSG_SIZ];\r
5725   FILE *f;\r
5726 \r
5727   if (fileName == NULL) fileName = buf1;\r
5728   if (defName == NULL) {\r
5729     safeStrCpy(fileName, "*.", 3 );\r
5730     strcat(fileName, defExt);\r
5731   } else {\r
5732     safeStrCpy(fileName, defName, MSG_SIZ );\r
5733   }\r
5734     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5735   if (number) *number = 0;\r
5736 \r
5737   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5738   openFileName.hwndOwner         = hwnd;\r
5739   openFileName.hInstance         = (HANDLE) hInst;\r
5740   openFileName.lpstrFilter       = nameFilt;\r
5741   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5742   openFileName.nMaxCustFilter    = 0L;\r
5743   openFileName.nFilterIndex      = 1L;\r
5744   openFileName.lpstrFile         = fileName;\r
5745   openFileName.nMaxFile          = MSG_SIZ;\r
5746   openFileName.lpstrFileTitle    = fileTitle;\r
5747   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5748   openFileName.lpstrInitialDir   = NULL;\r
5749   openFileName.lpstrTitle        = dlgTitle;\r
5750   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5751     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5752     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5753     | (oldDialog ? 0 : OFN_EXPLORER);\r
5754   openFileName.nFileOffset       = 0;\r
5755   openFileName.nFileExtension    = 0;\r
5756   openFileName.lpstrDefExt       = defExt;\r
5757   openFileName.lCustData         = (LONG) number;\r
5758   openFileName.lpfnHook          = oldDialog ?\r
5759     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5760   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5761 \r
5762   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5763                         GetOpenFileName(&openFileName)) {\r
5764     /* open the file */\r
5765     f = fopen(openFileName.lpstrFile, write);\r
5766     if (f == NULL) {\r
5767       MessageBox(hwnd, _("File open failed"), NULL,\r
5768                  MB_OK|MB_ICONEXCLAMATION);\r
5769       return NULL;\r
5770     }\r
5771   } else {\r
5772     int err = CommDlgExtendedError();\r
5773     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5774     return FALSE;\r
5775   }\r
5776   return f;\r
5777 }\r
5778 \r
5779 \r
5780 \r
5781 VOID APIENTRY\r
5782 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5783 {\r
5784   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5785 \r
5786   /*\r
5787    * Get the first pop-up menu in the menu template. This is the\r
5788    * menu that TrackPopupMenu displays.\r
5789    */\r
5790   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5791 \r
5792   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5793 \r
5794   /*\r
5795    * TrackPopup uses screen coordinates, so convert the\r
5796    * coordinates of the mouse click to screen coordinates.\r
5797    */\r
5798   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5799 \r
5800   /* Draw and track the floating pop-up menu. */\r
5801   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5802                  pt.x, pt.y, 0, hwnd, NULL);\r
5803 \r
5804   /* Destroy the menu.*/\r
5805   DestroyMenu(hmenu);\r
5806 }\r
5807    \r
5808 typedef struct {\r
5809   HWND hDlg, hText;\r
5810   int sizeX, sizeY, newSizeX, newSizeY;\r
5811   HDWP hdwp;\r
5812 } ResizeEditPlusButtonsClosure;\r
5813 \r
5814 BOOL CALLBACK\r
5815 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5816 {\r
5817   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5818   RECT rect;\r
5819   POINT pt;\r
5820 \r
5821   if (hChild == cl->hText) return TRUE;\r
5822   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5823   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5824   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5825   ScreenToClient(cl->hDlg, &pt);\r
5826   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5827     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5828   return TRUE;\r
5829 }\r
5830 \r
5831 /* Resize a dialog that has a (rich) edit field filling most of\r
5832    the top, with a row of buttons below */\r
5833 VOID\r
5834 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5835 {\r
5836   RECT rectText;\r
5837   int newTextHeight, newTextWidth;\r
5838   ResizeEditPlusButtonsClosure cl;\r
5839   \r
5840   /*if (IsIconic(hDlg)) return;*/\r
5841   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5842   \r
5843   cl.hdwp = BeginDeferWindowPos(8);\r
5844 \r
5845   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5846   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5847   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5848   if (newTextHeight < 0) {\r
5849     newSizeY += -newTextHeight;\r
5850     newTextHeight = 0;\r
5851   }\r
5852   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5853     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5854 \r
5855   cl.hDlg = hDlg;\r
5856   cl.hText = hText;\r
5857   cl.sizeX = sizeX;\r
5858   cl.sizeY = sizeY;\r
5859   cl.newSizeX = newSizeX;\r
5860   cl.newSizeY = newSizeY;\r
5861   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5862 \r
5863   EndDeferWindowPos(cl.hdwp);\r
5864 }\r
5865 \r
5866 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5867 {\r
5868     RECT    rChild, rParent;\r
5869     int     wChild, hChild, wParent, hParent;\r
5870     int     wScreen, hScreen, xNew, yNew;\r
5871     HDC     hdc;\r
5872 \r
5873     /* Get the Height and Width of the child window */\r
5874     GetWindowRect (hwndChild, &rChild);\r
5875     wChild = rChild.right - rChild.left;\r
5876     hChild = rChild.bottom - rChild.top;\r
5877 \r
5878     /* Get the Height and Width of the parent window */\r
5879     GetWindowRect (hwndParent, &rParent);\r
5880     wParent = rParent.right - rParent.left;\r
5881     hParent = rParent.bottom - rParent.top;\r
5882 \r
5883     /* Get the display limits */\r
5884     hdc = GetDC (hwndChild);\r
5885     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5886     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5887     ReleaseDC(hwndChild, hdc);\r
5888 \r
5889     /* Calculate new X position, then adjust for screen */\r
5890     xNew = rParent.left + ((wParent - wChild) /2);\r
5891     if (xNew < 0) {\r
5892         xNew = 0;\r
5893     } else if ((xNew+wChild) > wScreen) {\r
5894         xNew = wScreen - wChild;\r
5895     }\r
5896 \r
5897     /* Calculate new Y position, then adjust for screen */\r
5898     if( mode == 0 ) {\r
5899         yNew = rParent.top  + ((hParent - hChild) /2);\r
5900     }\r
5901     else {\r
5902         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5903     }\r
5904 \r
5905     if (yNew < 0) {\r
5906         yNew = 0;\r
5907     } else if ((yNew+hChild) > hScreen) {\r
5908         yNew = hScreen - hChild;\r
5909     }\r
5910 \r
5911     /* Set it, and return */\r
5912     return SetWindowPos (hwndChild, NULL,\r
5913                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5914 }\r
5915 \r
5916 /* Center one window over another */\r
5917 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5918 {\r
5919     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5920 }\r
5921 \r
5922 /*---------------------------------------------------------------------------*\\r
5923  *\r
5924  * Startup Dialog functions\r
5925  *\r
5926 \*---------------------------------------------------------------------------*/\r
5927 void\r
5928 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5929 {\r
5930   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5931 \r
5932   while (*cd != NULL) {\r
5933     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5934     cd++;\r
5935   }\r
5936 }\r
5937 \r
5938 void\r
5939 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5940 {\r
5941   char buf1[MAX_ARG_LEN];\r
5942   int len;\r
5943 \r
5944   if (str[0] == '@') {\r
5945     FILE* f = fopen(str + 1, "r");\r
5946     if (f == NULL) {\r
5947       DisplayFatalError(str + 1, errno, 2);\r
5948       return;\r
5949     }\r
5950     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5951     fclose(f);\r
5952     buf1[len] = NULLCHAR;\r
5953     str = buf1;\r
5954   }\r
5955 \r
5956   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5957 \r
5958   for (;;) {\r
5959     char buf[MSG_SIZ];\r
5960     char *end = strchr(str, '\n');\r
5961     if (end == NULL) return;\r
5962     memcpy(buf, str, end - str);\r
5963     buf[end - str] = NULLCHAR;\r
5964     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5965     str = end + 1;\r
5966   }\r
5967 }\r
5968 \r
5969 void\r
5970 SetStartupDialogEnables(HWND hDlg)\r
5971 {\r
5972   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
5973     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5974     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
5975   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5976     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
5977   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
5978     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
5979   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
5980     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
5981   EnableWindow(GetDlgItem(hDlg, IDOK),\r
5982     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5983     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
5984     IsDlgButtonChecked(hDlg, OPT_View));\r
5985 }\r
5986 \r
5987 char *\r
5988 QuoteForFilename(char *filename)\r
5989 {\r
5990   int dquote, space;\r
5991   dquote = strchr(filename, '"') != NULL;\r
5992   space = strchr(filename, ' ') != NULL;\r
5993   if (dquote || space) {\r
5994     if (dquote) {\r
5995       return "'";\r
5996     } else {\r
5997       return "\"";\r
5998     }\r
5999   } else {\r
6000     return "";\r
6001   }\r
6002 }\r
6003 \r
6004 VOID\r
6005 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6006 {\r
6007   char buf[MSG_SIZ];\r
6008   char *q;\r
6009 \r
6010   InitComboStringsFromOption(hwndCombo, nthnames);\r
6011   q = QuoteForFilename(nthcp);\r
6012     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6013   if (*nthdir != NULLCHAR) {\r
6014     q = QuoteForFilename(nthdir);\r
6015       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6016   }\r
6017   if (*nthcp == NULLCHAR) {\r
6018     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6019   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6020     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6021     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6022   }\r
6023 }\r
6024 \r
6025 LRESULT CALLBACK\r
6026 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6027 {\r
6028   char buf[MSG_SIZ];\r
6029   HANDLE hwndCombo;\r
6030   char *p;\r
6031 \r
6032   switch (message) {\r
6033   case WM_INITDIALOG:\r
6034     /* Center the dialog */\r
6035     CenterWindow (hDlg, GetDesktopWindow());\r
6036     Translate(hDlg, DLG_Startup);\r
6037     /* Initialize the dialog items */\r
6038     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6039                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6040                   firstChessProgramNames);\r
6041     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6042                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6043                   secondChessProgramNames);\r
6044     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6045     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6046       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6047     if (*appData.icsHelper != NULLCHAR) {\r
6048       char *q = QuoteForFilename(appData.icsHelper);\r
6049       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6050     }\r
6051     if (*appData.icsHost == NULLCHAR) {\r
6052       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6053       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6054     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6055       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6056       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6057     }\r
6058 \r
6059     if (appData.icsActive) {\r
6060       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6061     }\r
6062     else if (appData.noChessProgram) {\r
6063       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6064     }\r
6065     else {\r
6066       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6067     }\r
6068 \r
6069     SetStartupDialogEnables(hDlg);\r
6070     return TRUE;\r
6071 \r
6072   case WM_COMMAND:\r
6073     switch (LOWORD(wParam)) {\r
6074     case IDOK:\r
6075       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6076         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6077         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6078         p = buf;\r
6079         ParseArgs(StringGet, &p);\r
6080         safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6081         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6082         p = buf;\r
6083         ParseArgs(StringGet, &p);\r
6084         appData.noChessProgram = FALSE;\r
6085         appData.icsActive = FALSE;\r
6086       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6087         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6088         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6089         p = buf;\r
6090         ParseArgs(StringGet, &p);\r
6091         if (appData.zippyPlay) {\r
6092           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6093           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6094           p = buf;\r
6095           ParseArgs(StringGet, &p);\r
6096         }\r
6097       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6098         appData.noChessProgram = TRUE;\r
6099         appData.icsActive = FALSE;\r
6100       } else {\r
6101         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6102                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6103         return TRUE;\r
6104       }\r
6105       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6106         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6107         p = buf;\r
6108         ParseArgs(StringGet, &p);\r
6109       }\r
6110       EndDialog(hDlg, TRUE);\r
6111       return TRUE;\r
6112 \r
6113     case IDCANCEL:\r
6114       ExitEvent(0);\r
6115       return TRUE;\r
6116 \r
6117     case IDM_HELPCONTENTS:\r
6118       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6119         MessageBox (GetFocus(),\r
6120                     _("Unable to activate help"),\r
6121                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6122       }\r
6123       break;\r
6124 \r
6125     default:\r
6126       SetStartupDialogEnables(hDlg);\r
6127       break;\r
6128     }\r
6129     break;\r
6130   }\r
6131   return FALSE;\r
6132 }\r
6133 \r
6134 /*---------------------------------------------------------------------------*\\r
6135  *\r
6136  * About box dialog functions\r
6137  *\r
6138 \*---------------------------------------------------------------------------*/\r
6139 \r
6140 /* Process messages for "About" dialog box */\r
6141 LRESULT CALLBACK\r
6142 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6143 {\r
6144   switch (message) {\r
6145   case WM_INITDIALOG: /* message: initialize dialog box */\r
6146     /* Center the dialog over the application window */\r
6147     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6148     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6149     Translate(hDlg, ABOUTBOX);\r
6150     JAWS_COPYRIGHT\r
6151     return (TRUE);\r
6152 \r
6153   case WM_COMMAND: /* message: received a command */\r
6154     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6155         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6156       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6157       return (TRUE);\r
6158     }\r
6159     break;\r
6160   }\r
6161   return (FALSE);\r
6162 }\r
6163 \r
6164 /*---------------------------------------------------------------------------*\\r
6165  *\r
6166  * Comment Dialog functions\r
6167  *\r
6168 \*---------------------------------------------------------------------------*/\r
6169 \r
6170 LRESULT CALLBACK\r
6171 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6172 {\r
6173   static HANDLE hwndText = NULL;\r
6174   int len, newSizeX, newSizeY, flags;\r
6175   static int sizeX, sizeY;\r
6176   char *str;\r
6177   RECT rect;\r
6178   MINMAXINFO *mmi;\r
6179 \r
6180   switch (message) {\r
6181   case WM_INITDIALOG: /* message: initialize dialog box */\r
6182     /* Initialize the dialog items */\r
6183     Translate(hDlg, DLG_EditComment);\r
6184     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6185     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6186     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6187     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6188     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6189     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6190     SetWindowText(hDlg, commentTitle);\r
6191     if (editComment) {\r
6192       SetFocus(hwndText);\r
6193     } else {\r
6194       SetFocus(GetDlgItem(hDlg, IDOK));\r
6195     }\r
6196     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6197                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6198                 MAKELPARAM(FALSE, 0));\r
6199     /* Size and position the dialog */\r
6200     if (!commentDialog) {\r
6201       commentDialog = hDlg;\r
6202       flags = SWP_NOZORDER;\r
6203       GetClientRect(hDlg, &rect);\r
6204       sizeX = rect.right;\r
6205       sizeY = rect.bottom;\r
6206       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6207           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6208         WINDOWPLACEMENT wp;\r
6209         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6210         wp.length = sizeof(WINDOWPLACEMENT);\r
6211         wp.flags = 0;\r
6212         wp.showCmd = SW_SHOW;\r
6213         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6214         wp.rcNormalPosition.left = wpComment.x;\r
6215         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6216         wp.rcNormalPosition.top = wpComment.y;\r
6217         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6218         SetWindowPlacement(hDlg, &wp);\r
6219 \r
6220         GetClientRect(hDlg, &rect);\r
6221         newSizeX = rect.right;\r
6222         newSizeY = rect.bottom;\r
6223         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6224                               newSizeX, newSizeY);\r
6225         sizeX = newSizeX;\r
6226         sizeY = newSizeY;\r
6227       }\r
6228     }\r
6229     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );\r
6230     return FALSE;\r
6231 \r
6232   case WM_COMMAND: /* message: received a command */\r
6233     switch (LOWORD(wParam)) {\r
6234     case IDOK:\r
6235       if (editComment) {\r
6236         char *p, *q;\r
6237         /* Read changed options from the dialog box */\r
6238         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6239         len = GetWindowTextLength(hwndText);\r
6240         str = (char *) malloc(len + 1);\r
6241         GetWindowText(hwndText, str, len + 1);\r
6242         p = q = str;\r
6243         while (*q) {\r
6244           if (*q == '\r')\r
6245             q++;\r
6246           else\r
6247             *p++ = *q++;\r
6248         }\r
6249         *p = NULLCHAR;\r
6250         ReplaceComment(commentIndex, str);\r
6251         free(str);\r
6252       }\r
6253       CommentPopDown();\r
6254       return TRUE;\r
6255 \r
6256     case IDCANCEL:\r
6257     case OPT_CancelComment:\r
6258       CommentPopDown();\r
6259       return TRUE;\r
6260 \r
6261     case OPT_ClearComment:\r
6262       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6263       break;\r
6264 \r
6265     case OPT_EditComment:\r
6266       EditCommentEvent();\r
6267       return TRUE;\r
6268 \r
6269     default:\r
6270       break;\r
6271     }\r
6272     break;\r
6273 \r
6274   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6275         if( wParam == OPT_CommentText ) {\r
6276             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6277 \r
6278             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {\r
6279                 POINTL pt;\r
6280                 LRESULT index;\r
6281 \r
6282                 pt.x = LOWORD( lpMF->lParam );\r
6283                 pt.y = HIWORD( lpMF->lParam );\r
6284 \r
6285                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6286 \r
6287                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6288                 len = GetWindowTextLength(hwndText);\r
6289                 str = (char *) malloc(len + 1);\r
6290                 GetWindowText(hwndText, str, len + 1);\r
6291                 ReplaceComment(commentIndex, str);\r
6292                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6293                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6294                 free(str);\r
6295 \r
6296                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6297                 lpMF->msg = WM_USER;\r
6298 \r
6299                 return TRUE;\r
6300             }\r
6301         }\r
6302         break;\r
6303 \r
6304   case WM_SIZE:\r
6305     newSizeX = LOWORD(lParam);\r
6306     newSizeY = HIWORD(lParam);\r
6307     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6308     sizeX = newSizeX;\r
6309     sizeY = newSizeY;\r
6310     break;\r
6311 \r
6312   case WM_GETMINMAXINFO:\r
6313     /* Prevent resizing window too small */\r
6314     mmi = (MINMAXINFO *) lParam;\r
6315     mmi->ptMinTrackSize.x = 100;\r
6316     mmi->ptMinTrackSize.y = 100;\r
6317     break;\r
6318   }\r
6319   return FALSE;\r
6320 }\r
6321 \r
6322 VOID\r
6323 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6324 {\r
6325   FARPROC lpProc;\r
6326   char *p, *q;\r
6327 \r
6328   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6329 \r
6330   if (str == NULL) str = "";\r
6331   p = (char *) malloc(2 * strlen(str) + 2);\r
6332   q = p;\r
6333   while (*str) {\r
6334     if (*str == '\n') *q++ = '\r';\r
6335     *q++ = *str++;\r
6336   }\r
6337   *q = NULLCHAR;\r
6338   if (commentText != NULL) free(commentText);\r
6339 \r
6340   commentIndex = index;\r
6341   commentTitle = title;\r
6342   commentText = p;\r
6343   editComment = edit;\r
6344 \r
6345   if (commentDialog) {\r
6346     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6347     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6348   } else {\r
6349     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6350     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6351                  hwndMain, (DLGPROC)lpProc);\r
6352     FreeProcInstance(lpProc);\r
6353   }\r
6354   commentUp = TRUE;\r
6355 }\r
6356 \r
6357 \r
6358 /*---------------------------------------------------------------------------*\\r
6359  *\r
6360  * Type-in move dialog functions\r
6361  * \r
6362 \*---------------------------------------------------------------------------*/\r
6363 \r
6364 LRESULT CALLBACK\r
6365 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6366 {\r
6367   char move[MSG_SIZ];\r
6368   HWND hInput;\r
6369   ChessMove moveType;\r
6370   int fromX, fromY, toX, toY;\r
6371   char promoChar;\r
6372 \r
6373   switch (message) {\r
6374   case WM_INITDIALOG:\r
6375     move[0] = (char) lParam;\r
6376     move[1] = NULLCHAR;\r
6377     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6378     Translate(hDlg, DLG_TypeInMove);\r
6379     hInput = GetDlgItem(hDlg, OPT_Move);\r
6380     SetWindowText(hInput, move);\r
6381     SetFocus(hInput);\r
6382     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6383     return FALSE;\r
6384 \r
6385   case WM_COMMAND:\r
6386     switch (LOWORD(wParam)) {\r
6387     case IDOK:
6388       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6389       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6390       { int n; Board board;\r
6391         // [HGM] FENedit\r
6392         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
6393                 EditPositionPasteFEN(move);\r
6394                 EndDialog(hDlg, TRUE);\r
6395                 return TRUE;\r
6396         }\r
6397         // [HGM] movenum: allow move number to be typed in any mode\r
6398         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
6399           ToNrEvent(2*n-1);\r
6400           EndDialog(hDlg, TRUE);\r
6401           return TRUE;\r
6402         }\r
6403       }\r
6404       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6405         gameMode != Training) {\r
6406         DisplayMoveError(_("Displayed move is not current"));\r
6407       } else {\r
6408 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
6409         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6410           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
6411         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
6412         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6413           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6414           if (gameMode != Training)\r
6415               forwardMostMove = currentMove;\r
6416           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6417         } else {\r
6418           DisplayMoveError(_("Could not parse move"));\r
6419         }\r
6420       }\r
6421       EndDialog(hDlg, TRUE);\r
6422       return TRUE;\r
6423     case IDCANCEL:\r
6424       EndDialog(hDlg, FALSE);\r
6425       return TRUE;\r
6426     default:\r
6427       break;\r
6428     }\r
6429     break;\r
6430   }\r
6431   return FALSE;\r
6432 }\r
6433 \r
6434 VOID\r
6435 PopUpMoveDialog(char firstchar)\r
6436 {\r
6437     FARPROC lpProc;\r
6438     \r
6439     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6440         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6441         gameMode == AnalyzeMode || gameMode == EditGame || \r
6442         gameMode == EditPosition || gameMode == IcsExamining ||\r
6443         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6444         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
6445                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
6446                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
6447         gameMode == Training) {\r
6448       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6449       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6450         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6451       FreeProcInstance(lpProc);\r
6452     }\r
6453 }\r
6454 \r
6455 /*---------------------------------------------------------------------------*\\r
6456  *\r
6457  * Type-in name dialog functions\r
6458  * \r
6459 \*---------------------------------------------------------------------------*/\r
6460 \r
6461 LRESULT CALLBACK\r
6462 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6463 {\r
6464   char move[MSG_SIZ];\r
6465   HWND hInput;\r
6466 \r
6467   switch (message) {\r
6468   case WM_INITDIALOG:\r
6469     move[0] = (char) lParam;\r
6470     move[1] = NULLCHAR;\r
6471     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6472     Translate(hDlg, DLG_TypeInName);\r
6473     hInput = GetDlgItem(hDlg, OPT_Name);\r
6474     SetWindowText(hInput, move);\r
6475     SetFocus(hInput);\r
6476     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6477     return FALSE;\r
6478 \r
6479   case WM_COMMAND:\r
6480     switch (LOWORD(wParam)) {\r
6481     case IDOK:\r
6482       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6483       appData.userName = strdup(move);\r
6484       SetUserLogo();\r
6485       SetGameInfo();\r
6486       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6487         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6488         DisplayTitle(move);\r
6489       }\r
6490 \r
6491 \r
6492       EndDialog(hDlg, TRUE);\r
6493       return TRUE;\r
6494     case IDCANCEL:\r
6495       EndDialog(hDlg, FALSE);\r
6496       return TRUE;\r
6497     default:\r
6498       break;\r
6499     }\r
6500     break;\r
6501   }\r
6502   return FALSE;\r
6503 }\r
6504 \r
6505 VOID\r
6506 PopUpNameDialog(char firstchar)\r
6507 {\r
6508     FARPROC lpProc;\r
6509     \r
6510       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6511       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6512         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6513       FreeProcInstance(lpProc);\r
6514 }\r
6515 \r
6516 /*---------------------------------------------------------------------------*\\r
6517  *\r
6518  *  Error dialogs\r
6519  * \r
6520 \*---------------------------------------------------------------------------*/\r
6521 \r
6522 /* Nonmodal error box */\r
6523 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6524                              WPARAM wParam, LPARAM lParam);\r
6525 \r
6526 VOID\r
6527 ErrorPopUp(char *title, char *content)\r
6528 {\r
6529   FARPROC lpProc;\r
6530   char *p, *q;\r
6531   BOOLEAN modal = hwndMain == NULL;\r
6532 \r
6533   p = content;\r
6534   q = errorMessage;\r
6535   while (*p) {\r
6536     if (*p == '\n') {\r
6537       if (modal) {\r
6538         *q++ = ' ';\r
6539         p++;\r
6540       } else {\r
6541         *q++ = '\r';\r
6542         *q++ = *p++;\r
6543       }\r
6544     } else {\r
6545       *q++ = *p++;\r
6546     }\r
6547   }\r
6548   *q = NULLCHAR;\r
6549   strncpy(errorTitle, title, sizeof(errorTitle));\r
6550   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6551   \r
6552   if (modal) {\r
6553     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6554   } else {\r
6555     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6556     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6557                  hwndMain, (DLGPROC)lpProc);\r
6558     FreeProcInstance(lpProc);\r
6559   }\r
6560 }\r
6561 \r
6562 VOID\r
6563 ErrorPopDown()\r
6564 {\r
6565   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6566   if (errorDialog == NULL) return;\r
6567   DestroyWindow(errorDialog);\r
6568   errorDialog = NULL;\r
6569   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6570 }\r
6571 \r
6572 LRESULT CALLBACK\r
6573 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6574 {\r
6575   HANDLE hwndText;\r
6576   RECT rChild;\r
6577 \r
6578   switch (message) {\r
6579   case WM_INITDIALOG:\r
6580     GetWindowRect(hDlg, &rChild);\r
6581 \r
6582     /*\r
6583     SetWindowPos(hDlg, NULL, rChild.left,\r
6584       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6585       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6586     */\r
6587 \r
6588     /* \r
6589         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6590         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6591         and it doesn't work when you resize the dialog.\r
6592         For now, just give it a default position.\r
6593     */\r
6594     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6595     Translate(hDlg, DLG_Error);\r
6596 \r
6597     errorDialog = hDlg;\r
6598     SetWindowText(hDlg, errorTitle);\r
6599     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6600     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6601     return FALSE;\r
6602 \r
6603   case WM_COMMAND:\r
6604     switch (LOWORD(wParam)) {\r
6605     case IDOK:\r
6606     case IDCANCEL:\r
6607       if (errorDialog == hDlg) errorDialog = NULL;\r
6608       DestroyWindow(hDlg);\r
6609       return TRUE;\r
6610 \r
6611     default:\r
6612       break;\r
6613     }\r
6614     break;\r
6615   }\r
6616   return FALSE;\r
6617 }\r
6618 \r
6619 #ifdef GOTHIC\r
6620 HWND gothicDialog = NULL;\r
6621 \r
6622 LRESULT CALLBACK\r
6623 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6624 {\r
6625   HANDLE hwndText;\r
6626   RECT rChild;\r
6627   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6628 \r
6629   switch (message) {\r
6630   case WM_INITDIALOG:\r
6631     GetWindowRect(hDlg, &rChild);\r
6632 \r
6633     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6634                                                              SWP_NOZORDER);\r
6635 \r
6636     /* \r
6637         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6638         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6639         and it doesn't work when you resize the dialog.\r
6640         For now, just give it a default position.\r
6641     */\r
6642     gothicDialog = hDlg;\r
6643     SetWindowText(hDlg, errorTitle);\r
6644     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6645     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6646     return FALSE;\r
6647 \r
6648   case WM_COMMAND:\r
6649     switch (LOWORD(wParam)) {\r
6650     case IDOK:\r
6651     case IDCANCEL:\r
6652       if (errorDialog == hDlg) errorDialog = NULL;\r
6653       DestroyWindow(hDlg);\r
6654       return TRUE;\r
6655 \r
6656     default:\r
6657       break;\r
6658     }\r
6659     break;\r
6660   }\r
6661   return FALSE;\r
6662 }\r
6663 \r
6664 VOID\r
6665 GothicPopUp(char *title, VariantClass variant)\r
6666 {\r
6667   FARPROC lpProc;\r
6668   static char *lastTitle;\r
6669 \r
6670   strncpy(errorTitle, title, sizeof(errorTitle));\r
6671   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6672 \r
6673   if(lastTitle != title && gothicDialog != NULL) {\r
6674     DestroyWindow(gothicDialog);\r
6675     gothicDialog = NULL;\r
6676   }\r
6677   if(variant != VariantNormal && gothicDialog == NULL) {\r
6678     title = lastTitle;\r
6679     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6680     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6681                  hwndMain, (DLGPROC)lpProc);\r
6682     FreeProcInstance(lpProc);\r
6683   }\r
6684 }\r
6685 #endif\r
6686 \r
6687 /*---------------------------------------------------------------------------*\\r
6688  *\r
6689  *  Ics Interaction console functions\r
6690  *\r
6691 \*---------------------------------------------------------------------------*/\r
6692 \r
6693 #define HISTORY_SIZE 64\r
6694 static char *history[HISTORY_SIZE];\r
6695 int histIn = 0, histP = 0;\r
6696 \r
6697 VOID\r
6698 SaveInHistory(char *cmd)\r
6699 {\r
6700   if (history[histIn] != NULL) {\r
6701     free(history[histIn]);\r
6702     history[histIn] = NULL;\r
6703   }\r
6704   if (*cmd == NULLCHAR) return;\r
6705   history[histIn] = StrSave(cmd);\r
6706   histIn = (histIn + 1) % HISTORY_SIZE;\r
6707   if (history[histIn] != NULL) {\r
6708     free(history[histIn]);\r
6709     history[histIn] = NULL;\r
6710   }\r
6711   histP = histIn;\r
6712 }\r
6713 \r
6714 char *\r
6715 PrevInHistory(char *cmd)\r
6716 {\r
6717   int newhp;\r
6718   if (histP == histIn) {\r
6719     if (history[histIn] != NULL) free(history[histIn]);\r
6720     history[histIn] = StrSave(cmd);\r
6721   }\r
6722   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6723   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6724   histP = newhp;\r
6725   return history[histP];\r
6726 }\r
6727 \r
6728 char *\r
6729 NextInHistory()\r
6730 {\r
6731   if (histP == histIn) return NULL;\r
6732   histP = (histP + 1) % HISTORY_SIZE;\r
6733   return history[histP];   \r
6734 }\r
6735 \r
6736 HMENU\r
6737 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6738 {\r
6739   HMENU hmenu, h;\r
6740   int i = 0;\r
6741   hmenu = LoadMenu(hInst, "TextMenu");\r
6742   h = GetSubMenu(hmenu, 0);\r
6743   while (e->item) {\r
6744     if (strcmp(e->item, "-") == 0) {\r
6745       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6746     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6747       int flags = MF_STRING, j = 0;\r
6748       if (e->item[0] == '|') {\r
6749         flags |= MF_MENUBARBREAK;\r
6750         j++;\r
6751       }\r
6752       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6753       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6754     }\r
6755     e++;\r
6756     i++;\r
6757   } \r
6758   return hmenu;\r
6759 }\r
6760 \r
6761 WNDPROC consoleTextWindowProc;\r
6762 \r
6763 void\r
6764 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6765 {\r
6766   char buf[MSG_SIZ], name[MSG_SIZ];\r
6767   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6768   CHARRANGE sel;\r
6769 \r
6770   if (!getname) {\r
6771     SetWindowText(hInput, command);\r
6772     if (immediate) {\r
6773       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6774     } else {\r
6775       sel.cpMin = 999999;\r
6776       sel.cpMax = 999999;\r
6777       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6778       SetFocus(hInput);\r
6779     }\r
6780     return;\r
6781   }    \r
6782   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6783   if (sel.cpMin == sel.cpMax) {\r
6784     /* Expand to surrounding word */\r
6785     TEXTRANGE tr;\r
6786     do {\r
6787       tr.chrg.cpMax = sel.cpMin;\r
6788       tr.chrg.cpMin = --sel.cpMin;\r
6789       if (sel.cpMin < 0) break;\r
6790       tr.lpstrText = name;\r
6791       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6792     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6793     sel.cpMin++;\r
6794 \r
6795     do {\r
6796       tr.chrg.cpMin = sel.cpMax;\r
6797       tr.chrg.cpMax = ++sel.cpMax;\r
6798       tr.lpstrText = name;\r
6799       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6800     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6801     sel.cpMax--;\r
6802 \r
6803     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6804       MessageBeep(MB_ICONEXCLAMATION);\r
6805       return;\r
6806     }\r
6807     tr.chrg = sel;\r
6808     tr.lpstrText = name;\r
6809     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6810   } else {\r
6811     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6812       MessageBeep(MB_ICONEXCLAMATION);\r
6813       return;\r
6814     }\r
6815     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6816   }\r
6817   if (immediate) {\r
6818     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6819     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6820     SetWindowText(hInput, buf);\r
6821     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6822   } else {\r
6823     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6824       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6825     SetWindowText(hInput, buf);\r
6826     sel.cpMin = 999999;\r
6827     sel.cpMax = 999999;\r
6828     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6829     SetFocus(hInput);\r
6830   }\r
6831 }\r
6832 \r
6833 LRESULT CALLBACK \r
6834 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6835 {\r
6836   HWND hInput;\r
6837   CHARRANGE sel;\r
6838 \r
6839   switch (message) {\r
6840   case WM_KEYDOWN:\r
6841     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6842     switch (wParam) {\r
6843     case VK_PRIOR:\r
6844       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6845       return 0;\r
6846     case VK_NEXT:\r
6847       sel.cpMin = 999999;\r
6848       sel.cpMax = 999999;\r
6849       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6850       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6851       return 0;\r
6852     }\r
6853     break;\r
6854   case WM_CHAR:\r
6855    if(wParam != '\022') {\r
6856     if (wParam == '\t') {\r
6857       if (GetKeyState(VK_SHIFT) < 0) {\r
6858         /* shifted */\r
6859         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6860         if (buttonDesc[0].hwnd) {\r
6861           SetFocus(buttonDesc[0].hwnd);\r
6862         } else {\r
6863           SetFocus(hwndMain);\r
6864         }\r
6865       } else {\r
6866         /* unshifted */\r
6867         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6868       }\r
6869     } else {\r
6870       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6871       JAWS_DELETE( SetFocus(hInput); )\r
6872       SendMessage(hInput, message, wParam, lParam);\r
6873     }\r
6874     return 0;\r
6875    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
6876   case WM_RBUTTONDOWN:\r
6877     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6878       /* Move selection here if it was empty */\r
6879       POINT pt;\r
6880       pt.x = LOWORD(lParam);\r
6881       pt.y = HIWORD(lParam);\r
6882       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6883       if (sel.cpMin == sel.cpMax) {\r
6884         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6885         sel.cpMax = sel.cpMin;\r
6886         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6887       }\r
6888       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6889 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6890       POINT pt;\r
6891       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6892       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6893       if (sel.cpMin == sel.cpMax) {\r
6894         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6895         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6896       }\r
6897       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6898         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6899       }\r
6900       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6901       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6902       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6903       MenuPopup(hwnd, pt, hmenu, -1);\r
6904 }\r
6905     }\r
6906     return 0;\r
6907   case WM_RBUTTONUP:\r
6908     if (GetKeyState(VK_SHIFT) & ~1) {\r
6909       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6910         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6911     }\r
6912     return 0;\r
6913   case WM_PASTE:\r
6914     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6915     SetFocus(hInput);\r
6916     return SendMessage(hInput, message, wParam, lParam);\r
6917   case WM_MBUTTONDOWN:\r
6918     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6919   case WM_COMMAND:\r
6920     switch (LOWORD(wParam)) {\r
6921     case IDM_QuickPaste:\r
6922       {\r
6923         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6924         if (sel.cpMin == sel.cpMax) {\r
6925           MessageBeep(MB_ICONEXCLAMATION);\r
6926           return 0;\r
6927         }\r
6928         SendMessage(hwnd, WM_COPY, 0, 0);\r
6929         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6930         SendMessage(hInput, WM_PASTE, 0, 0);\r
6931         SetFocus(hInput);\r
6932         return 0;\r
6933       }\r
6934     case IDM_Cut:\r
6935       SendMessage(hwnd, WM_CUT, 0, 0);\r
6936       return 0;\r
6937     case IDM_Paste:\r
6938       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6939       return 0;\r
6940     case IDM_Copy:\r
6941       SendMessage(hwnd, WM_COPY, 0, 0);\r
6942       return 0;\r
6943     default:\r
6944       {\r
6945         int i = LOWORD(wParam) - IDM_CommandX;\r
6946         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6947             icsTextMenuEntry[i].command != NULL) {\r
6948           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6949                    icsTextMenuEntry[i].getname,\r
6950                    icsTextMenuEntry[i].immediate);\r
6951           return 0;\r
6952         }\r
6953       }\r
6954       break;\r
6955     }\r
6956     break;\r
6957   }\r
6958   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6959 }\r
6960 \r
6961 WNDPROC consoleInputWindowProc;\r
6962 \r
6963 LRESULT CALLBACK\r
6964 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6965 {\r
6966   char buf[MSG_SIZ];\r
6967   char *p;\r
6968   static BOOL sendNextChar = FALSE;\r
6969   static BOOL quoteNextChar = FALSE;\r
6970   InputSource *is = consoleInputSource;\r
6971   CHARFORMAT cf;\r
6972   CHARRANGE sel;\r
6973 \r
6974   switch (message) {\r
6975   case WM_CHAR:\r
6976     if (!appData.localLineEditing || sendNextChar) {\r
6977       is->buf[0] = (CHAR) wParam;\r
6978       is->count = 1;\r
6979       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6980       sendNextChar = FALSE;\r
6981       return 0;\r
6982     }\r
6983     if (quoteNextChar) {\r
6984       buf[0] = (char) wParam;\r
6985       buf[1] = NULLCHAR;\r
6986       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
6987       quoteNextChar = FALSE;\r
6988       return 0;\r
6989     }\r
6990     switch (wParam) {\r
6991     case '\r':   /* Enter key */\r
6992       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
6993       if (consoleEcho) SaveInHistory(is->buf);\r
6994       is->buf[is->count++] = '\n';\r
6995       is->buf[is->count] = NULLCHAR;\r
6996       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6997       if (consoleEcho) {\r
6998         ConsoleOutput(is->buf, is->count, TRUE);\r
6999       } else if (appData.localLineEditing) {\r
7000         ConsoleOutput("\n", 1, TRUE);\r
7001       }\r
7002       /* fall thru */\r
7003     case '\033': /* Escape key */\r
7004       SetWindowText(hwnd, "");\r
7005       cf.cbSize = sizeof(CHARFORMAT);\r
7006       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7007       if (consoleEcho) {\r
7008         cf.crTextColor = textAttribs[ColorNormal].color;\r
7009       } else {\r
7010         cf.crTextColor = COLOR_ECHOOFF;\r
7011       }\r
7012       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7013       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7014       return 0;\r
7015     case '\t':   /* Tab key */\r
7016       if (GetKeyState(VK_SHIFT) < 0) {\r
7017         /* shifted */\r
7018         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7019       } else {\r
7020         /* unshifted */\r
7021         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7022         if (buttonDesc[0].hwnd) {\r
7023           SetFocus(buttonDesc[0].hwnd);\r
7024         } else {\r
7025           SetFocus(hwndMain);\r
7026         }\r
7027       }\r
7028       return 0;\r
7029     case '\023': /* Ctrl+S */\r
7030       sendNextChar = TRUE;\r
7031       return 0;\r
7032     case '\021': /* Ctrl+Q */\r
7033       quoteNextChar = TRUE;\r
7034       return 0;\r
7035     JAWS_REPLAY\r
7036     default:\r
7037       break;\r
7038     }\r
7039     break;\r
7040   case WM_KEYDOWN:\r
7041     switch (wParam) {\r
7042     case VK_UP:\r
7043       GetWindowText(hwnd, buf, MSG_SIZ);\r
7044       p = PrevInHistory(buf);\r
7045       if (p != NULL) {\r
7046         SetWindowText(hwnd, p);\r
7047         sel.cpMin = 999999;\r
7048         sel.cpMax = 999999;\r
7049         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7050         return 0;\r
7051       }\r
7052       break;\r
7053     case VK_DOWN:\r
7054       p = NextInHistory();\r
7055       if (p != NULL) {\r
7056         SetWindowText(hwnd, p);\r
7057         sel.cpMin = 999999;\r
7058         sel.cpMax = 999999;\r
7059         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7060         return 0;\r
7061       }\r
7062       break;\r
7063     case VK_HOME:\r
7064     case VK_END:\r
7065       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7066       /* fall thru */\r
7067     case VK_PRIOR:\r
7068     case VK_NEXT:\r
7069       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7070       return 0;\r
7071     }\r
7072     break;\r
7073   case WM_MBUTTONDOWN:\r
7074     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7075       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7076     break;\r
7077   case WM_RBUTTONUP:\r
7078     if (GetKeyState(VK_SHIFT) & ~1) {\r
7079       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7080         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7081     } else {\r
7082       POINT pt;\r
7083       HMENU hmenu;\r
7084       hmenu = LoadMenu(hInst, "InputMenu");\r
7085       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7086       if (sel.cpMin == sel.cpMax) {\r
7087         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7088         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7089       }\r
7090       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7091         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7092       }\r
7093       pt.x = LOWORD(lParam);\r
7094       pt.y = HIWORD(lParam);\r
7095       MenuPopup(hwnd, pt, hmenu, -1);\r
7096     }\r
7097     return 0;\r
7098   case WM_COMMAND:\r
7099     switch (LOWORD(wParam)) { \r
7100     case IDM_Undo:\r
7101       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7102       return 0;\r
7103     case IDM_SelectAll:\r
7104       sel.cpMin = 0;\r
7105       sel.cpMax = -1; /*999999?*/\r
7106       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7107       return 0;\r
7108     case IDM_Cut:\r
7109       SendMessage(hwnd, WM_CUT, 0, 0);\r
7110       return 0;\r
7111     case IDM_Paste:\r
7112       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7113       return 0;\r
7114     case IDM_Copy:\r
7115       SendMessage(hwnd, WM_COPY, 0, 0);\r
7116       return 0;\r
7117     }\r
7118     break;\r
7119   }\r
7120   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7121 }\r
7122 \r
7123 #define CO_MAX  100000\r
7124 #define CO_TRIM   1000\r
7125 \r
7126 LRESULT CALLBACK\r
7127 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7128 {\r
7129   static SnapData sd;\r
7130   HWND hText, hInput;\r
7131   RECT rect;\r
7132   static int sizeX, sizeY;\r
7133   int newSizeX, newSizeY;\r
7134   MINMAXINFO *mmi;\r
7135   WORD wMask;\r
7136 \r
7137   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7138   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7139 \r
7140   switch (message) {\r
7141   case WM_NOTIFY:\r
7142     if (((NMHDR*)lParam)->code == EN_LINK)\r
7143     {\r
7144       ENLINK *pLink = (ENLINK*)lParam;\r
7145       if (pLink->msg == WM_LBUTTONUP)\r
7146       {\r
7147         TEXTRANGE tr;\r
7148 \r
7149         tr.chrg = pLink->chrg;\r
7150         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7151         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7152         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7153         free(tr.lpstrText);\r
7154       }\r
7155     }\r
7156     break;\r
7157   case WM_INITDIALOG: /* message: initialize dialog box */\r
7158     hwndConsole = hDlg;\r
7159     SetFocus(hInput);\r
7160     consoleTextWindowProc = (WNDPROC)\r
7161       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7162     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7163     consoleInputWindowProc = (WNDPROC)\r
7164       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7165     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7166     Colorize(ColorNormal, TRUE);\r
7167     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7168     ChangedConsoleFont();\r
7169     GetClientRect(hDlg, &rect);\r
7170     sizeX = rect.right;\r
7171     sizeY = rect.bottom;\r
7172     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7173         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7174       WINDOWPLACEMENT wp;\r
7175       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7176       wp.length = sizeof(WINDOWPLACEMENT);\r
7177       wp.flags = 0;\r
7178       wp.showCmd = SW_SHOW;\r
7179       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7180       wp.rcNormalPosition.left = wpConsole.x;\r
7181       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7182       wp.rcNormalPosition.top = wpConsole.y;\r
7183       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7184       SetWindowPlacement(hDlg, &wp);\r
7185     }\r
7186 \r
7187    // [HGM] Chessknight's change 2004-07-13\r
7188    else { /* Determine Defaults */\r
7189        WINDOWPLACEMENT wp;\r
7190        wpConsole.x = wpMain.width + 1;\r
7191        wpConsole.y = wpMain.y;\r
7192        wpConsole.width = screenWidth -  wpMain.width;\r
7193        wpConsole.height = wpMain.height;\r
7194        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7195        wp.length = sizeof(WINDOWPLACEMENT);\r
7196        wp.flags = 0;\r
7197        wp.showCmd = SW_SHOW;\r
7198        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7199        wp.rcNormalPosition.left = wpConsole.x;\r
7200        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7201        wp.rcNormalPosition.top = wpConsole.y;\r
7202        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7203        SetWindowPlacement(hDlg, &wp);\r
7204     }\r
7205 \r
7206    // Allow hText to highlight URLs and send notifications on them\r
7207    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7208    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7209    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7210    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
7211 \r
7212     return FALSE;\r
7213 \r
7214   case WM_SETFOCUS:\r
7215     SetFocus(hInput);\r
7216     return 0;\r
7217 \r
7218   case WM_CLOSE:\r
7219     ExitEvent(0);\r
7220     /* not reached */\r
7221     break;\r
7222 \r
7223   case WM_SIZE:\r
7224     if (IsIconic(hDlg)) break;\r
7225     newSizeX = LOWORD(lParam);\r
7226     newSizeY = HIWORD(lParam);\r
7227     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7228       RECT rectText, rectInput;\r
7229       POINT pt;\r
7230       int newTextHeight, newTextWidth;\r
7231       GetWindowRect(hText, &rectText);\r
7232       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7233       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7234       if (newTextHeight < 0) {\r
7235         newSizeY += -newTextHeight;\r
7236         newTextHeight = 0;\r
7237       }\r
7238       SetWindowPos(hText, NULL, 0, 0,\r
7239         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7240       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7241       pt.x = rectInput.left;\r
7242       pt.y = rectInput.top + newSizeY - sizeY;\r
7243       ScreenToClient(hDlg, &pt);\r
7244       SetWindowPos(hInput, NULL, \r
7245         pt.x, pt.y, /* needs client coords */   \r
7246         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7247         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7248     }\r
7249     sizeX = newSizeX;\r
7250     sizeY = newSizeY;\r
7251     break;\r
7252 \r
7253   case WM_GETMINMAXINFO:\r
7254     /* Prevent resizing window too small */\r
7255     mmi = (MINMAXINFO *) lParam;\r
7256     mmi->ptMinTrackSize.x = 100;\r
7257     mmi->ptMinTrackSize.y = 100;\r
7258     break;\r
7259 \r
7260   /* [AS] Snapping */\r
7261   case WM_ENTERSIZEMOVE:\r
7262     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7263 \r
7264   case WM_SIZING:\r
7265     return OnSizing( &sd, hDlg, wParam, lParam );\r
7266 \r
7267   case WM_MOVING:\r
7268     return OnMoving( &sd, hDlg, wParam, lParam );\r
7269 \r
7270   case WM_EXITSIZEMOVE:\r
7271         UpdateICSWidth(hText);\r
7272     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7273   }\r
7274 \r
7275   return DefWindowProc(hDlg, message, wParam, lParam);\r
7276 }\r
7277 \r
7278 \r
7279 VOID\r
7280 ConsoleCreate()\r
7281 {\r
7282   HWND hCons;\r
7283   if (hwndConsole) return;\r
7284   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7285   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7286 }\r
7287 \r
7288 \r
7289 VOID\r
7290 ConsoleOutput(char* data, int length, int forceVisible)\r
7291 {\r
7292   HWND hText;\r
7293   int trim, exlen;\r
7294   char *p, *q;\r
7295   char buf[CO_MAX+1];\r
7296   POINT pEnd;\r
7297   RECT rect;\r
7298   static int delayLF = 0;\r
7299   CHARRANGE savesel, sel;\r
7300 \r
7301   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7302   p = data;\r
7303   q = buf;\r
7304   if (delayLF) {\r
7305     *q++ = '\r';\r
7306     *q++ = '\n';\r
7307     delayLF = 0;\r
7308   }\r
7309   while (length--) {\r
7310     if (*p == '\n') {\r
7311       if (*++p) {\r
7312         *q++ = '\r';\r
7313         *q++ = '\n';\r
7314       } else {\r
7315         delayLF = 1;\r
7316       }\r
7317     } else if (*p == '\007') {\r
7318        MyPlaySound(&sounds[(int)SoundBell]);\r
7319        p++;\r
7320     } else {\r
7321       *q++ = *p++;\r
7322     }\r
7323   }\r
7324   *q = NULLCHAR;\r
7325   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7326   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7327   /* Save current selection */\r
7328   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7329   exlen = GetWindowTextLength(hText);\r
7330   /* Find out whether current end of text is visible */\r
7331   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7332   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7333   /* Trim existing text if it's too long */\r
7334   if (exlen + (q - buf) > CO_MAX) {\r
7335     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7336     sel.cpMin = 0;\r
7337     sel.cpMax = trim;\r
7338     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7339     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7340     exlen -= trim;\r
7341     savesel.cpMin -= trim;\r
7342     savesel.cpMax -= trim;\r
7343     if (exlen < 0) exlen = 0;\r
7344     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7345     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7346   }\r
7347   /* Append the new text */\r
7348   sel.cpMin = exlen;\r
7349   sel.cpMax = exlen;\r
7350   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7351   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7352   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7353   if (forceVisible || exlen == 0 ||\r
7354       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7355        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7356     /* Scroll to make new end of text visible if old end of text\r
7357        was visible or new text is an echo of user typein */\r
7358     sel.cpMin = 9999999;\r
7359     sel.cpMax = 9999999;\r
7360     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7361     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7362     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7363     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7364   }\r
7365   if (savesel.cpMax == exlen || forceVisible) {\r
7366     /* Move insert point to new end of text if it was at the old\r
7367        end of text or if the new text is an echo of user typein */\r
7368     sel.cpMin = 9999999;\r
7369     sel.cpMax = 9999999;\r
7370     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7371   } else {\r
7372     /* Restore previous selection */\r
7373     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7374   }\r
7375   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7376 }\r
7377 \r
7378 /*---------*/\r
7379 \r
7380 \r
7381 void\r
7382 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7383 {\r
7384   char buf[100];\r
7385   char *str;\r
7386   COLORREF oldFg, oldBg;\r
7387   HFONT oldFont;\r
7388   RECT rect;\r
7389 \r
7390   if(copyNumber > 1)
7391     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7392 \r
7393   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7394   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7395   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7396 \r
7397   rect.left = x;\r
7398   rect.right = x + squareSize;\r
7399   rect.top  = y;\r
7400   rect.bottom = y + squareSize;\r
7401   str = buf;\r
7402 \r
7403   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7404                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7405              y, ETO_CLIPPED|ETO_OPAQUE,\r
7406              &rect, str, strlen(str), NULL);\r
7407 \r
7408   (void) SetTextColor(hdc, oldFg);\r
7409   (void) SetBkColor(hdc, oldBg);\r
7410   (void) SelectObject(hdc, oldFont);\r
7411 }\r
7412 \r
7413 void\r
7414 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7415               RECT *rect, char *color, char *flagFell)\r
7416 {\r
7417   char buf[100];\r
7418   char *str;\r
7419   COLORREF oldFg, oldBg;\r
7420   HFONT oldFont;\r
7421 \r
7422   if (appData.clockMode) {\r
7423     if (tinyLayout)\r
7424       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7425     else\r
7426       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7427     str = buf;\r
7428   } else {\r
7429     str = color;\r
7430   }\r
7431 \r
7432   if (highlight) {\r
7433     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7434     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7435   } else {\r
7436     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7437     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7438   }\r
7439   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7440 \r
7441   JAWS_SILENCE\r
7442 \r
7443   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7444              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7445              rect, str, strlen(str), NULL);\r
7446   if(logoHeight > 0 && appData.clockMode) {\r
7447       RECT r;\r
7448       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s %s", buf+7, flagFell);\r
7449       r.top = rect->top + logoHeight/2;\r
7450       r.left = rect->left;\r
7451       r.right = rect->right;\r
7452       r.bottom = rect->bottom;\r
7453       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7454                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7455                  &r, str, strlen(str), NULL);\r
7456   }\r
7457   (void) SetTextColor(hdc, oldFg);\r
7458   (void) SetBkColor(hdc, oldBg);\r
7459   (void) SelectObject(hdc, oldFont);\r
7460 }\r
7461 \r
7462 \r
7463 int\r
7464 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7465            OVERLAPPED *ovl)\r
7466 {\r
7467   int ok, err;\r
7468 \r
7469   /* [AS]  */\r
7470   if( count <= 0 ) {\r
7471     if (appData.debugMode) {\r
7472       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7473     }\r
7474 \r
7475     return ERROR_INVALID_USER_BUFFER;\r
7476   }\r
7477 \r
7478   ResetEvent(ovl->hEvent);\r
7479   ovl->Offset = ovl->OffsetHigh = 0;\r
7480   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7481   if (ok) {\r
7482     err = NO_ERROR;\r
7483   } else {\r
7484     err = GetLastError();\r
7485     if (err == ERROR_IO_PENDING) {\r
7486       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7487       if (ok)\r
7488         err = NO_ERROR;\r
7489       else\r
7490         err = GetLastError();\r
7491     }\r
7492   }\r
7493   return err;\r
7494 }\r
7495 \r
7496 int\r
7497 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7498             OVERLAPPED *ovl)\r
7499 {\r
7500   int ok, err;\r
7501 \r
7502   ResetEvent(ovl->hEvent);\r
7503   ovl->Offset = ovl->OffsetHigh = 0;\r
7504   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7505   if (ok) {\r
7506     err = NO_ERROR;\r
7507   } else {\r
7508     err = GetLastError();\r
7509     if (err == ERROR_IO_PENDING) {\r
7510       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7511       if (ok)\r
7512         err = NO_ERROR;\r
7513       else\r
7514         err = GetLastError();\r
7515     }\r
7516   }\r
7517   return err;\r
7518 }\r
7519 \r
7520 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7521 void CheckForInputBufferFull( InputSource * is )\r
7522 {\r
7523     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7524         /* Look for end of line */\r
7525         char * p = is->buf;\r
7526         \r
7527         while( p < is->next && *p != '\n' ) {\r
7528             p++;\r
7529         }\r
7530 \r
7531         if( p >= is->next ) {\r
7532             if (appData.debugMode) {\r
7533                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7534             }\r
7535 \r
7536             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7537             is->count = (DWORD) -1;\r
7538             is->next = is->buf;\r
7539         }\r
7540     }\r
7541 }\r
7542 \r
7543 DWORD\r
7544 InputThread(LPVOID arg)\r
7545 {\r
7546   InputSource *is;\r
7547   OVERLAPPED ovl;\r
7548 \r
7549   is = (InputSource *) arg;\r
7550   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7551   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7552   while (is->hThread != NULL) {\r
7553     is->error = DoReadFile(is->hFile, is->next,\r
7554                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7555                            &is->count, &ovl);\r
7556     if (is->error == NO_ERROR) {\r
7557       is->next += is->count;\r
7558     } else {\r
7559       if (is->error == ERROR_BROKEN_PIPE) {\r
7560         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7561         is->count = 0;\r
7562       } else {\r
7563         is->count = (DWORD) -1;\r
7564         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7565         break; \r
7566       }\r
7567     }\r
7568 \r
7569     CheckForInputBufferFull( is );\r
7570 \r
7571     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7572 \r
7573     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7574 \r
7575     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7576   }\r
7577 \r
7578   CloseHandle(ovl.hEvent);\r
7579   CloseHandle(is->hFile);\r
7580 \r
7581   if (appData.debugMode) {\r
7582     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7583   }\r
7584 \r
7585   return 0;\r
7586 }\r
7587 \r
7588 \r
7589 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7590 DWORD\r
7591 NonOvlInputThread(LPVOID arg)\r
7592 {\r
7593   InputSource *is;\r
7594   char *p, *q;\r
7595   int i;\r
7596   char prev;\r
7597 \r
7598   is = (InputSource *) arg;\r
7599   while (is->hThread != NULL) {\r
7600     is->error = ReadFile(is->hFile, is->next,\r
7601                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7602                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7603     if (is->error == NO_ERROR) {\r
7604       /* Change CRLF to LF */\r
7605       if (is->next > is->buf) {\r
7606         p = is->next - 1;\r
7607         i = is->count + 1;\r
7608       } else {\r
7609         p = is->next;\r
7610         i = is->count;\r
7611       }\r
7612       q = p;\r
7613       prev = NULLCHAR;\r
7614       while (i > 0) {\r
7615         if (prev == '\r' && *p == '\n') {\r
7616           *(q-1) = '\n';\r
7617           is->count--;\r
7618         } else { \r
7619           *q++ = *p;\r
7620         }\r
7621         prev = *p++;\r
7622         i--;\r
7623       }\r
7624       *q = NULLCHAR;\r
7625       is->next = q;\r
7626     } else {\r
7627       if (is->error == ERROR_BROKEN_PIPE) {\r
7628         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7629         is->count = 0; \r
7630       } else {\r
7631         is->count = (DWORD) -1;\r
7632       }\r
7633     }\r
7634 \r
7635     CheckForInputBufferFull( is );\r
7636 \r
7637     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7638 \r
7639     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7640 \r
7641     if (is->count < 0) break;  /* Quit on error */\r
7642   }\r
7643   CloseHandle(is->hFile);\r
7644   return 0;\r
7645 }\r
7646 \r
7647 DWORD\r
7648 SocketInputThread(LPVOID arg)\r
7649 {\r
7650   InputSource *is;\r
7651 \r
7652   is = (InputSource *) arg;\r
7653   while (is->hThread != NULL) {\r
7654     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7655     if ((int)is->count == SOCKET_ERROR) {\r
7656       is->count = (DWORD) -1;\r
7657       is->error = WSAGetLastError();\r
7658     } else {\r
7659       is->error = NO_ERROR;\r
7660       is->next += is->count;\r
7661       if (is->count == 0 && is->second == is) {\r
7662         /* End of file on stderr; quit with no message */\r
7663         break;\r
7664       }\r
7665     }\r
7666     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7667 \r
7668     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7669 \r
7670     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7671   }\r
7672   return 0;\r
7673 }\r
7674 \r
7675 VOID\r
7676 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7677 {\r
7678   InputSource *is;\r
7679 \r
7680   is = (InputSource *) lParam;\r
7681   if (is->lineByLine) {\r
7682     /* Feed in lines one by one */\r
7683     char *p = is->buf;\r
7684     char *q = p;\r
7685     while (q < is->next) {\r
7686       if (*q++ == '\n') {\r
7687         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7688         p = q;\r
7689       }\r
7690     }\r
7691     \r
7692     /* Move any partial line to the start of the buffer */\r
7693     q = is->buf;\r
7694     while (p < is->next) {\r
7695       *q++ = *p++;\r
7696     }\r
7697     is->next = q;\r
7698 \r
7699     if (is->error != NO_ERROR || is->count == 0) {\r
7700       /* Notify backend of the error.  Note: If there was a partial\r
7701          line at the end, it is not flushed through. */\r
7702       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7703     }\r
7704   } else {\r
7705     /* Feed in the whole chunk of input at once */\r
7706     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7707     is->next = is->buf;\r
7708   }\r
7709 }\r
7710 \r
7711 /*---------------------------------------------------------------------------*\\r
7712  *\r
7713  *  Menu enables. Used when setting various modes.\r
7714  *\r
7715 \*---------------------------------------------------------------------------*/\r
7716 \r
7717 typedef struct {\r
7718   int item;\r
7719   int flags;\r
7720 } Enables;\r
7721 \r
7722 VOID\r
7723 GreyRevert(Boolean grey)\r
7724 { // [HGM] vari: for retracting variations in local mode\r
7725   HMENU hmenu = GetMenu(hwndMain);\r
7726   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7727   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7728 }\r
7729 \r
7730 VOID\r
7731 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7732 {\r
7733   while (enab->item > 0) {\r
7734     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7735     enab++;\r
7736   }\r
7737 }\r
7738 \r
7739 Enables gnuEnables[] = {\r
7740   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7741   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7742   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7743   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7744   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7745   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7746   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7747   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7748   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7749   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7750   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7751   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7752   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7753   { -1, -1 }\r
7754 };\r
7755 \r
7756 Enables icsEnables[] = {\r
7757   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7758   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7759   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7760   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7761   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7762   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7763   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7764   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7765   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7766   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7767   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7768   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7769   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7770   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7771   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7772   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7773   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7774   { -1, -1 }\r
7775 };\r
7776 \r
7777 #if ZIPPY\r
7778 Enables zippyEnables[] = {\r
7779   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7780   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7781   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7782   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7783   { -1, -1 }\r
7784 };\r
7785 #endif\r
7786 \r
7787 Enables ncpEnables[] = {\r
7788   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7789   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7790   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7791   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7792   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7793   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7794   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7795   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7796   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7797   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7798   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7799   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7800   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7801   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7802   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7803   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7804   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7805   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7806   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7807   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7808   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7809   { -1, -1 }\r
7810 };\r
7811 \r
7812 Enables trainingOnEnables[] = {\r
7813   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7814   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7815   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7816   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7817   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7818   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7819   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7820   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7821   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7822   { -1, -1 }\r
7823 };\r
7824 \r
7825 Enables trainingOffEnables[] = {\r
7826   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7827   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7828   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7829   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7830   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7831   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7832   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7833   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7834   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7835   { -1, -1 }\r
7836 };\r
7837 \r
7838 /* These modify either ncpEnables or gnuEnables */\r
7839 Enables cmailEnables[] = {\r
7840   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7841   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7842   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7843   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7844   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7845   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7846   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7847   { -1, -1 }\r
7848 };\r
7849 \r
7850 Enables machineThinkingEnables[] = {\r
7851   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7852   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7853   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7854   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7855   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7856   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7857   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7858   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7859   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7860   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7861   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7862   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7863   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7864   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7865   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7866   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7867   { -1, -1 }\r
7868 };\r
7869 \r
7870 Enables userThinkingEnables[] = {\r
7871   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7872   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7873   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7874   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7875   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7876   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7877   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7878   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7879   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7880   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7881   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7882   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7883   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7884   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7885   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7886   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7887   { -1, -1 }\r
7888 };\r
7889 \r
7890 /*---------------------------------------------------------------------------*\\r
7891  *\r
7892  *  Front-end interface functions exported by XBoard.\r
7893  *  Functions appear in same order as prototypes in frontend.h.\r
7894  * \r
7895 \*---------------------------------------------------------------------------*/\r
7896 VOID\r
7897 ModeHighlight()\r
7898 {\r
7899   static UINT prevChecked = 0;\r
7900   static int prevPausing = 0;\r
7901   UINT nowChecked;\r
7902 \r
7903   if (pausing != prevPausing) {\r
7904     prevPausing = pausing;\r
7905     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7906                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7907     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7908   }\r
7909 \r
7910   switch (gameMode) {\r
7911   case BeginningOfGame:\r
7912     if (appData.icsActive)\r
7913       nowChecked = IDM_IcsClient;\r
7914     else if (appData.noChessProgram)\r
7915       nowChecked = IDM_EditGame;\r
7916     else\r
7917       nowChecked = IDM_MachineBlack;\r
7918     break;\r
7919   case MachinePlaysBlack:\r
7920     nowChecked = IDM_MachineBlack;\r
7921     break;\r
7922   case MachinePlaysWhite:\r
7923     nowChecked = IDM_MachineWhite;\r
7924     break;\r
7925   case TwoMachinesPlay:\r
7926     nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match\r
7927     break;\r
7928   case AnalyzeMode:\r
7929     nowChecked = IDM_AnalysisMode;\r
7930     break;\r
7931   case AnalyzeFile:\r
7932     nowChecked = IDM_AnalyzeFile;\r
7933     break;\r
7934   case EditGame:\r
7935     nowChecked = IDM_EditGame;\r
7936     break;\r
7937   case PlayFromGameFile:\r
7938     nowChecked = IDM_LoadGame;\r
7939     break;\r
7940   case EditPosition:\r
7941     nowChecked = IDM_EditPosition;\r
7942     break;\r
7943   case Training:\r
7944     nowChecked = IDM_Training;\r
7945     break;\r
7946   case IcsPlayingWhite:\r
7947   case IcsPlayingBlack:\r
7948   case IcsObserving:\r
7949   case IcsIdle:\r
7950     nowChecked = IDM_IcsClient;\r
7951     break;\r
7952   default:\r
7953   case EndOfGame:\r
7954     nowChecked = 0;\r
7955     break;\r
7956   }\r
7957   if (prevChecked != 0)\r
7958     (void) CheckMenuItem(GetMenu(hwndMain),\r
7959                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7960   if (nowChecked != 0)\r
7961     (void) CheckMenuItem(GetMenu(hwndMain),\r
7962                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
7963 \r
7964   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7965     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7966                           MF_BYCOMMAND|MF_ENABLED);\r
7967   } else {\r
7968     (void) EnableMenuItem(GetMenu(hwndMain), \r
7969                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7970   }\r
7971 \r
7972   prevChecked = nowChecked;\r
7973 \r
7974   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
7975   if (appData.icsActive) {\r
7976        if (appData.icsEngineAnalyze) {\r
7977                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7978                        MF_BYCOMMAND|MF_CHECKED);\r
7979        } else {\r
7980                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7981                        MF_BYCOMMAND|MF_UNCHECKED);\r
7982        }\r
7983   }\r
7984 }\r
7985 \r
7986 VOID\r
7987 SetICSMode()\r
7988 {\r
7989   HMENU hmenu = GetMenu(hwndMain);\r
7990   SetMenuEnables(hmenu, icsEnables);\r
7991   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
7992     MF_BYPOSITION|MF_ENABLED);\r
7993 #if ZIPPY\r
7994   if (appData.zippyPlay) {\r
7995     SetMenuEnables(hmenu, zippyEnables);\r
7996     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
7997          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7998           MF_BYCOMMAND|MF_ENABLED);\r
7999   }\r
8000 #endif\r
8001 }\r
8002 \r
8003 VOID\r
8004 SetGNUMode()\r
8005 {\r
8006   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8007 }\r
8008 \r
8009 VOID\r
8010 SetNCPMode()\r
8011 {\r
8012   HMENU hmenu = GetMenu(hwndMain);\r
8013   SetMenuEnables(hmenu, ncpEnables);\r
8014   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8015     MF_BYPOSITION|MF_GRAYED);\r
8016     DrawMenuBar(hwndMain);\r
8017 }\r
8018 \r
8019 VOID\r
8020 SetCmailMode()\r
8021 {\r
8022   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8023 }\r
8024 \r
8025 VOID \r
8026 SetTrainingModeOn()\r
8027 {\r
8028   int i;\r
8029   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8030   for (i = 0; i < N_BUTTONS; i++) {\r
8031     if (buttonDesc[i].hwnd != NULL)\r
8032       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8033   }\r
8034   CommentPopDown();\r
8035 }\r
8036 \r
8037 VOID SetTrainingModeOff()\r
8038 {\r
8039   int i;\r
8040   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8041   for (i = 0; i < N_BUTTONS; i++) {\r
8042     if (buttonDesc[i].hwnd != NULL)\r
8043       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8044   }\r
8045 }\r
8046 \r
8047 \r
8048 VOID\r
8049 SetUserThinkingEnables()\r
8050 {\r
8051   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8052 }\r
8053 \r
8054 VOID\r
8055 SetMachineThinkingEnables()\r
8056 {\r
8057   HMENU hMenu = GetMenu(hwndMain);\r
8058   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8059 \r
8060   SetMenuEnables(hMenu, machineThinkingEnables);\r
8061 \r
8062   if (gameMode == MachinePlaysBlack) {\r
8063     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8064   } else if (gameMode == MachinePlaysWhite) {\r
8065     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8066   } else if (gameMode == TwoMachinesPlay) {\r
8067     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8068   }\r
8069 }\r
8070 \r
8071 \r
8072 VOID\r
8073 DisplayTitle(char *str)\r
8074 {\r
8075   char title[MSG_SIZ], *host;\r
8076   if (str[0] != NULLCHAR) {\r
8077     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8078   } else if (appData.icsActive) {\r
8079     if (appData.icsCommPort[0] != NULLCHAR)\r
8080       host = "ICS";\r
8081     else \r
8082       host = appData.icsHost;\r
8083       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8084   } else if (appData.noChessProgram) {\r
8085     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8086   } else {\r
8087     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8088     strcat(title, ": ");\r
8089     strcat(title, first.tidy);\r
8090   }\r
8091   SetWindowText(hwndMain, title);\r
8092 }\r
8093 \r
8094 \r
8095 VOID\r
8096 DisplayMessage(char *str1, char *str2)\r
8097 {\r
8098   HDC hdc;\r
8099   HFONT oldFont;\r
8100   int remain = MESSAGE_TEXT_MAX - 1;\r
8101   int len;\r
8102 \r
8103   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8104   messageText[0] = NULLCHAR;\r
8105   if (*str1) {\r
8106     len = strlen(str1);\r
8107     if (len > remain) len = remain;\r
8108     strncpy(messageText, str1, len);\r
8109     messageText[len] = NULLCHAR;\r
8110     remain -= len;\r
8111   }\r
8112   if (*str2 && remain >= 2) {\r
8113     if (*str1) {\r
8114       strcat(messageText, "  ");\r
8115       remain -= 2;\r
8116     }\r
8117     len = strlen(str2);\r
8118     if (len > remain) len = remain;\r
8119     strncat(messageText, str2, len);\r
8120   }\r
8121   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8122 \r
8123   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8124 \r
8125   SAYMACHINEMOVE();\r
8126 \r
8127   hdc = GetDC(hwndMain);\r
8128   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8129   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8130              &messageRect, messageText, strlen(messageText), NULL);\r
8131   (void) SelectObject(hdc, oldFont);\r
8132   (void) ReleaseDC(hwndMain, hdc);\r
8133 }\r
8134 \r
8135 VOID\r
8136 DisplayError(char *str, int error)\r
8137 {\r
8138   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8139   int len;\r
8140 \r
8141   if (error == 0) {\r
8142     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8143   } else {\r
8144     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8145                         NULL, error, LANG_NEUTRAL,\r
8146                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8147     if (len > 0) {\r
8148       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8149     } else {\r
8150       ErrorMap *em = errmap;\r
8151       while (em->err != 0 && em->err != error) em++;\r
8152       if (em->err != 0) {\r
8153         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8154       } else {\r
8155         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8156       }\r
8157     }\r
8158   }\r
8159   \r
8160   ErrorPopUp(_("Error"), buf);\r
8161 }\r
8162 \r
8163 \r
8164 VOID\r
8165 DisplayMoveError(char *str)\r
8166 {\r
8167   fromX = fromY = -1;\r
8168   ClearHighlights();\r
8169   DrawPosition(FALSE, NULL);\r
8170   if (appData.popupMoveErrors) {\r
8171     ErrorPopUp(_("Error"), str);\r
8172   } else {\r
8173     DisplayMessage(str, "");\r
8174     moveErrorMessageUp = TRUE;\r
8175   }\r
8176 }\r
8177 \r
8178 VOID\r
8179 DisplayFatalError(char *str, int error, int exitStatus)\r
8180 {\r
8181   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8182   int len;\r
8183   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8184 \r
8185   if (error != 0) {\r
8186     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8187                         NULL, error, LANG_NEUTRAL,\r
8188                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8189     if (len > 0) {\r
8190       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8191     } else {\r
8192       ErrorMap *em = errmap;\r
8193       while (em->err != 0 && em->err != error) em++;\r
8194       if (em->err != 0) {\r
8195         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8196       } else {\r
8197         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8198       }\r
8199     }\r
8200     str = buf;\r
8201   }\r
8202   if (appData.debugMode) {\r
8203     fprintf(debugFP, "%s: %s\n", label, str);\r
8204   }\r
8205   if (appData.popupExitMessage) {\r
8206     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8207                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8208   }\r
8209   ExitEvent(exitStatus);\r
8210 }\r
8211 \r
8212 \r
8213 VOID\r
8214 DisplayInformation(char *str)\r
8215 {\r
8216   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8217 }\r
8218 \r
8219 \r
8220 VOID\r
8221 DisplayNote(char *str)\r
8222 {\r
8223   ErrorPopUp(_("Note"), str);\r
8224 }\r
8225 \r
8226 \r
8227 typedef struct {\r
8228   char *title, *question, *replyPrefix;\r
8229   ProcRef pr;\r
8230 } QuestionParams;\r
8231 \r
8232 LRESULT CALLBACK\r
8233 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8234 {\r
8235   static QuestionParams *qp;\r
8236   char reply[MSG_SIZ];\r
8237   int len, err;\r
8238 \r
8239   switch (message) {\r
8240   case WM_INITDIALOG:\r
8241     qp = (QuestionParams *) lParam;\r
8242     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8243     Translate(hDlg, DLG_Question);\r
8244     SetWindowText(hDlg, qp->title);\r
8245     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8246     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8247     return FALSE;\r
8248 \r
8249   case WM_COMMAND:\r
8250     switch (LOWORD(wParam)) {\r
8251     case IDOK:\r
8252       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8253       if (*reply) strcat(reply, " ");\r
8254       len = strlen(reply);\r
8255       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8256       strcat(reply, "\n");\r
8257       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8258       EndDialog(hDlg, TRUE);\r
8259       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8260       return TRUE;\r
8261     case IDCANCEL:\r
8262       EndDialog(hDlg, FALSE);\r
8263       return TRUE;\r
8264     default:\r
8265       break;\r
8266     }\r
8267     break;\r
8268   }\r
8269   return FALSE;\r
8270 }\r
8271 \r
8272 VOID\r
8273 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8274 {\r
8275     QuestionParams qp;\r
8276     FARPROC lpProc;\r
8277     \r
8278     qp.title = title;\r
8279     qp.question = question;\r
8280     qp.replyPrefix = replyPrefix;\r
8281     qp.pr = pr;\r
8282     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8283     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8284       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8285     FreeProcInstance(lpProc);\r
8286 }\r
8287 \r
8288 /* [AS] Pick FRC position */\r
8289 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8290 {\r
8291     static int * lpIndexFRC;\r
8292     BOOL index_is_ok;\r
8293     char buf[16];\r
8294 \r
8295     switch( message )\r
8296     {\r
8297     case WM_INITDIALOG:\r
8298         lpIndexFRC = (int *) lParam;\r
8299 \r
8300         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8301         Translate(hDlg, DLG_NewGameFRC);\r
8302 \r
8303         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8304         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8305         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8306         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8307 \r
8308         break;\r
8309 \r
8310     case WM_COMMAND:\r
8311         switch( LOWORD(wParam) ) {\r
8312         case IDOK:\r
8313             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8314             EndDialog( hDlg, 0 );\r
8315             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8316             return TRUE;\r
8317         case IDCANCEL:\r
8318             EndDialog( hDlg, 1 );   \r
8319             return TRUE;\r
8320         case IDC_NFG_Edit:\r
8321             if( HIWORD(wParam) == EN_CHANGE ) {\r
8322                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8323 \r
8324                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8325             }\r
8326             return TRUE;\r
8327         case IDC_NFG_Random:\r
8328           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8329             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8330             return TRUE;\r
8331         }\r
8332 \r
8333         break;\r
8334     }\r
8335 \r
8336     return FALSE;\r
8337 }\r
8338 \r
8339 int NewGameFRC()\r
8340 {\r
8341     int result;\r
8342     int index = appData.defaultFrcPosition;\r
8343     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8344 \r
8345     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8346 \r
8347     if( result == 0 ) {\r
8348         appData.defaultFrcPosition = index;\r
8349     }\r
8350 \r
8351     return result;\r
8352 }\r
8353 \r
8354 /* [AS] Game list options. Refactored by HGM */\r
8355 \r
8356 HWND gameListOptionsDialog;\r
8357 \r
8358 // low-level front-end: clear text edit / list widget\r
8359 void\r
8360 GLT_ClearList()\r
8361 {\r
8362     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8363 }\r
8364 \r
8365 // low-level front-end: clear text edit / list widget\r
8366 void\r
8367 GLT_DeSelectList()\r
8368 {\r
8369     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8370 }\r
8371 \r
8372 // low-level front-end: append line to text edit / list widget\r
8373 void\r
8374 GLT_AddToList( char *name )\r
8375 {\r
8376     if( name != 0 ) {\r
8377             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8378     }\r
8379 }\r
8380 \r
8381 // low-level front-end: get line from text edit / list widget\r
8382 Boolean\r
8383 GLT_GetFromList( int index, char *name )\r
8384 {\r
8385     if( name != 0 ) {\r
8386             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8387                 return TRUE;\r
8388     }\r
8389     return FALSE;\r
8390 }\r
8391 \r
8392 void GLT_MoveSelection( HWND hDlg, int delta )\r
8393 {\r
8394     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8395     int idx2 = idx1 + delta;\r
8396     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8397 \r
8398     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8399         char buf[128];\r
8400 \r
8401         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8402         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8403         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8404         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8405     }\r
8406 }\r
8407 \r
8408 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8409 {\r
8410     switch( message )\r
8411     {\r
8412     case WM_INITDIALOG:\r
8413         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8414         \r
8415         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8416         Translate(hDlg, DLG_GameListOptions);\r
8417 \r
8418         /* Initialize list */\r
8419         GLT_TagsToList( lpUserGLT );\r
8420 \r
8421         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8422 \r
8423         break;\r
8424 \r
8425     case WM_COMMAND:\r
8426         switch( LOWORD(wParam) ) {\r
8427         case IDOK:\r
8428             GLT_ParseList();\r
8429             EndDialog( hDlg, 0 );\r
8430             return TRUE;\r
8431         case IDCANCEL:\r
8432             EndDialog( hDlg, 1 );\r
8433             return TRUE;\r
8434 \r
8435         case IDC_GLT_Default:\r
8436             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8437             return TRUE;\r
8438 \r
8439         case IDC_GLT_Restore:\r
8440             GLT_TagsToList( appData.gameListTags );\r
8441             return TRUE;\r
8442 \r
8443         case IDC_GLT_Up:\r
8444             GLT_MoveSelection( hDlg, -1 );\r
8445             return TRUE;\r
8446 \r
8447         case IDC_GLT_Down:\r
8448             GLT_MoveSelection( hDlg, +1 );\r
8449             return TRUE;\r
8450         }\r
8451 \r
8452         break;\r
8453     }\r
8454 \r
8455     return FALSE;\r
8456 }\r
8457 \r
8458 int GameListOptions()\r
8459 {\r
8460     int result;\r
8461     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8462 \r
8463       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8464 \r
8465     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8466 \r
8467     if( result == 0 ) {\r
8468         /* [AS] Memory leak here! */\r
8469         appData.gameListTags = strdup( lpUserGLT ); \r
8470     }\r
8471 \r
8472     return result;\r
8473 }\r
8474 \r
8475 VOID\r
8476 DisplayIcsInteractionTitle(char *str)\r
8477 {\r
8478   char consoleTitle[MSG_SIZ];\r
8479 \r
8480     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8481   SetWindowText(hwndConsole, consoleTitle);\r
8482 }\r
8483 \r
8484 void\r
8485 DrawPosition(int fullRedraw, Board board)\r
8486 {\r
8487   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8488 }\r
8489 \r
8490 void NotifyFrontendLogin()\r
8491 {\r
8492         if (hwndConsole)\r
8493                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8494 }\r
8495 \r
8496 VOID\r
8497 ResetFrontEnd()\r
8498 {\r
8499   fromX = fromY = -1;\r
8500   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8501     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8502     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8503     dragInfo.lastpos = dragInfo.pos;\r
8504     dragInfo.start.x = dragInfo.start.y = -1;\r
8505     dragInfo.from = dragInfo.start;\r
8506     ReleaseCapture();\r
8507     DrawPosition(TRUE, NULL);\r
8508   }\r
8509   TagsPopDown();\r
8510 }\r
8511 \r
8512 \r
8513 VOID\r
8514 CommentPopUp(char *title, char *str)\r
8515 {\r
8516   HWND hwnd = GetActiveWindow();\r
8517   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8518   SAY(str);\r
8519   SetActiveWindow(hwnd);\r
8520 }\r
8521 \r
8522 VOID\r
8523 CommentPopDown(void)\r
8524 {\r
8525   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8526   if (commentDialog) {\r
8527     ShowWindow(commentDialog, SW_HIDE);\r
8528   }\r
8529   commentUp = FALSE;\r
8530 }\r
8531 \r
8532 VOID\r
8533 EditCommentPopUp(int index, char *title, char *str)\r
8534 {\r
8535   EitherCommentPopUp(index, title, str, TRUE);\r
8536 }\r
8537 \r
8538 \r
8539 VOID\r
8540 RingBell()\r
8541 {\r
8542   MyPlaySound(&sounds[(int)SoundMove]);\r
8543 }\r
8544 \r
8545 VOID PlayIcsWinSound()\r
8546 {\r
8547   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8548 }\r
8549 \r
8550 VOID PlayIcsLossSound()\r
8551 {\r
8552   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8553 }\r
8554 \r
8555 VOID PlayIcsDrawSound()\r
8556 {\r
8557   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8558 }\r
8559 \r
8560 VOID PlayIcsUnfinishedSound()\r
8561 {\r
8562   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8563 }\r
8564 \r
8565 VOID\r
8566 PlayAlarmSound()\r
8567 {\r
8568   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8569 }\r
8570 \r
8571 \r
8572 VOID\r
8573 EchoOn()\r
8574 {\r
8575   HWND hInput;\r
8576   consoleEcho = TRUE;\r
8577   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8578   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8579   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8580 }\r
8581 \r
8582 \r
8583 VOID\r
8584 EchoOff()\r
8585 {\r
8586   CHARFORMAT cf;\r
8587   HWND hInput;\r
8588   consoleEcho = FALSE;\r
8589   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8590   /* This works OK: set text and background both to the same color */\r
8591   cf = consoleCF;\r
8592   cf.crTextColor = COLOR_ECHOOFF;\r
8593   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8594   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8595 }\r
8596 \r
8597 /* No Raw()...? */\r
8598 \r
8599 void Colorize(ColorClass cc, int continuation)\r
8600 {\r
8601   currentColorClass = cc;\r
8602   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8603   consoleCF.crTextColor = textAttribs[cc].color;\r
8604   consoleCF.dwEffects = textAttribs[cc].effects;\r
8605   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8606 }\r
8607 \r
8608 char *\r
8609 UserName()\r
8610 {\r
8611   static char buf[MSG_SIZ];\r
8612   DWORD bufsiz = MSG_SIZ;\r
8613 \r
8614   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8615         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8616   }\r
8617   if (!GetUserName(buf, &bufsiz)) {\r
8618     /*DisplayError("Error getting user name", GetLastError());*/\r
8619     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8620   }\r
8621   return buf;\r
8622 }\r
8623 \r
8624 char *\r
8625 HostName()\r
8626 {\r
8627   static char buf[MSG_SIZ];\r
8628   DWORD bufsiz = MSG_SIZ;\r
8629 \r
8630   if (!GetComputerName(buf, &bufsiz)) {\r
8631     /*DisplayError("Error getting host name", GetLastError());*/\r
8632     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8633   }\r
8634   return buf;\r
8635 }\r
8636 \r
8637 \r
8638 int\r
8639 ClockTimerRunning()\r
8640 {\r
8641   return clockTimerEvent != 0;\r
8642 }\r
8643 \r
8644 int\r
8645 StopClockTimer()\r
8646 {\r
8647   if (clockTimerEvent == 0) return FALSE;\r
8648   KillTimer(hwndMain, clockTimerEvent);\r
8649   clockTimerEvent = 0;\r
8650   return TRUE;\r
8651 }\r
8652 \r
8653 void\r
8654 StartClockTimer(long millisec)\r
8655 {\r
8656   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8657                              (UINT) millisec, NULL);\r
8658 }\r
8659 \r
8660 void\r
8661 DisplayWhiteClock(long timeRemaining, int highlight)\r
8662 {\r
8663   HDC hdc;\r
8664   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8665 \r
8666   if(appData.noGUI) return;\r
8667   hdc = GetDC(hwndMain);\r
8668   if (!IsIconic(hwndMain)) {\r
8669     DisplayAClock(hdc, timeRemaining, highlight, \r
8670                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8671   }\r
8672   if (highlight && iconCurrent == iconBlack) {\r
8673     iconCurrent = iconWhite;\r
8674     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8675     if (IsIconic(hwndMain)) {\r
8676       DrawIcon(hdc, 2, 2, iconCurrent);\r
8677     }\r
8678   }\r
8679   (void) ReleaseDC(hwndMain, hdc);\r
8680   if (hwndConsole)\r
8681     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8682 }\r
8683 \r
8684 void\r
8685 DisplayBlackClock(long timeRemaining, int highlight)\r
8686 {\r
8687   HDC hdc;\r
8688   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8689 \r
8690   if(appData.noGUI) return;\r
8691   hdc = GetDC(hwndMain);\r
8692   if (!IsIconic(hwndMain)) {\r
8693     DisplayAClock(hdc, timeRemaining, highlight, \r
8694                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8695   }\r
8696   if (highlight && iconCurrent == iconWhite) {\r
8697     iconCurrent = iconBlack;\r
8698     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8699     if (IsIconic(hwndMain)) {\r
8700       DrawIcon(hdc, 2, 2, iconCurrent);\r
8701     }\r
8702   }\r
8703   (void) ReleaseDC(hwndMain, hdc);\r
8704   if (hwndConsole)\r
8705     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8706 }\r
8707 \r
8708 \r
8709 int\r
8710 LoadGameTimerRunning()\r
8711 {\r
8712   return loadGameTimerEvent != 0;\r
8713 }\r
8714 \r
8715 int\r
8716 StopLoadGameTimer()\r
8717 {\r
8718   if (loadGameTimerEvent == 0) return FALSE;\r
8719   KillTimer(hwndMain, loadGameTimerEvent);\r
8720   loadGameTimerEvent = 0;\r
8721   return TRUE;\r
8722 }\r
8723 \r
8724 void\r
8725 StartLoadGameTimer(long millisec)\r
8726 {\r
8727   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8728                                 (UINT) millisec, NULL);\r
8729 }\r
8730 \r
8731 void\r
8732 AutoSaveGame()\r
8733 {\r
8734   char *defName;\r
8735   FILE *f;\r
8736   char fileTitle[MSG_SIZ];\r
8737 \r
8738   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8739   f = OpenFileDialog(hwndMain, "a", defName,\r
8740                      appData.oldSaveStyle ? "gam" : "pgn",\r
8741                      GAME_FILT, \r
8742                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8743   if (f != NULL) {\r
8744     SaveGame(f, 0, "");\r
8745     fclose(f);\r
8746   }\r
8747 }\r
8748 \r
8749 \r
8750 void\r
8751 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8752 {\r
8753   if (delayedTimerEvent != 0) {\r
8754     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8755       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8756     }\r
8757     KillTimer(hwndMain, delayedTimerEvent);\r
8758     delayedTimerEvent = 0;\r
8759     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8760     delayedTimerCallback();\r
8761   }\r
8762   delayedTimerCallback = cb;\r
8763   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8764                                 (UINT) millisec, NULL);\r
8765 }\r
8766 \r
8767 DelayedEventCallback\r
8768 GetDelayedEvent()\r
8769 {\r
8770   if (delayedTimerEvent) {\r
8771     return delayedTimerCallback;\r
8772   } else {\r
8773     return NULL;\r
8774   }\r
8775 }\r
8776 \r
8777 void\r
8778 CancelDelayedEvent()\r
8779 {\r
8780   if (delayedTimerEvent) {\r
8781     KillTimer(hwndMain, delayedTimerEvent);\r
8782     delayedTimerEvent = 0;\r
8783   }\r
8784 }\r
8785 \r
8786 DWORD GetWin32Priority(int nice)\r
8787 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8788 /*\r
8789 REALTIME_PRIORITY_CLASS     0x00000100\r
8790 HIGH_PRIORITY_CLASS         0x00000080\r
8791 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8792 NORMAL_PRIORITY_CLASS       0x00000020\r
8793 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8794 IDLE_PRIORITY_CLASS         0x00000040\r
8795 */\r
8796         if (nice < -15) return 0x00000080;\r
8797         if (nice < 0)   return 0x00008000;\r
8798         if (nice == 0)  return 0x00000020;\r
8799         if (nice < 15)  return 0x00004000;\r
8800         return 0x00000040;\r
8801 }\r
8802 \r
8803 /* Start a child process running the given program.\r
8804    The process's standard output can be read from "from", and its\r
8805    standard input can be written to "to".\r
8806    Exit with fatal error if anything goes wrong.\r
8807    Returns an opaque pointer that can be used to destroy the process\r
8808    later.\r
8809 */\r
8810 int\r
8811 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8812 {\r
8813 #define BUFSIZE 4096\r
8814 \r
8815   HANDLE hChildStdinRd, hChildStdinWr,\r
8816     hChildStdoutRd, hChildStdoutWr;\r
8817   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8818   SECURITY_ATTRIBUTES saAttr;\r
8819   BOOL fSuccess;\r
8820   PROCESS_INFORMATION piProcInfo;\r
8821   STARTUPINFO siStartInfo;\r
8822   ChildProc *cp;\r
8823   char buf[MSG_SIZ];\r
8824   DWORD err;\r
8825 \r
8826   if (appData.debugMode) {\r
8827     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8828   }\r
8829 \r
8830   *pr = NoProc;\r
8831 \r
8832   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8833   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8834   saAttr.bInheritHandle = TRUE;\r
8835   saAttr.lpSecurityDescriptor = NULL;\r
8836 \r
8837   /*\r
8838    * The steps for redirecting child's STDOUT:\r
8839    *     1. Create anonymous pipe to be STDOUT for child.\r
8840    *     2. Create a noninheritable duplicate of read handle,\r
8841    *         and close the inheritable read handle.\r
8842    */\r
8843 \r
8844   /* Create a pipe for the child's STDOUT. */\r
8845   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8846     return GetLastError();\r
8847   }\r
8848 \r
8849   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8850   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8851                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8852                              FALSE,     /* not inherited */\r
8853                              DUPLICATE_SAME_ACCESS);\r
8854   if (! fSuccess) {\r
8855     return GetLastError();\r
8856   }\r
8857   CloseHandle(hChildStdoutRd);\r
8858 \r
8859   /*\r
8860    * The steps for redirecting child's STDIN:\r
8861    *     1. Create anonymous pipe to be STDIN for child.\r
8862    *     2. Create a noninheritable duplicate of write handle,\r
8863    *         and close the inheritable write handle.\r
8864    */\r
8865 \r
8866   /* Create a pipe for the child's STDIN. */\r
8867   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8868     return GetLastError();\r
8869   }\r
8870 \r
8871   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8872   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8873                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8874                              FALSE,     /* not inherited */\r
8875                              DUPLICATE_SAME_ACCESS);\r
8876   if (! fSuccess) {\r
8877     return GetLastError();\r
8878   }\r
8879   CloseHandle(hChildStdinWr);\r
8880 \r
8881   /* Arrange to (1) look in dir for the child .exe file, and\r
8882    * (2) have dir be the child's working directory.  Interpret\r
8883    * dir relative to the directory WinBoard loaded from. */\r
8884   GetCurrentDirectory(MSG_SIZ, buf);\r
8885   SetCurrentDirectory(installDir);\r
8886   SetCurrentDirectory(dir);\r
8887 \r
8888   /* Now create the child process. */\r
8889 \r
8890   siStartInfo.cb = sizeof(STARTUPINFO);\r
8891   siStartInfo.lpReserved = NULL;\r
8892   siStartInfo.lpDesktop = NULL;\r
8893   siStartInfo.lpTitle = NULL;\r
8894   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8895   siStartInfo.cbReserved2 = 0;\r
8896   siStartInfo.lpReserved2 = NULL;\r
8897   siStartInfo.hStdInput = hChildStdinRd;\r
8898   siStartInfo.hStdOutput = hChildStdoutWr;\r
8899   siStartInfo.hStdError = hChildStdoutWr;\r
8900 \r
8901   fSuccess = CreateProcess(NULL,\r
8902                            cmdLine,        /* command line */\r
8903                            NULL,           /* process security attributes */\r
8904                            NULL,           /* primary thread security attrs */\r
8905                            TRUE,           /* handles are inherited */\r
8906                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8907                            NULL,           /* use parent's environment */\r
8908                            NULL,\r
8909                            &siStartInfo, /* STARTUPINFO pointer */\r
8910                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8911 \r
8912   err = GetLastError();\r
8913   SetCurrentDirectory(buf); /* return to prev directory */\r
8914   if (! fSuccess) {\r
8915     return err;\r
8916   }\r
8917 \r
8918   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8919     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8920     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8921   }\r
8922 \r
8923   /* Close the handles we don't need in the parent */\r
8924   CloseHandle(piProcInfo.hThread);\r
8925   CloseHandle(hChildStdinRd);\r
8926   CloseHandle(hChildStdoutWr);\r
8927 \r
8928   /* Prepare return value */\r
8929   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8930   cp->kind = CPReal;\r
8931   cp->hProcess = piProcInfo.hProcess;\r
8932   cp->pid = piProcInfo.dwProcessId;\r
8933   cp->hFrom = hChildStdoutRdDup;\r
8934   cp->hTo = hChildStdinWrDup;\r
8935 \r
8936   *pr = (void *) cp;\r
8937 \r
8938   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8939      2000 where engines sometimes don't see the initial command(s)\r
8940      from WinBoard and hang.  I don't understand how that can happen,\r
8941      but the Sleep is harmless, so I've put it in.  Others have also\r
8942      reported what may be the same problem, so hopefully this will fix\r
8943      it for them too.  */\r
8944   Sleep(500);\r
8945 \r
8946   return NO_ERROR;\r
8947 }\r
8948 \r
8949 \r
8950 void\r
8951 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8952 {\r
8953   ChildProc *cp; int result;\r
8954 \r
8955   cp = (ChildProc *) pr;\r
8956   if (cp == NULL) return;\r
8957 \r
8958   switch (cp->kind) {\r
8959   case CPReal:\r
8960     /* TerminateProcess is considered harmful, so... */\r
8961     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8962     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8963     /* The following doesn't work because the chess program\r
8964        doesn't "have the same console" as WinBoard.  Maybe\r
8965        we could arrange for this even though neither WinBoard\r
8966        nor the chess program uses a console for stdio? */\r
8967     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8968 \r
8969     /* [AS] Special termination modes for misbehaving programs... */\r
8970     if( signal == 9 ) { \r
8971         result = TerminateProcess( cp->hProcess, 0 );\r
8972 \r
8973         if ( appData.debugMode) {\r
8974             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
8975         }\r
8976     }\r
8977     else if( signal == 10 ) {\r
8978         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
8979 \r
8980         if( dw != WAIT_OBJECT_0 ) {\r
8981             result = TerminateProcess( cp->hProcess, 0 );\r
8982 \r
8983             if ( appData.debugMode) {\r
8984                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
8985             }\r
8986 \r
8987         }\r
8988     }\r
8989 \r
8990     CloseHandle(cp->hProcess);\r
8991     break;\r
8992 \r
8993   case CPComm:\r
8994     if (cp->hFrom) CloseHandle(cp->hFrom);\r
8995     break;\r
8996 \r
8997   case CPSock:\r
8998     closesocket(cp->sock);\r
8999     WSACleanup();\r
9000     break;\r
9001 \r
9002   case CPRcmd:\r
9003     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9004     closesocket(cp->sock);\r
9005     closesocket(cp->sock2);\r
9006     WSACleanup();\r
9007     break;\r
9008   }\r
9009   free(cp);\r
9010 }\r
9011 \r
9012 void\r
9013 InterruptChildProcess(ProcRef pr)\r
9014 {\r
9015   ChildProc *cp;\r
9016 \r
9017   cp = (ChildProc *) pr;\r
9018   if (cp == NULL) return;\r
9019   switch (cp->kind) {\r
9020   case CPReal:\r
9021     /* The following doesn't work because the chess program\r
9022        doesn't "have the same console" as WinBoard.  Maybe\r
9023        we could arrange for this even though neither WinBoard\r
9024        nor the chess program uses a console for stdio */\r
9025     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9026     break;\r
9027 \r
9028   case CPComm:\r
9029   case CPSock:\r
9030     /* Can't interrupt */\r
9031     break;\r
9032 \r
9033   case CPRcmd:\r
9034     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9035     break;\r
9036   }\r
9037 }\r
9038 \r
9039 \r
9040 int\r
9041 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9042 {\r
9043   char cmdLine[MSG_SIZ];\r
9044 \r
9045   if (port[0] == NULLCHAR) {\r
9046     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9047   } else {\r
9048     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9049   }\r
9050   return StartChildProcess(cmdLine, "", pr);\r
9051 }\r
9052 \r
9053 \r
9054 /* Code to open TCP sockets */\r
9055 \r
9056 int\r
9057 OpenTCP(char *host, char *port, ProcRef *pr)\r
9058 {\r
9059   ChildProc *cp;\r
9060   int err;\r
9061   SOCKET s;\r
9062   struct sockaddr_in sa, mysa;\r
9063   struct hostent FAR *hp;\r
9064   unsigned short uport;\r
9065   WORD wVersionRequested;\r
9066   WSADATA wsaData;\r
9067 \r
9068   /* Initialize socket DLL */\r
9069   wVersionRequested = MAKEWORD(1, 1);\r
9070   err = WSAStartup(wVersionRequested, &wsaData);\r
9071   if (err != 0) return err;\r
9072 \r
9073   /* Make socket */\r
9074   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9075     err = WSAGetLastError();\r
9076     WSACleanup();\r
9077     return err;\r
9078   }\r
9079 \r
9080   /* Bind local address using (mostly) don't-care values.\r
9081    */\r
9082   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9083   mysa.sin_family = AF_INET;\r
9084   mysa.sin_addr.s_addr = INADDR_ANY;\r
9085   uport = (unsigned short) 0;\r
9086   mysa.sin_port = htons(uport);\r
9087   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9088       == SOCKET_ERROR) {\r
9089     err = WSAGetLastError();\r
9090     WSACleanup();\r
9091     return err;\r
9092   }\r
9093 \r
9094   /* Resolve remote host name */\r
9095   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9096   if (!(hp = gethostbyname(host))) {\r
9097     unsigned int b0, b1, b2, b3;\r
9098 \r
9099     err = WSAGetLastError();\r
9100 \r
9101     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9102       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9103       hp->h_addrtype = AF_INET;\r
9104       hp->h_length = 4;\r
9105       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9106       hp->h_addr_list[0] = (char *) malloc(4);\r
9107       hp->h_addr_list[0][0] = (char) b0;\r
9108       hp->h_addr_list[0][1] = (char) b1;\r
9109       hp->h_addr_list[0][2] = (char) b2;\r
9110       hp->h_addr_list[0][3] = (char) b3;\r
9111     } else {\r
9112       WSACleanup();\r
9113       return err;\r
9114     }\r
9115   }\r
9116   sa.sin_family = hp->h_addrtype;\r
9117   uport = (unsigned short) atoi(port);\r
9118   sa.sin_port = htons(uport);\r
9119   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9120 \r
9121   /* Make connection */\r
9122   if (connect(s, (struct sockaddr *) &sa,\r
9123               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9124     err = WSAGetLastError();\r
9125     WSACleanup();\r
9126     return err;\r
9127   }\r
9128 \r
9129   /* Prepare return value */\r
9130   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9131   cp->kind = CPSock;\r
9132   cp->sock = s;\r
9133   *pr = (ProcRef *) cp;\r
9134 \r
9135   return NO_ERROR;\r
9136 }\r
9137 \r
9138 int\r
9139 OpenCommPort(char *name, ProcRef *pr)\r
9140 {\r
9141   HANDLE h;\r
9142   COMMTIMEOUTS ct;\r
9143   ChildProc *cp;\r
9144   char fullname[MSG_SIZ];\r
9145 \r
9146   if (*name != '\\')\r
9147     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9148   else\r
9149     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9150 \r
9151   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9152                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9153   if (h == (HANDLE) -1) {\r
9154     return GetLastError();\r
9155   }\r
9156   hCommPort = h;\r
9157 \r
9158   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9159 \r
9160   /* Accumulate characters until a 100ms pause, then parse */\r
9161   ct.ReadIntervalTimeout = 100;\r
9162   ct.ReadTotalTimeoutMultiplier = 0;\r
9163   ct.ReadTotalTimeoutConstant = 0;\r
9164   ct.WriteTotalTimeoutMultiplier = 0;\r
9165   ct.WriteTotalTimeoutConstant = 0;\r
9166   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9167 \r
9168   /* Prepare return value */\r
9169   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9170   cp->kind = CPComm;\r
9171   cp->hFrom = h;\r
9172   cp->hTo = h;\r
9173   *pr = (ProcRef *) cp;\r
9174 \r
9175   return NO_ERROR;\r
9176 }\r
9177 \r
9178 int\r
9179 OpenLoopback(ProcRef *pr)\r
9180 {\r
9181   DisplayFatalError(_("Not implemented"), 0, 1);\r
9182   return NO_ERROR;\r
9183 }\r
9184 \r
9185 \r
9186 int\r
9187 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9188 {\r
9189   ChildProc *cp;\r
9190   int err;\r
9191   SOCKET s, s2, s3;\r
9192   struct sockaddr_in sa, mysa;\r
9193   struct hostent FAR *hp;\r
9194   unsigned short uport;\r
9195   WORD wVersionRequested;\r
9196   WSADATA wsaData;\r
9197   int fromPort;\r
9198   char stderrPortStr[MSG_SIZ];\r
9199 \r
9200   /* Initialize socket DLL */\r
9201   wVersionRequested = MAKEWORD(1, 1);\r
9202   err = WSAStartup(wVersionRequested, &wsaData);\r
9203   if (err != 0) return err;\r
9204 \r
9205   /* Resolve remote host name */\r
9206   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9207   if (!(hp = gethostbyname(host))) {\r
9208     unsigned int b0, b1, b2, b3;\r
9209 \r
9210     err = WSAGetLastError();\r
9211 \r
9212     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9213       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9214       hp->h_addrtype = AF_INET;\r
9215       hp->h_length = 4;\r
9216       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9217       hp->h_addr_list[0] = (char *) malloc(4);\r
9218       hp->h_addr_list[0][0] = (char) b0;\r
9219       hp->h_addr_list[0][1] = (char) b1;\r
9220       hp->h_addr_list[0][2] = (char) b2;\r
9221       hp->h_addr_list[0][3] = (char) b3;\r
9222     } else {\r
9223       WSACleanup();\r
9224       return err;\r
9225     }\r
9226   }\r
9227   sa.sin_family = hp->h_addrtype;\r
9228   uport = (unsigned short) 514;\r
9229   sa.sin_port = htons(uport);\r
9230   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9231 \r
9232   /* Bind local socket to unused "privileged" port address\r
9233    */\r
9234   s = INVALID_SOCKET;\r
9235   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9236   mysa.sin_family = AF_INET;\r
9237   mysa.sin_addr.s_addr = INADDR_ANY;\r
9238   for (fromPort = 1023;; fromPort--) {\r
9239     if (fromPort < 0) {\r
9240       WSACleanup();\r
9241       return WSAEADDRINUSE;\r
9242     }\r
9243     if (s == INVALID_SOCKET) {\r
9244       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9245         err = WSAGetLastError();\r
9246         WSACleanup();\r
9247         return err;\r
9248       }\r
9249     }\r
9250     uport = (unsigned short) fromPort;\r
9251     mysa.sin_port = htons(uport);\r
9252     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9253         == SOCKET_ERROR) {\r
9254       err = WSAGetLastError();\r
9255       if (err == WSAEADDRINUSE) continue;\r
9256       WSACleanup();\r
9257       return err;\r
9258     }\r
9259     if (connect(s, (struct sockaddr *) &sa,\r
9260       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9261       err = WSAGetLastError();\r
9262       if (err == WSAEADDRINUSE) {\r
9263         closesocket(s);\r
9264         s = -1;\r
9265         continue;\r
9266       }\r
9267       WSACleanup();\r
9268       return err;\r
9269     }\r
9270     break;\r
9271   }\r
9272 \r
9273   /* Bind stderr local socket to unused "privileged" port address\r
9274    */\r
9275   s2 = INVALID_SOCKET;\r
9276   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9277   mysa.sin_family = AF_INET;\r
9278   mysa.sin_addr.s_addr = INADDR_ANY;\r
9279   for (fromPort = 1023;; fromPort--) {\r
9280     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9281     if (fromPort < 0) {\r
9282       (void) closesocket(s);\r
9283       WSACleanup();\r
9284       return WSAEADDRINUSE;\r
9285     }\r
9286     if (s2 == INVALID_SOCKET) {\r
9287       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9288         err = WSAGetLastError();\r
9289         closesocket(s);\r
9290         WSACleanup();\r
9291         return err;\r
9292       }\r
9293     }\r
9294     uport = (unsigned short) fromPort;\r
9295     mysa.sin_port = htons(uport);\r
9296     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9297         == SOCKET_ERROR) {\r
9298       err = WSAGetLastError();\r
9299       if (err == WSAEADDRINUSE) continue;\r
9300       (void) closesocket(s);\r
9301       WSACleanup();\r
9302       return err;\r
9303     }\r
9304     if (listen(s2, 1) == SOCKET_ERROR) {\r
9305       err = WSAGetLastError();\r
9306       if (err == WSAEADDRINUSE) {\r
9307         closesocket(s2);\r
9308         s2 = INVALID_SOCKET;\r
9309         continue;\r
9310       }\r
9311       (void) closesocket(s);\r
9312       (void) closesocket(s2);\r
9313       WSACleanup();\r
9314       return err;\r
9315     }\r
9316     break;\r
9317   }\r
9318   prevStderrPort = fromPort; // remember port used\r
9319   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9320 \r
9321   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9322     err = WSAGetLastError();\r
9323     (void) closesocket(s);\r
9324     (void) closesocket(s2);\r
9325     WSACleanup();\r
9326     return err;\r
9327   }\r
9328 \r
9329   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9330     err = WSAGetLastError();\r
9331     (void) closesocket(s);\r
9332     (void) closesocket(s2);\r
9333     WSACleanup();\r
9334     return err;\r
9335   }\r
9336   if (*user == NULLCHAR) user = UserName();\r
9337   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9338     err = WSAGetLastError();\r
9339     (void) closesocket(s);\r
9340     (void) closesocket(s2);\r
9341     WSACleanup();\r
9342     return err;\r
9343   }\r
9344   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9345     err = WSAGetLastError();\r
9346     (void) closesocket(s);\r
9347     (void) closesocket(s2);\r
9348     WSACleanup();\r
9349     return err;\r
9350   }\r
9351 \r
9352   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9353     err = WSAGetLastError();\r
9354     (void) closesocket(s);\r
9355     (void) closesocket(s2);\r
9356     WSACleanup();\r
9357     return err;\r
9358   }\r
9359   (void) closesocket(s2);  /* Stop listening */\r
9360 \r
9361   /* Prepare return value */\r
9362   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9363   cp->kind = CPRcmd;\r
9364   cp->sock = s;\r
9365   cp->sock2 = s3;\r
9366   *pr = (ProcRef *) cp;\r
9367 \r
9368   return NO_ERROR;\r
9369 }\r
9370 \r
9371 \r
9372 InputSourceRef\r
9373 AddInputSource(ProcRef pr, int lineByLine,\r
9374                InputCallback func, VOIDSTAR closure)\r
9375 {\r
9376   InputSource *is, *is2 = NULL;\r
9377   ChildProc *cp = (ChildProc *) pr;\r
9378 \r
9379   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9380   is->lineByLine = lineByLine;\r
9381   is->func = func;\r
9382   is->closure = closure;\r
9383   is->second = NULL;\r
9384   is->next = is->buf;\r
9385   if (pr == NoProc) {\r
9386     is->kind = CPReal;\r
9387     consoleInputSource = is;\r
9388   } else {\r
9389     is->kind = cp->kind;\r
9390     /* \r
9391         [AS] Try to avoid a race condition if the thread is given control too early:\r
9392         we create all threads suspended so that the is->hThread variable can be\r
9393         safely assigned, then let the threads start with ResumeThread.\r
9394     */\r
9395     switch (cp->kind) {\r
9396     case CPReal:\r
9397       is->hFile = cp->hFrom;\r
9398       cp->hFrom = NULL; /* now owned by InputThread */\r
9399       is->hThread =\r
9400         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9401                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9402       break;\r
9403 \r
9404     case CPComm:\r
9405       is->hFile = cp->hFrom;\r
9406       cp->hFrom = NULL; /* now owned by InputThread */\r
9407       is->hThread =\r
9408         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9409                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9410       break;\r
9411 \r
9412     case CPSock:\r
9413       is->sock = cp->sock;\r
9414       is->hThread =\r
9415         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9416                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9417       break;\r
9418 \r
9419     case CPRcmd:\r
9420       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9421       *is2 = *is;\r
9422       is->sock = cp->sock;\r
9423       is->second = is2;\r
9424       is2->sock = cp->sock2;\r
9425       is2->second = is2;\r
9426       is->hThread =\r
9427         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9428                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9429       is2->hThread =\r
9430         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9431                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9432       break;\r
9433     }\r
9434 \r
9435     if( is->hThread != NULL ) {\r
9436         ResumeThread( is->hThread );\r
9437     }\r
9438 \r
9439     if( is2 != NULL && is2->hThread != NULL ) {\r
9440         ResumeThread( is2->hThread );\r
9441     }\r
9442   }\r
9443 \r
9444   return (InputSourceRef) is;\r
9445 }\r
9446 \r
9447 void\r
9448 RemoveInputSource(InputSourceRef isr)\r
9449 {\r
9450   InputSource *is;\r
9451 \r
9452   is = (InputSource *) isr;\r
9453   is->hThread = NULL;  /* tell thread to stop */\r
9454   CloseHandle(is->hThread);\r
9455   if (is->second != NULL) {\r
9456     is->second->hThread = NULL;\r
9457     CloseHandle(is->second->hThread);\r
9458   }\r
9459 }\r
9460 \r
9461 int no_wrap(char *message, int count)\r
9462 {\r
9463     ConsoleOutput(message, count, FALSE);\r
9464     return count;\r
9465 }\r
9466 \r
9467 int\r
9468 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9469 {\r
9470   DWORD dOutCount;\r
9471   int outCount = SOCKET_ERROR;\r
9472   ChildProc *cp = (ChildProc *) pr;\r
9473   static OVERLAPPED ovl;\r
9474   static int line = 0;\r
9475 \r
9476   if (pr == NoProc)\r
9477   {\r
9478     if (appData.noJoin || !appData.useInternalWrap)\r
9479       return no_wrap(message, count);\r
9480     else\r
9481     {\r
9482       int width = get_term_width();\r
9483       int len = wrap(NULL, message, count, width, &line);\r
9484       char *msg = malloc(len);\r
9485       int dbgchk;\r
9486 \r
9487       if (!msg)\r
9488         return no_wrap(message, count);\r
9489       else\r
9490       {\r
9491         dbgchk = wrap(msg, message, count, width, &line);\r
9492         if (dbgchk != len && appData.debugMode)\r
9493             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9494         ConsoleOutput(msg, len, FALSE);\r
9495         free(msg);\r
9496         return len;\r
9497       }\r
9498     }\r
9499   }\r
9500 \r
9501   if (ovl.hEvent == NULL) {\r
9502     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9503   }\r
9504   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9505 \r
9506   switch (cp->kind) {\r
9507   case CPSock:\r
9508   case CPRcmd:\r
9509     outCount = send(cp->sock, message, count, 0);\r
9510     if (outCount == SOCKET_ERROR) {\r
9511       *outError = WSAGetLastError();\r
9512     } else {\r
9513       *outError = NO_ERROR;\r
9514     }\r
9515     break;\r
9516 \r
9517   case CPReal:\r
9518     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9519                   &dOutCount, NULL)) {\r
9520       *outError = NO_ERROR;\r
9521       outCount = (int) dOutCount;\r
9522     } else {\r
9523       *outError = GetLastError();\r
9524     }\r
9525     break;\r
9526 \r
9527   case CPComm:\r
9528     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9529                             &dOutCount, &ovl);\r
9530     if (*outError == NO_ERROR) {\r
9531       outCount = (int) dOutCount;\r
9532     }\r
9533     break;\r
9534   }\r
9535   return outCount;\r
9536 }\r
9537 \r
9538 int\r
9539 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9540                        long msdelay)\r
9541 {\r
9542   /* Ignore delay, not implemented for WinBoard */\r
9543   return OutputToProcess(pr, message, count, outError);\r
9544 }\r
9545 \r
9546 \r
9547 void\r
9548 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9549                         char *buf, int count, int error)\r
9550 {\r
9551   DisplayFatalError(_("Not implemented"), 0, 1);\r
9552 }\r
9553 \r
9554 /* see wgamelist.c for Game List functions */\r
9555 /* see wedittags.c for Edit Tags functions */\r
9556 \r
9557 \r
9558 VOID\r
9559 ICSInitScript()\r
9560 {\r
9561   FILE *f;\r
9562   char buf[MSG_SIZ];\r
9563   char *dummy;\r
9564 \r
9565   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9566     f = fopen(buf, "r");\r
9567     if (f != NULL) {\r
9568       ProcessICSInitScript(f);\r
9569       fclose(f);\r
9570     }\r
9571   }\r
9572 }\r
9573 \r
9574 \r
9575 VOID\r
9576 StartAnalysisClock()\r
9577 {\r
9578   if (analysisTimerEvent) return;\r
9579   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9580                                         (UINT) 2000, NULL);\r
9581 }\r
9582 \r
9583 VOID\r
9584 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9585 {\r
9586   highlightInfo.sq[0].x = fromX;\r
9587   highlightInfo.sq[0].y = fromY;\r
9588   highlightInfo.sq[1].x = toX;\r
9589   highlightInfo.sq[1].y = toY;\r
9590 }\r
9591 \r
9592 VOID\r
9593 ClearHighlights()\r
9594 {\r
9595   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9596     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9597 }\r
9598 \r
9599 VOID\r
9600 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9601 {\r
9602   premoveHighlightInfo.sq[0].x = fromX;\r
9603   premoveHighlightInfo.sq[0].y = fromY;\r
9604   premoveHighlightInfo.sq[1].x = toX;\r
9605   premoveHighlightInfo.sq[1].y = toY;\r
9606 }\r
9607 \r
9608 VOID\r
9609 ClearPremoveHighlights()\r
9610 {\r
9611   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9612     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9613 }\r
9614 \r
9615 VOID\r
9616 ShutDownFrontEnd()\r
9617 {\r
9618   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9619   DeleteClipboardTempFiles();\r
9620 }\r
9621 \r
9622 void\r
9623 BoardToTop()\r
9624 {\r
9625     if (IsIconic(hwndMain))\r
9626       ShowWindow(hwndMain, SW_RESTORE);\r
9627 \r
9628     SetActiveWindow(hwndMain);\r
9629 }\r
9630 \r
9631 /*\r
9632  * Prototypes for animation support routines\r
9633  */\r
9634 static void ScreenSquare(int column, int row, POINT * pt);\r
9635 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9636      POINT frames[], int * nFrames);\r
9637 \r
9638 \r
9639 #define kFactor 4\r
9640 \r
9641 void\r
9642 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9643 {       // [HGM] atomic: animate blast wave\r
9644         int i;\r
9645 \r
9646         explodeInfo.fromX = fromX;\r
9647         explodeInfo.fromY = fromY;\r
9648         explodeInfo.toX = toX;\r
9649         explodeInfo.toY = toY;\r
9650         for(i=1; i<4*kFactor; i++) {\r
9651             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9652             DrawPosition(FALSE, board);\r
9653             Sleep(appData.animSpeed);\r
9654         }\r
9655         explodeInfo.radius = 0;\r
9656         DrawPosition(TRUE, board);\r
9657 }\r
9658 \r
9659 void\r
9660 AnimateMove(board, fromX, fromY, toX, toY)\r
9661      Board board;\r
9662      int fromX;\r
9663      int fromY;\r
9664      int toX;\r
9665      int toY;\r
9666 {\r
9667   ChessSquare piece;\r
9668   POINT start, finish, mid;\r
9669   POINT frames[kFactor * 2 + 1];\r
9670   int nFrames, n;\r
9671 \r
9672   if (!appData.animate) return;\r
9673   if (doingSizing) return;\r
9674   if (fromY < 0 || fromX < 0) return;\r
9675   piece = board[fromY][fromX];\r
9676   if (piece >= EmptySquare) return;\r
9677 \r
9678   ScreenSquare(fromX, fromY, &start);\r
9679   ScreenSquare(toX, toY, &finish);\r
9680 \r
9681   /* All moves except knight jumps move in straight line */\r
9682   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9683     mid.x = start.x + (finish.x - start.x) / 2;\r
9684     mid.y = start.y + (finish.y - start.y) / 2;\r
9685   } else {\r
9686     /* Knight: make straight movement then diagonal */\r
9687     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9688        mid.x = start.x + (finish.x - start.x) / 2;\r
9689        mid.y = start.y;\r
9690      } else {\r
9691        mid.x = start.x;\r
9692        mid.y = start.y + (finish.y - start.y) / 2;\r
9693      }\r
9694   }\r
9695   \r
9696   /* Don't use as many frames for very short moves */\r
9697   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9698     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9699   else\r
9700     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9701 \r
9702   animInfo.from.x = fromX;\r
9703   animInfo.from.y = fromY;\r
9704   animInfo.to.x = toX;\r
9705   animInfo.to.y = toY;\r
9706   animInfo.lastpos = start;\r
9707   animInfo.piece = piece;\r
9708   for (n = 0; n < nFrames; n++) {\r
9709     animInfo.pos = frames[n];\r
9710     DrawPosition(FALSE, NULL);\r
9711     animInfo.lastpos = animInfo.pos;\r
9712     Sleep(appData.animSpeed);\r
9713   }\r
9714   animInfo.pos = finish;\r
9715   DrawPosition(FALSE, NULL);\r
9716   animInfo.piece = EmptySquare;\r
9717   Explode(board, fromX, fromY, toX, toY);\r
9718 }\r
9719 \r
9720 /*      Convert board position to corner of screen rect and color       */\r
9721 \r
9722 static void\r
9723 ScreenSquare(column, row, pt)\r
9724      int column; int row; POINT * pt;\r
9725 {\r
9726   if (flipView) {\r
9727     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9728     pt->y = lineGap + row * (squareSize + lineGap);\r
9729   } else {\r
9730     pt->x = lineGap + column * (squareSize + lineGap);\r
9731     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9732   }\r
9733 }\r
9734 \r
9735 /*      Generate a series of frame coords from start->mid->finish.\r
9736         The movement rate doubles until the half way point is\r
9737         reached, then halves back down to the final destination,\r
9738         which gives a nice slow in/out effect. The algorithmn\r
9739         may seem to generate too many intermediates for short\r
9740         moves, but remember that the purpose is to attract the\r
9741         viewers attention to the piece about to be moved and\r
9742         then to where it ends up. Too few frames would be less\r
9743         noticeable.                                             */\r
9744 \r
9745 static void\r
9746 Tween(start, mid, finish, factor, frames, nFrames)\r
9747      POINT * start; POINT * mid;\r
9748      POINT * finish; int factor;\r
9749      POINT frames[]; int * nFrames;\r
9750 {\r
9751   int n, fraction = 1, count = 0;\r
9752 \r
9753   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9754   for (n = 0; n < factor; n++)\r
9755     fraction *= 2;\r
9756   for (n = 0; n < factor; n++) {\r
9757     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9758     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9759     count ++;\r
9760     fraction = fraction / 2;\r
9761   }\r
9762   \r
9763   /* Midpoint */\r
9764   frames[count] = *mid;\r
9765   count ++;\r
9766   \r
9767   /* Slow out, stepping 1/2, then 1/4, ... */\r
9768   fraction = 2;\r
9769   for (n = 0; n < factor; n++) {\r
9770     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9771     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9772     count ++;\r
9773     fraction = fraction * 2;\r
9774   }\r
9775   *nFrames = count;\r
9776 }\r
9777 \r
9778 void\r
9779 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9780 {\r
9781     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9782 \r
9783     EvalGraphSet( first, last, current, pvInfoList );\r
9784 }\r
9785 \r
9786 void\r
9787 SettingsPopUp(ChessProgramState *cps)\r
9788 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9789       EngineOptionsPopup(savedHwnd, cps);\r
9790 }\r