50d82dde6cb4d4d8f2f0c579789e79febe4a1f0d
[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, 2011 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   ChessSquare piece;\r
127 } DragInfo;\r
128 \r
129 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
130 \r
131 typedef struct {\r
132   POINT sq[2];    /* board coordinates of from, to squares */\r
133 } HighlightInfo;\r
134 \r
135 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
136 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
137 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
138 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
139 \r
140 typedef struct { // [HGM] atomic\r
141   int fromX, fromY, toX, toY, radius;\r
142 } ExplodeInfo;\r
143 \r
144 static ExplodeInfo explodeInfo;\r
145 \r
146 /* Window class names */\r
147 char szAppName[] = "WinBoard";\r
148 char szConsoleName[] = "WBConsole";\r
149 \r
150 /* Title bar text */\r
151 char szTitle[] = "WinBoard";\r
152 char szConsoleTitle[] = "I C S Interaction";\r
153 \r
154 char *programName;\r
155 char *settingsFileName;\r
156 Boolean saveSettingsOnExit;\r
157 char installDir[MSG_SIZ];\r
158 int errorExitStatus;\r
159 \r
160 BoardSize boardSize;\r
161 Boolean chessProgram;\r
162 //static int boardX, boardY;\r
163 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
164 int squareSize, lineGap, minorSize;\r
165 static int winW, winH;\r
166 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
167 static int logoHeight = 0;\r
168 static char messageText[MESSAGE_TEXT_MAX];\r
169 static int clockTimerEvent = 0;\r
170 static int loadGameTimerEvent = 0;\r
171 static int analysisTimerEvent = 0;\r
172 static DelayedEventCallback delayedTimerCallback;\r
173 static int delayedTimerEvent = 0;\r
174 static int buttonCount = 2;\r
175 char *icsTextMenuString;\r
176 char *icsNames;\r
177 char *firstChessProgramNames;\r
178 char *secondChessProgramNames;\r
179 \r
180 #define PALETTESIZE 256\r
181 \r
182 HINSTANCE hInst;          /* current instance */\r
183 Boolean alwaysOnTop = FALSE;\r
184 RECT boardRect;\r
185 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
186   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
187 HPALETTE hPal;\r
188 ColorClass currentColorClass;\r
189 \r
190 static HWND savedHwnd;\r
191 HWND hCommPort = NULL;    /* currently open comm port */\r
192 static HWND hwndPause;    /* pause button */\r
193 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
194 static HBRUSH lightSquareBrush, darkSquareBrush,\r
195   blackSquareBrush, /* [HGM] for band between board and holdings */\r
196   explodeBrush,     /* [HGM] atomic */\r
197   markerBrush,      /* [HGM] markers */\r
198   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
199 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
200 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
201 static HPEN gridPen = NULL;\r
202 static HPEN highlightPen = NULL;\r
203 static HPEN premovePen = NULL;\r
204 static NPLOGPALETTE pLogPal;\r
205 static BOOL paletteChanged = FALSE;\r
206 static HICON iconWhite, iconBlack, iconCurrent;\r
207 static int doingSizing = FALSE;\r
208 static int lastSizing = 0;\r
209 static int prevStderrPort;\r
210 static HBITMAP userLogo;\r
211 \r
212 static HBITMAP liteBackTexture = NULL;\r
213 static HBITMAP darkBackTexture = NULL;\r
214 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
215 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
216 static int backTextureSquareSize = 0;\r
217 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
218 \r
219 #if __GNUC__ && !defined(_winmajor)\r
220 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
221 #else\r
222 #if defined(_winmajor)\r
223 #define oldDialog (_winmajor < 4)\r
224 #else\r
225 #define oldDialog 0\r
226 #endif\r
227 #endif\r
228 \r
229 #define INTERNATIONAL\r
230 \r
231 #ifdef INTERNATIONAL\r
232 #  define _(s) T_(s)\r
233 #  define N_(s) s\r
234 #else\r
235 #  define _(s) s\r
236 #  define N_(s) s\r
237 #  define T_(s) s\r
238 #  define Translate(x, y)\r
239 #  define LoadLanguageFile(s)\r
240 #endif\r
241 \r
242 #ifdef INTERNATIONAL\r
243 \r
244 Boolean barbaric; // flag indicating if translation is needed\r
245 \r
246 // list of item numbers used in each dialog (used to alter language at run time)\r
247 \r
248 #define ABOUTBOX -1  /* not sure why these are needed */\r
249 #define ABOUTBOX2 -1\r
250 \r
251 int dialogItems[][40] = {\r
252 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
253 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
254   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
255 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL }, \r
256 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
257   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
258 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
259 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
260   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
261 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
262 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
263   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
264 { ABOUTBOX2, IDC_ChessBoard }, \r
265 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
266   OPT_GameListClose, IDC_GameListDoFilter }, \r
267 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
268 { DLG_Error, IDOK }, \r
269 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
270   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
271 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
272 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
273   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
274   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
275 { DLG_IndexNumber, IDC_Index }, \r
276 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
277 { DLG_TypeInName, IDOK, IDCANCEL }, \r
278 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
279   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
280 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
281   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
282   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
283   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
284   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
285   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
286   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
287 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
288   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
289   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
290   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
291   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
292   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
293   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
294   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
295   GPB_General, GPB_Alarm }, \r
296 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
297   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
298   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
299   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
300   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
301   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
302   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
303   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size }, \r
304 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
305   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
306   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
307   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
308   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
309   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
310   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
311   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
312   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
313 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
314   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
315   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,\r
316   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
317 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
318 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
319   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
320 { DLG_MoveHistory }, \r
321 { DLG_EvalGraph }, \r
322 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
323 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
324 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
325   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
326   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
327   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
328 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
329   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
330   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
331 { 0 }\r
332 };\r
333 \r
334 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
335 static int lastChecked;\r
336 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
337 extern int tinyLayout;\r
338 extern char * menuBarText[][10];\r
339 \r
340 void\r
341 LoadLanguageFile(char *name)\r
342 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
343     FILE *f;\r
344     int i=0, j=0, n=0, k;\r
345     char buf[MSG_SIZ];\r
346 \r
347     if(!name || name[0] == NULLCHAR) return;\r
348       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
349     appData.language = oldLanguage;\r
350     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
351     if((f = fopen(buf, "r")) == NULL) return;\r
352     while((k = fgetc(f)) != EOF) {\r
353         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
354         languageBuf[i] = k;\r
355         if(k == '\n') {\r
356             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
357                 char *p;\r
358                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
359                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
360                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
361                         english[j] = languageBuf + n + 1; *p = 0;\r
362                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
363 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
364                     }\r
365                 }\r
366             }\r
367             n = i + 1;\r
368         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
369             switch(k) {\r
370               case 'n': k = '\n'; break;\r
371               case 'r': k = '\r'; break;\r
372               case 't': k = '\t'; break;\r
373             }\r
374             languageBuf[--i] = k;\r
375         }\r
376         i++;\r
377     }\r
378     fclose(f);\r
379     barbaric = (j != 0);\r
380     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
381 }\r
382 \r
383 char *\r
384 T_(char *s)\r
385 {   // return the translation of the given string\r
386     // efficiency can be improved a lot...\r
387     int i=0;\r
388 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
389     if(!barbaric) return s;\r
390     if(!s) return ""; // sanity\r
391     while(english[i]) {\r
392         if(!strcmp(s, english[i])) return foreign[i];\r
393         i++;\r
394     }\r
395     return s;\r
396 }\r
397 \r
398 void\r
399 Translate(HWND hDlg, int dialogID)\r
400 {   // translate all text items in the given dialog\r
401     int i=0, j, k;\r
402     char buf[MSG_SIZ], *s;\r
403     if(!barbaric) return;\r
404     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
405     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
406     GetWindowText( hDlg, buf, MSG_SIZ );\r
407     s = T_(buf);\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 HMENU\r
418 TranslateOneMenu(int i, HMENU subMenu)\r
419 {\r
420     int j;\r
421     static MENUITEMINFO info;\r
422 \r
423     info.cbSize = sizeof(MENUITEMINFO);\r
424     info.fMask = MIIM_STATE | MIIM_TYPE;\r
425           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
426             char buf[MSG_SIZ];\r
427             info.dwTypeData = buf;\r
428             info.cch = sizeof(buf);\r
429             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
430             if(i < 10) {
431                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
432                 else menuText[i][j] = strdup(buf); // remember original on first change\r
433             }\r
434             if(buf[0] == NULLCHAR) continue;\r
435             info.dwTypeData = T_(buf);\r
436             info.cch = strlen(buf)+1;\r
437             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
438           }\r
439     return subMenu;\r
440 }\r
441 \r
442 void\r
443 TranslateMenus(int addLanguage)\r
444 {\r
445     int i;\r
446     WIN32_FIND_DATA fileData;\r
447     HANDLE hFind;\r
448 #define IDM_English 1970\r
449     if(1) {\r
450         HMENU mainMenu = GetMenu(hwndMain);\r
451         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
452           HMENU subMenu = GetSubMenu(mainMenu, i);\r
453           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
454                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
455           TranslateOneMenu(i, subMenu);\r
456         }\r
457         DrawMenuBar(hwndMain);\r
458     }\r
459 \r
460     if(!addLanguage) return;\r
461     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
462         HMENU mainMenu = GetMenu(hwndMain);\r
463         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
464         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
465         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
466         i = 0; lastChecked = IDM_English;\r
467         do {\r
468             char *p, *q = fileData.cFileName;\r
469             int checkFlag = MF_UNCHECKED;\r
470             languageFile[i] = strdup(q);\r
471             if(barbaric && !strcmp(oldLanguage, q)) {\r
472                 checkFlag = MF_CHECKED;\r
473                 lastChecked = IDM_English + i + 1;\r
474                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
475             }\r
476             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
477             p = strstr(fileData.cFileName, ".lng");\r
478             if(p) *p = 0;\r
479             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
480         } while(FindNextFile(hFind, &fileData));\r
481         FindClose(hFind);\r
482     }\r
483 }\r
484 \r
485 #endif\r
486 \r
487 typedef struct {\r
488   char *name;\r
489   int squareSize;\r
490   int lineGap;\r
491   int smallLayout;\r
492   int tinyLayout;\r
493   int cliWidth, cliHeight;\r
494 } SizeInfo;\r
495 \r
496 SizeInfo sizeInfo[] = \r
497 {\r
498   { "tiny",     21, 0, 1, 1, 0, 0 },\r
499   { "teeny",    25, 1, 1, 1, 0, 0 },\r
500   { "dinky",    29, 1, 1, 1, 0, 0 },\r
501   { "petite",   33, 1, 1, 1, 0, 0 },\r
502   { "slim",     37, 2, 1, 0, 0, 0 },\r
503   { "small",    40, 2, 1, 0, 0, 0 },\r
504   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
505   { "middling", 49, 2, 0, 0, 0, 0 },\r
506   { "average",  54, 2, 0, 0, 0, 0 },\r
507   { "moderate", 58, 3, 0, 0, 0, 0 },\r
508   { "medium",   64, 3, 0, 0, 0, 0 },\r
509   { "bulky",    72, 3, 0, 0, 0, 0 },\r
510   { "large",    80, 3, 0, 0, 0, 0 },\r
511   { "big",      87, 3, 0, 0, 0, 0 },\r
512   { "huge",     95, 3, 0, 0, 0, 0 },\r
513   { "giant",    108, 3, 0, 0, 0, 0 },\r
514   { "colossal", 116, 4, 0, 0, 0, 0 },\r
515   { "titanic",  129, 4, 0, 0, 0, 0 },\r
516   { NULL, 0, 0, 0, 0, 0, 0 }\r
517 };\r
518 \r
519 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
520 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
521 {\r
522   { 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
523   { 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
524   { 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
525   { 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
526   { 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
527   { 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
528   { 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
529   { 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
530   { 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
531   { 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
532   { 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
533   { 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
534   { 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
535   { 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
536   { 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
537   { 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
538   { 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
539   { 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
540 };\r
541 \r
542 MyFont *font[NUM_SIZES][NUM_FONTS];\r
543 \r
544 typedef struct {\r
545   char *label;\r
546   int id;\r
547   HWND hwnd;\r
548   WNDPROC wndproc;\r
549 } MyButtonDesc;\r
550 \r
551 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
552 #define N_BUTTONS 5\r
553 \r
554 MyButtonDesc buttonDesc[N_BUTTONS] =\r
555 {\r
556   {"<<", IDM_ToStart, NULL, NULL},\r
557   {"<", IDM_Backward, NULL, NULL},\r
558   {"P", IDM_Pause, NULL, NULL},\r
559   {">", IDM_Forward, NULL, NULL},\r
560   {">>", IDM_ToEnd, NULL, NULL},\r
561 };\r
562 \r
563 int tinyLayout = 0, smallLayout = 0;\r
564 #define MENU_BAR_ITEMS 9\r
565 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
566   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
567   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
568 };\r
569 \r
570 \r
571 MySound sounds[(int)NSoundClasses];\r
572 MyTextAttribs textAttribs[(int)NColorClasses];\r
573 \r
574 MyColorizeAttribs colorizeAttribs[] = {\r
575   { (COLORREF)0, 0, N_("Shout Text") },\r
576   { (COLORREF)0, 0, N_("SShout/CShout") },\r
577   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
578   { (COLORREF)0, 0, N_("Channel Text") },\r
579   { (COLORREF)0, 0, N_("Kibitz Text") },\r
580   { (COLORREF)0, 0, N_("Tell Text") },\r
581   { (COLORREF)0, 0, N_("Challenge Text") },\r
582   { (COLORREF)0, 0, N_("Request Text") },\r
583   { (COLORREF)0, 0, N_("Seek Text") },\r
584   { (COLORREF)0, 0, N_("Normal Text") },\r
585   { (COLORREF)0, 0, N_("None") }\r
586 };\r
587 \r
588 \r
589 \r
590 static char *commentTitle;\r
591 static char *commentText;\r
592 static int commentIndex;\r
593 static Boolean editComment = FALSE;\r
594 \r
595 \r
596 char errorTitle[MSG_SIZ];\r
597 char errorMessage[2*MSG_SIZ];\r
598 HWND errorDialog = NULL;\r
599 BOOLEAN moveErrorMessageUp = FALSE;\r
600 BOOLEAN consoleEcho = TRUE;\r
601 CHARFORMAT consoleCF;\r
602 COLORREF consoleBackgroundColor;\r
603 \r
604 char *programVersion;\r
605 \r
606 #define CPReal 1\r
607 #define CPComm 2\r
608 #define CPSock 3\r
609 #define CPRcmd 4\r
610 typedef int CPKind;\r
611 \r
612 typedef struct {\r
613   CPKind kind;\r
614   HANDLE hProcess;\r
615   DWORD pid;\r
616   HANDLE hTo;\r
617   HANDLE hFrom;\r
618   SOCKET sock;\r
619   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
620 } ChildProc;\r
621 \r
622 #define INPUT_SOURCE_BUF_SIZE 4096\r
623 \r
624 typedef struct _InputSource {\r
625   CPKind kind;\r
626   HANDLE hFile;\r
627   SOCKET sock;\r
628   int lineByLine;\r
629   HANDLE hThread;\r
630   DWORD id;\r
631   char buf[INPUT_SOURCE_BUF_SIZE];\r
632   char *next;\r
633   DWORD count;\r
634   int error;\r
635   InputCallback func;\r
636   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
637   VOIDSTAR closure;\r
638 } InputSource;\r
639 \r
640 InputSource *consoleInputSource;\r
641 \r
642 DCB dcb;\r
643 \r
644 /* forward */\r
645 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
646 VOID ConsoleCreate();\r
647 LRESULT CALLBACK\r
648   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
649 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
650 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
651 VOID ParseCommSettings(char *arg, DCB *dcb);\r
652 LRESULT CALLBACK\r
653   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
654 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
655 void ParseIcsTextMenu(char *icsTextMenuString);\r
656 VOID PopUpNameDialog(char firstchar);\r
657 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
658 \r
659 /* [AS] */\r
660 int NewGameFRC();\r
661 int GameListOptions();\r
662 \r
663 int dummy; // [HGM] for obsolete args\r
664 \r
665 HWND hwndMain = NULL;        /* root window*/\r
666 HWND hwndConsole = NULL;\r
667 HWND commentDialog = NULL;\r
668 HWND moveHistoryDialog = NULL;\r
669 HWND evalGraphDialog = NULL;\r
670 HWND engineOutputDialog = NULL;\r
671 HWND gameListDialog = NULL;\r
672 HWND editTagsDialog = NULL;\r
673 \r
674 int commentUp = FALSE;\r
675 \r
676 WindowPlacement wpMain;\r
677 WindowPlacement wpConsole;\r
678 WindowPlacement wpComment;\r
679 WindowPlacement wpMoveHistory;\r
680 WindowPlacement wpEvalGraph;\r
681 WindowPlacement wpEngineOutput;\r
682 WindowPlacement wpGameList;\r
683 WindowPlacement wpTags;\r
684 \r
685 VOID EngineOptionsPopup(); // [HGM] settings\r
686 \r
687 VOID GothicPopUp(char *title, VariantClass variant);\r
688 /*\r
689  * Setting "frozen" should disable all user input other than deleting\r
690  * the window.  We do this while engines are initializing themselves.\r
691  */\r
692 static int frozen = 0;\r
693 static int oldMenuItemState[MENU_BAR_ITEMS];\r
694 void FreezeUI()\r
695 {\r
696   HMENU hmenu;\r
697   int i;\r
698 \r
699   if (frozen) return;\r
700   frozen = 1;\r
701   hmenu = GetMenu(hwndMain);\r
702   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
703     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
704   }\r
705   DrawMenuBar(hwndMain);\r
706 }\r
707 \r
708 /* Undo a FreezeUI */\r
709 void ThawUI()\r
710 {\r
711   HMENU hmenu;\r
712   int i;\r
713 \r
714   if (!frozen) return;\r
715   frozen = 0;\r
716   hmenu = GetMenu(hwndMain);\r
717   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
718     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
719   }\r
720   DrawMenuBar(hwndMain);\r
721 }\r
722 \r
723 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
724 \r
725 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
726 #ifdef JAWS\r
727 #include "jaws.c"\r
728 #else\r
729 #define JAWS_INIT\r
730 #define JAWS_ARGS\r
731 #define JAWS_ALT_INTERCEPT\r
732 #define JAWS_KB_NAVIGATION\r
733 #define JAWS_MENU_ITEMS\r
734 #define JAWS_SILENCE\r
735 #define JAWS_REPLAY\r
736 #define JAWS_ACCEL\r
737 #define JAWS_COPYRIGHT\r
738 #define JAWS_DELETE(X) X\r
739 #define SAYMACHINEMOVE()\r
740 #define SAY(X)\r
741 #endif\r
742 \r
743 /*---------------------------------------------------------------------------*\\r
744  *\r
745  * WinMain\r
746  *\r
747 \*---------------------------------------------------------------------------*/\r
748 \r
749 int APIENTRY\r
750 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
751         LPSTR lpCmdLine, int nCmdShow)\r
752 {\r
753   MSG msg;\r
754   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
755 //  INITCOMMONCONTROLSEX ex;\r
756 \r
757   debugFP = stderr;\r
758 \r
759   LoadLibrary("RICHED32.DLL");\r
760   consoleCF.cbSize = sizeof(CHARFORMAT);\r
761 \r
762   if (!InitApplication(hInstance)) {\r
763     return (FALSE);\r
764   }\r
765   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
766     return (FALSE);\r
767   }\r
768 \r
769   JAWS_INIT\r
770   TranslateMenus(1);\r
771 \r
772 //  InitCommonControlsEx(&ex);\r
773   InitCommonControls();\r
774 \r
775   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
776   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
777   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
778 \r
779   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
780 \r
781   while (GetMessage(&msg, /* message structure */\r
782                     NULL, /* handle of window receiving the message */\r
783                     0,    /* lowest message to examine */\r
784                     0))   /* highest message to examine */\r
785     {\r
786 \r
787       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
788         // [HGM] navigate: switch between all windows with tab\r
789         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
790         int i, currentElement = 0;\r
791 \r
792         // first determine what element of the chain we come from (if any)\r
793         if(appData.icsActive) {\r
794             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
795             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
796         }\r
797         if(engineOutputDialog && EngineOutputIsUp()) {\r
798             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
799             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
800         }\r
801         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
802             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
803         }\r
804         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
805         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
806         if(msg.hwnd == e1)                 currentElement = 2; else\r
807         if(msg.hwnd == e2)                 currentElement = 3; else\r
808         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
809         if(msg.hwnd == mh)                currentElement = 4; else\r
810         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
811         if(msg.hwnd == hText)  currentElement = 5; else\r
812         if(msg.hwnd == hInput) currentElement = 6; else\r
813         for (i = 0; i < N_BUTTONS; i++) {\r
814             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
815         }\r
816 \r
817         // determine where to go to\r
818         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
819           do {\r
820             currentElement = (currentElement + direction) % 7;\r
821             switch(currentElement) {\r
822                 case 0:\r
823                   h = hwndMain; break; // passing this case always makes the loop exit\r
824                 case 1:\r
825                   h = buttonDesc[0].hwnd; break; // could be NULL\r
826                 case 2:\r
827                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
828                   h = e1; break;\r
829                 case 3:\r
830                   if(!EngineOutputIsUp()) continue;\r
831                   h = e2; break;\r
832                 case 4:\r
833                   if(!MoveHistoryIsUp()) continue;\r
834                   h = mh; break;\r
835 //              case 6: // input to eval graph does not seem to get here!\r
836 //                if(!EvalGraphIsUp()) continue;\r
837 //                h = evalGraphDialog; break;\r
838                 case 5:\r
839                   if(!appData.icsActive) continue;\r
840                   SAY("display");\r
841                   h = hText; break;\r
842                 case 6:\r
843                   if(!appData.icsActive) continue;\r
844                   SAY("input");\r
845                   h = hInput; break;\r
846             }\r
847           } while(h == 0);\r
848 \r
849           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
850           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
851           SetFocus(h);\r
852 \r
853           continue; // this message now has been processed\r
854         }\r
855       }\r
856 \r
857       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
858           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
859           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
860           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
861           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
862           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
863           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
864           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
865           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
866           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
867         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
868         for(i=0; i<MAX_CHAT; i++) \r
869             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
870                 done = 1; break;\r
871         }\r
872         if(done) continue; // [HGM] chat: end patch\r
873         TranslateMessage(&msg); /* Translates virtual key codes */\r
874         DispatchMessage(&msg);  /* Dispatches message to window */\r
875       }\r
876     }\r
877 \r
878 \r
879   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
880 }\r
881 \r
882 /*---------------------------------------------------------------------------*\\r
883  *\r
884  * Initialization functions\r
885  *\r
886 \*---------------------------------------------------------------------------*/\r
887 \r
888 void\r
889 SetUserLogo()\r
890 {   // update user logo if necessary\r
891     static char oldUserName[MSG_SIZ], *curName;\r
892 \r
893     if(appData.autoLogo) {\r
894           curName = UserName();\r
895           if(strcmp(curName, oldUserName)) {\r
896             snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
897                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
898                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
899                 if(userLogo == NULL)\r
900                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
901           }\r
902     }\r
903 }\r
904 \r
905 BOOL\r
906 InitApplication(HINSTANCE hInstance)\r
907 {\r
908   WNDCLASS wc;\r
909 \r
910   /* Fill in window class structure with parameters that describe the */\r
911   /* main window. */\r
912 \r
913   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
914   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
915   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
916   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
917   wc.hInstance     = hInstance;         /* Owner of this class */\r
918   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
919   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
920   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
921   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
922   wc.lpszClassName = szAppName;                 /* Name to register as */\r
923 \r
924   /* Register the window class and return success/failure code. */\r
925   if (!RegisterClass(&wc)) return FALSE;\r
926 \r
927   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
928   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
929   wc.cbClsExtra    = 0;\r
930   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
931   wc.hInstance     = hInstance;\r
932   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
933   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
934   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
935   wc.lpszMenuName  = NULL;\r
936   wc.lpszClassName = szConsoleName;\r
937 \r
938   if (!RegisterClass(&wc)) return FALSE;\r
939   return TRUE;\r
940 }\r
941 \r
942 \r
943 /* Set by InitInstance, used by EnsureOnScreen */\r
944 int screenHeight, screenWidth;\r
945 \r
946 void\r
947 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
948 {\r
949 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
950   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
951   if (*x > screenWidth - 32) *x = 0;\r
952   if (*y > screenHeight - 32) *y = 0;\r
953   if (*x < minX) *x = minX;\r
954   if (*y < minY) *y = minY;\r
955 }\r
956 \r
957 BOOL\r
958 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
959 {\r
960   HWND hwnd; /* Main window handle. */\r
961   int ibs;\r
962   WINDOWPLACEMENT wp;\r
963   char *filepart;\r
964 \r
965   hInst = hInstance;    /* Store instance handle in our global variable */\r
966   programName = szAppName;\r
967 \r
968   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
969     *filepart = NULLCHAR;\r
970   } else {\r
971     GetCurrentDirectory(MSG_SIZ, installDir);\r
972   }\r
973   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
974   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
975   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
976   /* xboard, and older WinBoards, controlled the move sound with the\r
977      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
978      always turn the option on (so that the backend will call us),\r
979      then let the user turn the sound off by setting it to silence if\r
980      desired.  To accommodate old winboard.ini files saved by old\r
981      versions of WinBoard, we also turn off the sound if the option\r
982      was initially set to false. [HGM] taken out of InitAppData */\r
983   if (!appData.ringBellAfterMoves) {\r
984     sounds[(int)SoundMove].name = strdup("");\r
985     appData.ringBellAfterMoves = TRUE;\r
986   }\r
987   if (appData.debugMode) {\r
988     debugFP = fopen(appData.nameOfDebugFile, "w");\r
989     setbuf(debugFP, NULL);\r
990   }\r
991 \r
992   LoadLanguageFile(appData.language);\r
993 \r
994   InitBackEnd1();\r
995 \r
996 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
997 //  InitEngineUCI( installDir, &second );\r
998 \r
999   /* Create a main window for this application instance. */\r
1000   hwnd = CreateWindow(szAppName, szTitle,\r
1001                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1002                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1003                       NULL, NULL, hInstance, NULL);\r
1004   hwndMain = hwnd;\r
1005 \r
1006   /* If window could not be created, return "failure" */\r
1007   if (!hwnd) {\r
1008     return (FALSE);\r
1009   }\r
1010 \r
1011   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1012   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
1013       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1014 \r
1015       if (first.programLogo == NULL && appData.debugMode) {\r
1016           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
1017       }\r
1018   } else if(appData.autoLogo) {\r
1019       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
1020         char buf[MSG_SIZ];\r
1021           snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.firstDirectory);\r
1022         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
1023       }\r
1024   }\r
1025 \r
1026   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
1027       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1028 \r
1029       if (second.programLogo == NULL && appData.debugMode) {\r
1030           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
1031       }\r
1032   } else if(appData.autoLogo) {\r
1033       char buf[MSG_SIZ];\r
1034       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
1035         snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);\r
1036         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1037       } else\r
1038       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
1039         snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);\r
1040         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
1041       }\r
1042   }\r
1043 \r
1044   SetUserLogo();\r
1045 \r
1046   iconWhite = LoadIcon(hInstance, "icon_white");\r
1047   iconBlack = LoadIcon(hInstance, "icon_black");\r
1048   iconCurrent = iconWhite;\r
1049   InitDrawingColors();\r
1050   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1051   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1052   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1053     /* Compute window size for each board size, and use the largest\r
1054        size that fits on this screen as the default. */\r
1055     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1056     if (boardSize == (BoardSize)-1 &&\r
1057         winH <= screenHeight\r
1058            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1059         && winW <= screenWidth) {\r
1060       boardSize = (BoardSize)ibs;\r
1061     }\r
1062   }\r
1063 \r
1064   InitDrawingSizes(boardSize, 0);\r
1065   InitMenuChecks();\r
1066   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1067 \r
1068   /* [AS] Load textures if specified */\r
1069   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1070   \r
1071   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1072       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1073       liteBackTextureMode = appData.liteBackTextureMode;\r
1074 \r
1075       if (liteBackTexture == NULL && appData.debugMode) {\r
1076           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1077       }\r
1078   }\r
1079   \r
1080   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1081       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1082       darkBackTextureMode = appData.darkBackTextureMode;\r
1083 \r
1084       if (darkBackTexture == NULL && appData.debugMode) {\r
1085           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1086       }\r
1087   }\r
1088 \r
1089   mysrandom( (unsigned) time(NULL) );\r
1090 \r
1091   /* [AS] Restore layout */\r
1092   if( wpMoveHistory.visible ) {\r
1093       MoveHistoryPopUp();\r
1094   }\r
1095 \r
1096   if( wpEvalGraph.visible ) {\r
1097       EvalGraphPopUp();\r
1098   }\r
1099 \r
1100   if( wpEngineOutput.visible ) {\r
1101       EngineOutputPopUp();\r
1102   }\r
1103 \r
1104   /* Make the window visible; update its client area; and return "success" */\r
1105   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1106   wp.length = sizeof(WINDOWPLACEMENT);\r
1107   wp.flags = 0;\r
1108   wp.showCmd = nCmdShow;\r
1109   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1110   wp.rcNormalPosition.left = wpMain.x;\r
1111   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1112   wp.rcNormalPosition.top = wpMain.y;\r
1113   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1114   SetWindowPlacement(hwndMain, &wp);\r
1115 \r
1116   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1117 \r
1118   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1119                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1120 \r
1121   if (hwndConsole) {\r
1122 #if AOT_CONSOLE\r
1123     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1124                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1125 #endif\r
1126     ShowWindow(hwndConsole, nCmdShow);\r
1127     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
1128       char buf[MSG_SIZ], *p = buf, *q;\r
1129         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
1130       do {\r
1131         q = strchr(p, ';');\r
1132         if(q) *q++ = 0;\r
1133         if(*p) ChatPopUp(p);\r
1134       } while(p=q);\r
1135     }\r
1136     SetActiveWindow(hwndConsole);\r
1137   }\r
1138   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1139   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1140 \r
1141   return TRUE;\r
1142 \r
1143 }\r
1144 \r
1145 VOID\r
1146 InitMenuChecks()\r
1147 {\r
1148   HMENU hmenu = GetMenu(hwndMain);\r
1149 \r
1150   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1151                         MF_BYCOMMAND|((appData.icsActive &&\r
1152                                        *appData.icsCommPort != NULLCHAR) ?\r
1153                                       MF_ENABLED : MF_GRAYED));\r
1154   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1155                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1156                                      MF_CHECKED : MF_UNCHECKED));\r
1157 }\r
1158 \r
1159 //---------------------------------------------------------------------------------------------------------\r
1160 \r
1161 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1162 #define XBOARD FALSE\r
1163 \r
1164 #define OPTCHAR "/"\r
1165 #define SEPCHAR "="\r
1166 \r
1167 #include "args.h"\r
1168 \r
1169 // front-end part of option handling\r
1170 \r
1171 VOID\r
1172 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1173 {\r
1174   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1175   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1176   DeleteDC(hdc);\r
1177   lf->lfWidth = 0;\r
1178   lf->lfEscapement = 0;\r
1179   lf->lfOrientation = 0;\r
1180   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1181   lf->lfItalic = mfp->italic;\r
1182   lf->lfUnderline = mfp->underline;\r
1183   lf->lfStrikeOut = mfp->strikeout;\r
1184   lf->lfCharSet = mfp->charset;\r
1185   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1186   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1187   lf->lfQuality = DEFAULT_QUALITY;\r
1188   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1189     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1190 }\r
1191 \r
1192 void\r
1193 CreateFontInMF(MyFont *mf)\r
1194\r
1195   LFfromMFP(&mf->lf, &mf->mfp);\r
1196   if (mf->hf) DeleteObject(mf->hf);\r
1197   mf->hf = CreateFontIndirect(&mf->lf);\r
1198 }\r
1199 \r
1200 // [HGM] This platform-dependent table provides the location for storing the color info\r
1201 void *\r
1202 colorVariable[] = {\r
1203   &whitePieceColor, \r
1204   &blackPieceColor, \r
1205   &lightSquareColor,\r
1206   &darkSquareColor, \r
1207   &highlightSquareColor,\r
1208   &premoveHighlightColor,\r
1209   NULL,\r
1210   &consoleBackgroundColor,\r
1211   &appData.fontForeColorWhite,\r
1212   &appData.fontBackColorWhite,\r
1213   &appData.fontForeColorBlack,\r
1214   &appData.fontBackColorBlack,\r
1215   &appData.evalHistColorWhite,\r
1216   &appData.evalHistColorBlack,\r
1217   &appData.highlightArrowColor,\r
1218 };\r
1219 \r
1220 /* Command line font name parser.  NULL name means do nothing.\r
1221    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1222    For backward compatibility, syntax without the colon is also\r
1223    accepted, but font names with digits in them won't work in that case.\r
1224 */\r
1225 VOID\r
1226 ParseFontName(char *name, MyFontParams *mfp)\r
1227 {\r
1228   char *p, *q;\r
1229   if (name == NULL) return;\r
1230   p = name;\r
1231   q = strchr(p, ':');\r
1232   if (q) {\r
1233     if (q - p >= sizeof(mfp->faceName))\r
1234       ExitArgError(_("Font name too long:"), name);\r
1235     memcpy(mfp->faceName, p, q - p);\r
1236     mfp->faceName[q - p] = NULLCHAR;\r
1237     p = q + 1;\r
1238   } else {\r
1239     q = mfp->faceName;\r
1240     while (*p && !isdigit(*p)) {\r
1241       *q++ = *p++;\r
1242       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1243         ExitArgError(_("Font name too long:"), name);\r
1244     }\r
1245     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1246     *q = NULLCHAR;\r
1247   }\r
1248   if (!*p) ExitArgError(_("Font point size missing:"), name);\r
1249   mfp->pointSize = (float) atof(p);\r
1250   mfp->bold = (strchr(p, 'b') != NULL);\r
1251   mfp->italic = (strchr(p, 'i') != NULL);\r
1252   mfp->underline = (strchr(p, 'u') != NULL);\r
1253   mfp->strikeout = (strchr(p, 's') != NULL);\r
1254   mfp->charset = DEFAULT_CHARSET;\r
1255   q = strchr(p, 'c');\r
1256   if (q)\r
1257     mfp->charset = (BYTE) atoi(q+1);\r
1258 }\r
1259 \r
1260 void\r
1261 ParseFont(char *name, int number)\r
1262 { // wrapper to shield back-end from 'font'\r
1263   ParseFontName(name, &font[boardSize][number]->mfp);\r
1264 }\r
1265 \r
1266 void\r
1267 SetFontDefaults()\r
1268 { // in WB  we have a 2D array of fonts; this initializes their description\r
1269   int i, j;\r
1270   /* Point font array elements to structures and\r
1271      parse default font names */\r
1272   for (i=0; i<NUM_FONTS; i++) {\r
1273     for (j=0; j<NUM_SIZES; j++) {\r
1274       font[j][i] = &fontRec[j][i];\r
1275       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1276     }\r
1277   }\r
1278 }\r
1279 \r
1280 void\r
1281 CreateFonts()\r
1282 { // here we create the actual fonts from the selected descriptions\r
1283   int i, j;\r
1284   for (i=0; i<NUM_FONTS; i++) {\r
1285     for (j=0; j<NUM_SIZES; j++) {\r
1286       CreateFontInMF(font[j][i]);\r
1287     }\r
1288   }\r
1289 }\r
1290 /* Color name parser.\r
1291    X version accepts X color names, but this one\r
1292    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1293 COLORREF\r
1294 ParseColorName(char *name)\r
1295 {\r
1296   int red, green, blue, count;\r
1297   char buf[MSG_SIZ];\r
1298 \r
1299   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1300   if (count != 3) {\r
1301     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1302       &red, &green, &blue);\r
1303   }\r
1304   if (count != 3) {\r
1305     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1306     DisplayError(buf, 0);\r
1307     return RGB(0, 0, 0);\r
1308   }\r
1309   return PALETTERGB(red, green, blue);\r
1310 }\r
1311 \r
1312 void\r
1313 ParseColor(int n, char *name)\r
1314 { // for WinBoard the color is an int, which needs to be derived from the string\r
1315   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1316 }\r
1317 \r
1318 void\r
1319 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1320 {\r
1321   char *e = argValue;\r
1322   int eff = 0;\r
1323 \r
1324   while (*e) {\r
1325     if (*e == 'b')      eff |= CFE_BOLD;\r
1326     else if (*e == 'i') eff |= CFE_ITALIC;\r
1327     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1328     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1329     else if (*e == '#' || isdigit(*e)) break;\r
1330     e++;\r
1331   }\r
1332   *effects = eff;\r
1333   *color   = ParseColorName(e);\r
1334 }\r
1335 \r
1336 void\r
1337 ParseTextAttribs(ColorClass cc, char *s)\r
1338 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1339     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1340     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1341 }\r
1342 \r
1343 void\r
1344 ParseBoardSize(void *addr, char *name)\r
1345 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1346   BoardSize bs = SizeTiny;\r
1347   while (sizeInfo[bs].name != NULL) {\r
1348     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1349         *(BoardSize *)addr = bs;\r
1350         return;\r
1351     }\r
1352     bs++;\r
1353   }\r
1354   ExitArgError(_("Unrecognized board size value"), name);\r
1355 }\r
1356 \r
1357 void\r
1358 LoadAllSounds()\r
1359 { // [HGM] import name from appData first\r
1360   ColorClass cc;\r
1361   SoundClass sc;\r
1362   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1363     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1364     textAttribs[cc].sound.data = NULL;\r
1365     MyLoadSound(&textAttribs[cc].sound);\r
1366   }\r
1367   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1368     textAttribs[cc].sound.name = strdup("");\r
1369     textAttribs[cc].sound.data = NULL;\r
1370   }\r
1371   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1372     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1373     sounds[sc].data = NULL;\r
1374     MyLoadSound(&sounds[sc]);\r
1375   }\r
1376 }\r
1377 \r
1378 void\r
1379 SetCommPortDefaults()\r
1380 {\r
1381    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1382   dcb.DCBlength = sizeof(DCB);\r
1383   dcb.BaudRate = 9600;\r
1384   dcb.fBinary = TRUE;\r
1385   dcb.fParity = FALSE;\r
1386   dcb.fOutxCtsFlow = FALSE;\r
1387   dcb.fOutxDsrFlow = FALSE;\r
1388   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1389   dcb.fDsrSensitivity = FALSE;\r
1390   dcb.fTXContinueOnXoff = TRUE;\r
1391   dcb.fOutX = FALSE;\r
1392   dcb.fInX = FALSE;\r
1393   dcb.fNull = FALSE;\r
1394   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1395   dcb.fAbortOnError = FALSE;\r
1396   dcb.ByteSize = 7;\r
1397   dcb.Parity = SPACEPARITY;\r
1398   dcb.StopBits = ONESTOPBIT;\r
1399 }\r
1400 \r
1401 // [HGM] args: these three cases taken out to stay in front-end\r
1402 void\r
1403 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1404 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1405         // while the curent board size determines the element. This system should be ported to XBoard.\r
1406         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1407         int bs;\r
1408         for (bs=0; bs<NUM_SIZES; bs++) {\r
1409           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1410           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1411           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1412             ad->argName, mfp->faceName, mfp->pointSize,\r
1413             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1414             mfp->bold ? "b" : "",\r
1415             mfp->italic ? "i" : "",\r
1416             mfp->underline ? "u" : "",\r
1417             mfp->strikeout ? "s" : "",\r
1418             (int)mfp->charset);\r
1419         }\r
1420       }\r
1421 \r
1422 void\r
1423 ExportSounds()\r
1424 { // [HGM] copy the names from the internal WB variables to appData\r
1425   ColorClass cc;\r
1426   SoundClass sc;\r
1427   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1428     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1429   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1430     (&appData.soundMove)[sc] = sounds[sc].name;\r
1431 }\r
1432 \r
1433 void\r
1434 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1435 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1436         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1437         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1438           (ta->effects & CFE_BOLD) ? "b" : "",\r
1439           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1440           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1441           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1442           (ta->effects) ? " " : "",\r
1443           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1444       }\r
1445 \r
1446 void\r
1447 SaveColor(FILE *f, ArgDescriptor *ad)\r
1448 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1449         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1450         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1451           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1452 }\r
1453 \r
1454 void\r
1455 SaveBoardSize(FILE *f, char *name, void *addr)\r
1456 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1457   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1458 }\r
1459 \r
1460 void\r
1461 ParseCommPortSettings(char *s)\r
1462 { // wrapper to keep dcb from back-end\r
1463   ParseCommSettings(s, &dcb);\r
1464 }\r
1465 \r
1466 void\r
1467 GetWindowCoords()\r
1468 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1469   GetActualPlacement(hwndMain, &wpMain);\r
1470   GetActualPlacement(hwndConsole, &wpConsole);\r
1471   GetActualPlacement(commentDialog, &wpComment);\r
1472   GetActualPlacement(editTagsDialog, &wpTags);\r
1473   GetActualPlacement(gameListDialog, &wpGameList);\r
1474   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1475   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1476   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1477 }\r
1478 \r
1479 void\r
1480 PrintCommPortSettings(FILE *f, char *name)\r
1481 { // wrapper to shield back-end from DCB\r
1482       PrintCommSettings(f, name, &dcb);\r
1483 }\r
1484 \r
1485 int\r
1486 MySearchPath(char *installDir, char *name, char *fullname)\r
1487 {\r
1488   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1489   if(name[0]== '%') {\r
1490     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1491     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1492       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1493       *strchr(buf, '%') = 0;\r
1494       strcat(fullname, getenv(buf));\r
1495       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1496     }\r
1497     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1498     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1499     return (int) strlen(fullname);\r
1500   }\r
1501   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1502 }\r
1503 \r
1504 int\r
1505 MyGetFullPathName(char *name, char *fullname)\r
1506 {\r
1507   char *dummy;\r
1508   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1509 }\r
1510 \r
1511 int\r
1512 MainWindowUp()\r
1513 { // [HGM] args: allows testing if main window is realized from back-end\r
1514   return hwndMain != NULL;\r
1515 }\r
1516 \r
1517 void\r
1518 PopUpStartupDialog()\r
1519 {\r
1520     FARPROC lpProc;\r
1521     \r
1522     LoadLanguageFile(appData.language);\r
1523     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1524     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1525     FreeProcInstance(lpProc);\r
1526 }\r
1527 \r
1528 /*---------------------------------------------------------------------------*\\r
1529  *\r
1530  * GDI board drawing routines\r
1531  *\r
1532 \*---------------------------------------------------------------------------*/\r
1533 \r
1534 /* [AS] Draw square using background texture */\r
1535 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1536 {\r
1537     XFORM   x;\r
1538 \r
1539     if( mode == 0 ) {\r
1540         return; /* Should never happen! */\r
1541     }\r
1542 \r
1543     SetGraphicsMode( dst, GM_ADVANCED );\r
1544 \r
1545     switch( mode ) {\r
1546     case 1:\r
1547         /* Identity */\r
1548         break;\r
1549     case 2:\r
1550         /* X reflection */\r
1551         x.eM11 = -1.0;\r
1552         x.eM12 = 0;\r
1553         x.eM21 = 0;\r
1554         x.eM22 = 1.0;\r
1555         x.eDx = (FLOAT) dw + dx - 1;\r
1556         x.eDy = 0;\r
1557         dx = 0;\r
1558         SetWorldTransform( dst, &x );\r
1559         break;\r
1560     case 3:\r
1561         /* Y reflection */\r
1562         x.eM11 = 1.0;\r
1563         x.eM12 = 0;\r
1564         x.eM21 = 0;\r
1565         x.eM22 = -1.0;\r
1566         x.eDx = 0;\r
1567         x.eDy = (FLOAT) dh + dy - 1;\r
1568         dy = 0;\r
1569         SetWorldTransform( dst, &x );\r
1570         break;\r
1571     case 4:\r
1572         /* X/Y flip */\r
1573         x.eM11 = 0;\r
1574         x.eM12 = 1.0;\r
1575         x.eM21 = 1.0;\r
1576         x.eM22 = 0;\r
1577         x.eDx = (FLOAT) dx;\r
1578         x.eDy = (FLOAT) dy;\r
1579         dx = 0;\r
1580         dy = 0;\r
1581         SetWorldTransform( dst, &x );\r
1582         break;\r
1583     }\r
1584 \r
1585     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1586 \r
1587     x.eM11 = 1.0;\r
1588     x.eM12 = 0;\r
1589     x.eM21 = 0;\r
1590     x.eM22 = 1.0;\r
1591     x.eDx = 0;\r
1592     x.eDy = 0;\r
1593     SetWorldTransform( dst, &x );\r
1594 \r
1595     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1596 }\r
1597 \r
1598 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1599 enum {\r
1600     PM_WP = (int) WhitePawn, \r
1601     PM_WN = (int) WhiteKnight, \r
1602     PM_WB = (int) WhiteBishop, \r
1603     PM_WR = (int) WhiteRook, \r
1604     PM_WQ = (int) WhiteQueen, \r
1605     PM_WF = (int) WhiteFerz, \r
1606     PM_WW = (int) WhiteWazir, \r
1607     PM_WE = (int) WhiteAlfil, \r
1608     PM_WM = (int) WhiteMan, \r
1609     PM_WO = (int) WhiteCannon, \r
1610     PM_WU = (int) WhiteUnicorn, \r
1611     PM_WH = (int) WhiteNightrider, \r
1612     PM_WA = (int) WhiteAngel, \r
1613     PM_WC = (int) WhiteMarshall, \r
1614     PM_WAB = (int) WhiteCardinal, \r
1615     PM_WD = (int) WhiteDragon, \r
1616     PM_WL = (int) WhiteLance, \r
1617     PM_WS = (int) WhiteCobra, \r
1618     PM_WV = (int) WhiteFalcon, \r
1619     PM_WSG = (int) WhiteSilver, \r
1620     PM_WG = (int) WhiteGrasshopper, \r
1621     PM_WK = (int) WhiteKing,\r
1622     PM_BP = (int) BlackPawn, \r
1623     PM_BN = (int) BlackKnight, \r
1624     PM_BB = (int) BlackBishop, \r
1625     PM_BR = (int) BlackRook, \r
1626     PM_BQ = (int) BlackQueen, \r
1627     PM_BF = (int) BlackFerz, \r
1628     PM_BW = (int) BlackWazir, \r
1629     PM_BE = (int) BlackAlfil, \r
1630     PM_BM = (int) BlackMan,\r
1631     PM_BO = (int) BlackCannon, \r
1632     PM_BU = (int) BlackUnicorn, \r
1633     PM_BH = (int) BlackNightrider, \r
1634     PM_BA = (int) BlackAngel, \r
1635     PM_BC = (int) BlackMarshall, \r
1636     PM_BG = (int) BlackGrasshopper, \r
1637     PM_BAB = (int) BlackCardinal,\r
1638     PM_BD = (int) BlackDragon,\r
1639     PM_BL = (int) BlackLance,\r
1640     PM_BS = (int) BlackCobra,\r
1641     PM_BV = (int) BlackFalcon,\r
1642     PM_BSG = (int) BlackSilver,\r
1643     PM_BK = (int) BlackKing\r
1644 };\r
1645 \r
1646 static HFONT hPieceFont = NULL;\r
1647 static HBITMAP hPieceMask[(int) EmptySquare];\r
1648 static HBITMAP hPieceFace[(int) EmptySquare];\r
1649 static int fontBitmapSquareSize = 0;\r
1650 static char pieceToFontChar[(int) EmptySquare] =\r
1651                               { 'p', 'n', 'b', 'r', 'q', \r
1652                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1653                       'k', 'o', 'm', 'v', 't', 'w', \r
1654                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1655                                                               'l' };\r
1656 \r
1657 extern BOOL SetCharTable( char *table, const char * map );\r
1658 /* [HGM] moved to backend.c */\r
1659 \r
1660 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1661 {\r
1662     HBRUSH hbrush;\r
1663     BYTE r1 = GetRValue( color );\r
1664     BYTE g1 = GetGValue( color );\r
1665     BYTE b1 = GetBValue( color );\r
1666     BYTE r2 = r1 / 2;\r
1667     BYTE g2 = g1 / 2;\r
1668     BYTE b2 = b1 / 2;\r
1669     RECT rc;\r
1670 \r
1671     /* Create a uniform background first */\r
1672     hbrush = CreateSolidBrush( color );\r
1673     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1674     FillRect( hdc, &rc, hbrush );\r
1675     DeleteObject( hbrush );\r
1676     \r
1677     if( mode == 1 ) {\r
1678         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1679         int steps = squareSize / 2;\r
1680         int i;\r
1681 \r
1682         for( i=0; i<steps; i++ ) {\r
1683             BYTE r = r1 - (r1-r2) * i / steps;\r
1684             BYTE g = g1 - (g1-g2) * i / steps;\r
1685             BYTE b = b1 - (b1-b2) * i / steps;\r
1686 \r
1687             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1688             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1689             FillRect( hdc, &rc, hbrush );\r
1690             DeleteObject(hbrush);\r
1691         }\r
1692     }\r
1693     else if( mode == 2 ) {\r
1694         /* Diagonal gradient, good more or less for every piece */\r
1695         POINT triangle[3];\r
1696         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1697         HBRUSH hbrush_old;\r
1698         int steps = squareSize;\r
1699         int i;\r
1700 \r
1701         triangle[0].x = squareSize - steps;\r
1702         triangle[0].y = squareSize;\r
1703         triangle[1].x = squareSize;\r
1704         triangle[1].y = squareSize;\r
1705         triangle[2].x = squareSize;\r
1706         triangle[2].y = squareSize - steps;\r
1707 \r
1708         for( i=0; i<steps; i++ ) {\r
1709             BYTE r = r1 - (r1-r2) * i / steps;\r
1710             BYTE g = g1 - (g1-g2) * i / steps;\r
1711             BYTE b = b1 - (b1-b2) * i / steps;\r
1712 \r
1713             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1714             hbrush_old = SelectObject( hdc, hbrush );\r
1715             Polygon( hdc, triangle, 3 );\r
1716             SelectObject( hdc, hbrush_old );\r
1717             DeleteObject(hbrush);\r
1718             triangle[0].x++;\r
1719             triangle[2].y++;\r
1720         }\r
1721 \r
1722         SelectObject( hdc, hpen );\r
1723     }\r
1724 }\r
1725 \r
1726 /*\r
1727     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1728     seems to work ok. The main problem here is to find the "inside" of a chess\r
1729     piece: follow the steps as explained below.\r
1730 */\r
1731 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1732 {\r
1733     HBITMAP hbm;\r
1734     HBITMAP hbm_old;\r
1735     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1736     RECT rc;\r
1737     SIZE sz;\r
1738     POINT pt;\r
1739     int backColor = whitePieceColor; \r
1740     int foreColor = blackPieceColor;\r
1741     \r
1742     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1743         backColor = appData.fontBackColorWhite;\r
1744         foreColor = appData.fontForeColorWhite;\r
1745     }\r
1746     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1747         backColor = appData.fontBackColorBlack;\r
1748         foreColor = appData.fontForeColorBlack;\r
1749     }\r
1750 \r
1751     /* Mask */\r
1752     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1753 \r
1754     hbm_old = SelectObject( hdc, hbm );\r
1755 \r
1756     rc.left = 0;\r
1757     rc.top = 0;\r
1758     rc.right = squareSize;\r
1759     rc.bottom = squareSize;\r
1760 \r
1761     /* Step 1: background is now black */\r
1762     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1763 \r
1764     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1765 \r
1766     pt.x = (squareSize - sz.cx) / 2;\r
1767     pt.y = (squareSize - sz.cy) / 2;\r
1768 \r
1769     SetBkMode( hdc, TRANSPARENT );\r
1770     SetTextColor( hdc, chroma );\r
1771     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1772     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1773 \r
1774     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1775     /* Step 3: the area outside the piece is filled with white */\r
1776 //    FloodFill( hdc, 0, 0, chroma );\r
1777     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1778     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1779     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1780     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1781     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1782     /* \r
1783         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1784         but if the start point is not inside the piece we're lost!\r
1785         There should be a better way to do this... if we could create a region or path\r
1786         from the fill operation we would be fine for example.\r
1787     */\r
1788 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1789     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1790 \r
1791     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1792         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1793         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1794 \r
1795         SelectObject( dc2, bm2 );\r
1796         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1797         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1798         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1799         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1800         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1801 \r
1802         DeleteDC( dc2 );\r
1803         DeleteObject( bm2 );\r
1804     }\r
1805 \r
1806     SetTextColor( hdc, 0 );\r
1807     /* \r
1808         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1809         draw the piece again in black for safety.\r
1810     */\r
1811     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1812 \r
1813     SelectObject( hdc, hbm_old );\r
1814 \r
1815     if( hPieceMask[index] != NULL ) {\r
1816         DeleteObject( hPieceMask[index] );\r
1817     }\r
1818 \r
1819     hPieceMask[index] = hbm;\r
1820 \r
1821     /* Face */\r
1822     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1823 \r
1824     SelectObject( hdc, hbm );\r
1825 \r
1826     {\r
1827         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1828         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1829         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1830 \r
1831         SelectObject( dc1, hPieceMask[index] );\r
1832         SelectObject( dc2, bm2 );\r
1833         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1834         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1835         \r
1836         /* \r
1837             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1838             the piece background and deletes (makes transparent) the rest.\r
1839             Thanks to that mask, we are free to paint the background with the greates\r
1840             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1841             We use this, to make gradients and give the pieces a "roundish" look.\r
1842         */\r
1843         SetPieceBackground( hdc, backColor, 2 );\r
1844         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1845 \r
1846         DeleteDC( dc2 );\r
1847         DeleteDC( dc1 );\r
1848         DeleteObject( bm2 );\r
1849     }\r
1850 \r
1851     SetTextColor( hdc, foreColor );\r
1852     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1853 \r
1854     SelectObject( hdc, hbm_old );\r
1855 \r
1856     if( hPieceFace[index] != NULL ) {\r
1857         DeleteObject( hPieceFace[index] );\r
1858     }\r
1859 \r
1860     hPieceFace[index] = hbm;\r
1861 }\r
1862 \r
1863 static int TranslatePieceToFontPiece( int piece )\r
1864 {\r
1865     switch( piece ) {\r
1866     case BlackPawn:\r
1867         return PM_BP;\r
1868     case BlackKnight:\r
1869         return PM_BN;\r
1870     case BlackBishop:\r
1871         return PM_BB;\r
1872     case BlackRook:\r
1873         return PM_BR;\r
1874     case BlackQueen:\r
1875         return PM_BQ;\r
1876     case BlackKing:\r
1877         return PM_BK;\r
1878     case WhitePawn:\r
1879         return PM_WP;\r
1880     case WhiteKnight:\r
1881         return PM_WN;\r
1882     case WhiteBishop:\r
1883         return PM_WB;\r
1884     case WhiteRook:\r
1885         return PM_WR;\r
1886     case WhiteQueen:\r
1887         return PM_WQ;\r
1888     case WhiteKing:\r
1889         return PM_WK;\r
1890 \r
1891     case BlackAngel:\r
1892         return PM_BA;\r
1893     case BlackMarshall:\r
1894         return PM_BC;\r
1895     case BlackFerz:\r
1896         return PM_BF;\r
1897     case BlackNightrider:\r
1898         return PM_BH;\r
1899     case BlackAlfil:\r
1900         return PM_BE;\r
1901     case BlackWazir:\r
1902         return PM_BW;\r
1903     case BlackUnicorn:\r
1904         return PM_BU;\r
1905     case BlackCannon:\r
1906         return PM_BO;\r
1907     case BlackGrasshopper:\r
1908         return PM_BG;\r
1909     case BlackMan:\r
1910         return PM_BM;\r
1911     case BlackSilver:\r
1912         return PM_BSG;\r
1913     case BlackLance:\r
1914         return PM_BL;\r
1915     case BlackFalcon:\r
1916         return PM_BV;\r
1917     case BlackCobra:\r
1918         return PM_BS;\r
1919     case BlackCardinal:\r
1920         return PM_BAB;\r
1921     case BlackDragon:\r
1922         return PM_BD;\r
1923 \r
1924     case WhiteAngel:\r
1925         return PM_WA;\r
1926     case WhiteMarshall:\r
1927         return PM_WC;\r
1928     case WhiteFerz:\r
1929         return PM_WF;\r
1930     case WhiteNightrider:\r
1931         return PM_WH;\r
1932     case WhiteAlfil:\r
1933         return PM_WE;\r
1934     case WhiteWazir:\r
1935         return PM_WW;\r
1936     case WhiteUnicorn:\r
1937         return PM_WU;\r
1938     case WhiteCannon:\r
1939         return PM_WO;\r
1940     case WhiteGrasshopper:\r
1941         return PM_WG;\r
1942     case WhiteMan:\r
1943         return PM_WM;\r
1944     case WhiteSilver:\r
1945         return PM_WSG;\r
1946     case WhiteLance:\r
1947         return PM_WL;\r
1948     case WhiteFalcon:\r
1949         return PM_WV;\r
1950     case WhiteCobra:\r
1951         return PM_WS;\r
1952     case WhiteCardinal:\r
1953         return PM_WAB;\r
1954     case WhiteDragon:\r
1955         return PM_WD;\r
1956     }\r
1957 \r
1958     return 0;\r
1959 }\r
1960 \r
1961 void CreatePiecesFromFont()\r
1962 {\r
1963     LOGFONT lf;\r
1964     HDC hdc_window = NULL;\r
1965     HDC hdc = NULL;\r
1966     HFONT hfont_old;\r
1967     int fontHeight;\r
1968     int i;\r
1969 \r
1970     if( fontBitmapSquareSize < 0 ) {\r
1971         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1972         return;\r
1973     }\r
1974 \r
1975     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1976         fontBitmapSquareSize = -1;\r
1977         return;\r
1978     }\r
1979 \r
1980     if( fontBitmapSquareSize != squareSize ) {\r
1981         hdc_window = GetDC( hwndMain );\r
1982         hdc = CreateCompatibleDC( hdc_window );\r
1983 \r
1984         if( hPieceFont != NULL ) {\r
1985             DeleteObject( hPieceFont );\r
1986         }\r
1987         else {\r
1988             for( i=0; i<=(int)BlackKing; i++ ) {\r
1989                 hPieceMask[i] = NULL;\r
1990                 hPieceFace[i] = NULL;\r
1991             }\r
1992         }\r
1993 \r
1994         fontHeight = 75;\r
1995 \r
1996         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
1997             fontHeight = appData.fontPieceSize;\r
1998         }\r
1999 \r
2000         fontHeight = (fontHeight * squareSize) / 100;\r
2001 \r
2002         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2003         lf.lfWidth = 0;\r
2004         lf.lfEscapement = 0;\r
2005         lf.lfOrientation = 0;\r
2006         lf.lfWeight = FW_NORMAL;\r
2007         lf.lfItalic = 0;\r
2008         lf.lfUnderline = 0;\r
2009         lf.lfStrikeOut = 0;\r
2010         lf.lfCharSet = DEFAULT_CHARSET;\r
2011         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2012         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2013         lf.lfQuality = PROOF_QUALITY;\r
2014         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2015         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2016         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2017 \r
2018         hPieceFont = CreateFontIndirect( &lf );\r
2019 \r
2020         if( hPieceFont == NULL ) {\r
2021             fontBitmapSquareSize = -2;\r
2022         }\r
2023         else {\r
2024             /* Setup font-to-piece character table */\r
2025             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2026                 /* No (or wrong) global settings, try to detect the font */\r
2027                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2028                     /* Alpha */\r
2029                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2030                 }\r
2031                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2032                     /* DiagramTT* family */\r
2033                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2034                 }\r
2035                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2036                     /* Fairy symbols */\r
2037                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2038                 }\r
2039                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2040                     /* Good Companion (Some characters get warped as literal :-( */\r
2041                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2042                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2043                     SetCharTable(pieceToFontChar, s);\r
2044                 }\r
2045                 else {\r
2046                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2047                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2048                 }\r
2049             }\r
2050 \r
2051             /* Create bitmaps */\r
2052             hfont_old = SelectObject( hdc, hPieceFont );\r
2053             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2054                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2055                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2056 \r
2057             SelectObject( hdc, hfont_old );\r
2058 \r
2059             fontBitmapSquareSize = squareSize;\r
2060         }\r
2061     }\r
2062 \r
2063     if( hdc != NULL ) {\r
2064         DeleteDC( hdc );\r
2065     }\r
2066 \r
2067     if( hdc_window != NULL ) {\r
2068         ReleaseDC( hwndMain, hdc_window );\r
2069     }\r
2070 }\r
2071 \r
2072 HBITMAP\r
2073 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2074 {\r
2075   char name[128];\r
2076 \r
2077     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2078   if (gameInfo.event &&\r
2079       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2080       strcmp(name, "k80s") == 0) {\r
2081     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2082   }\r
2083   return LoadBitmap(hinst, name);\r
2084 }\r
2085 \r
2086 \r
2087 /* Insert a color into the program's logical palette\r
2088    structure.  This code assumes the given color is\r
2089    the result of the RGB or PALETTERGB macro, and it\r
2090    knows how those macros work (which is documented).\r
2091 */\r
2092 VOID\r
2093 InsertInPalette(COLORREF color)\r
2094 {\r
2095   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2096 \r
2097   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2098     DisplayFatalError(_("Too many colors"), 0, 1);\r
2099     pLogPal->palNumEntries--;\r
2100     return;\r
2101   }\r
2102 \r
2103   pe->peFlags = (char) 0;\r
2104   pe->peRed = (char) (0xFF & color);\r
2105   pe->peGreen = (char) (0xFF & (color >> 8));\r
2106   pe->peBlue = (char) (0xFF & (color >> 16));\r
2107   return;\r
2108 }\r
2109 \r
2110 \r
2111 VOID\r
2112 InitDrawingColors()\r
2113 {\r
2114   if (pLogPal == NULL) {\r
2115     /* Allocate enough memory for a logical palette with\r
2116      * PALETTESIZE entries and set the size and version fields\r
2117      * of the logical palette structure.\r
2118      */\r
2119     pLogPal = (NPLOGPALETTE)\r
2120       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2121                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2122     pLogPal->palVersion    = 0x300;\r
2123   }\r
2124   pLogPal->palNumEntries = 0;\r
2125 \r
2126   InsertInPalette(lightSquareColor);\r
2127   InsertInPalette(darkSquareColor);\r
2128   InsertInPalette(whitePieceColor);\r
2129   InsertInPalette(blackPieceColor);\r
2130   InsertInPalette(highlightSquareColor);\r
2131   InsertInPalette(premoveHighlightColor);\r
2132 \r
2133   /*  create a logical color palette according the information\r
2134    *  in the LOGPALETTE structure.\r
2135    */\r
2136   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2137 \r
2138   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2139   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2140   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2141   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2142   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2143   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2144   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2145   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2146   /* [AS] Force rendering of the font-based pieces */\r
2147   if( fontBitmapSquareSize > 0 ) {\r
2148     fontBitmapSquareSize = 0;\r
2149   }\r
2150 }\r
2151 \r
2152 \r
2153 int\r
2154 BoardWidth(int boardSize, int n)\r
2155 { /* [HGM] argument n added to allow different width and height */\r
2156   int lineGap = sizeInfo[boardSize].lineGap;\r
2157 \r
2158   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2159       lineGap = appData.overrideLineGap;\r
2160   }\r
2161 \r
2162   return (n + 1) * lineGap +\r
2163           n * sizeInfo[boardSize].squareSize;\r
2164 }\r
2165 \r
2166 /* Respond to board resize by dragging edge */\r
2167 VOID\r
2168 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2169 {\r
2170   BoardSize newSize = NUM_SIZES - 1;\r
2171   static int recurse = 0;\r
2172   if (IsIconic(hwndMain)) return;\r
2173   if (recurse > 0) return;\r
2174   recurse++;\r
2175   while (newSize > 0) {\r
2176         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2177         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2178            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2179     newSize--;\r
2180   } \r
2181   boardSize = newSize;\r
2182   InitDrawingSizes(boardSize, flags);\r
2183   recurse--;\r
2184 }\r
2185 \r
2186 \r
2187 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2188 \r
2189 VOID\r
2190 InitDrawingSizes(BoardSize boardSize, int flags)\r
2191 {\r
2192   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2193   ChessSquare piece;\r
2194   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2195   HDC hdc;\r
2196   SIZE clockSize, messageSize;\r
2197   HFONT oldFont;\r
2198   char buf[MSG_SIZ];\r
2199   char *str;\r
2200   HMENU hmenu = GetMenu(hwndMain);\r
2201   RECT crect, wrect, oldRect;\r
2202   int offby;\r
2203   LOGBRUSH logbrush;\r
2204 \r
2205   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2206   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2207 \r
2208   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2209   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2210 \r
2211   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2212   oldRect.top = wpMain.y;\r
2213   oldRect.right = wpMain.x + wpMain.width;\r
2214   oldRect.bottom = wpMain.y + wpMain.height;\r
2215 \r
2216   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2217   smallLayout = sizeInfo[boardSize].smallLayout;\r
2218   squareSize = sizeInfo[boardSize].squareSize;\r
2219   lineGap = sizeInfo[boardSize].lineGap;\r
2220   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2221 \r
2222   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2223       lineGap = appData.overrideLineGap;\r
2224   }\r
2225 \r
2226   if (tinyLayout != oldTinyLayout) {\r
2227     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2228     if (tinyLayout) {\r
2229       style &= ~WS_SYSMENU;\r
2230       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2231                  "&Minimize\tCtrl+F4");\r
2232     } else {\r
2233       style |= WS_SYSMENU;\r
2234       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2235     }\r
2236     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2237 \r
2238     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2239       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2240         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2241     }\r
2242     DrawMenuBar(hwndMain);\r
2243   }\r
2244 \r
2245   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2246   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2247 \r
2248   /* Get text area sizes */\r
2249   hdc = GetDC(hwndMain);\r
2250   if (appData.clockMode) {\r
2251     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2252   } else {\r
2253     snprintf(buf, MSG_SIZ, _("White"));\r
2254   }\r
2255   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2256   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2257   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2258   str = _("We only care about the height here");\r
2259   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2260   SelectObject(hdc, oldFont);\r
2261   ReleaseDC(hwndMain, hdc);\r
2262 \r
2263   /* Compute where everything goes */\r
2264   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2265         /* [HGM] logo: if either logo is on, reserve space for it */\r
2266         logoHeight =  2*clockSize.cy;\r
2267         leftLogoRect.left   = OUTER_MARGIN;\r
2268         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2269         leftLogoRect.top    = OUTER_MARGIN;\r
2270         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2271 \r
2272         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2273         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2274         rightLogoRect.top    = OUTER_MARGIN;\r
2275         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2276 \r
2277 \r
2278     whiteRect.left = leftLogoRect.right;\r
2279     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2280     whiteRect.top = OUTER_MARGIN;\r
2281     whiteRect.bottom = whiteRect.top + logoHeight;\r
2282 \r
2283     blackRect.right = rightLogoRect.left;\r
2284     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2285     blackRect.top = whiteRect.top;\r
2286     blackRect.bottom = whiteRect.bottom;\r
2287   } else {\r
2288     whiteRect.left = OUTER_MARGIN;\r
2289     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2290     whiteRect.top = OUTER_MARGIN;\r
2291     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2292 \r
2293     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2294     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2295     blackRect.top = whiteRect.top;\r
2296     blackRect.bottom = whiteRect.bottom;\r
2297 \r
2298     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2299   }\r
2300 \r
2301   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2302   if (appData.showButtonBar) {\r
2303     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2304       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2305   } else {\r
2306     messageRect.right = OUTER_MARGIN + boardWidth;\r
2307   }\r
2308   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2309   messageRect.bottom = messageRect.top + messageSize.cy;\r
2310 \r
2311   boardRect.left = OUTER_MARGIN;\r
2312   boardRect.right = boardRect.left + boardWidth;\r
2313   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2314   boardRect.bottom = boardRect.top + boardHeight;\r
2315 \r
2316   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2317   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2318   oldBoardSize = boardSize;\r
2319   oldTinyLayout = tinyLayout;\r
2320   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2321   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2322     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2323   winW *= 1 + twoBoards;\r
2324   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2325   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2326   wpMain.height = winH; //       without disturbing window attachments\r
2327   GetWindowRect(hwndMain, &wrect);\r
2328   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2329                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2330 \r
2331   // [HGM] placement: let attached windows follow size change.\r
2332   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2333   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2334   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2335   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2336   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2337 \r
2338   /* compensate if menu bar wrapped */\r
2339   GetClientRect(hwndMain, &crect);\r
2340   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2341   wpMain.height += offby;\r
2342   switch (flags) {\r
2343   case WMSZ_TOPLEFT:\r
2344     SetWindowPos(hwndMain, NULL, \r
2345                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2346                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2347     break;\r
2348 \r
2349   case WMSZ_TOPRIGHT:\r
2350   case WMSZ_TOP:\r
2351     SetWindowPos(hwndMain, NULL, \r
2352                  wrect.left, wrect.bottom - wpMain.height, \r
2353                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2354     break;\r
2355 \r
2356   case WMSZ_BOTTOMLEFT:\r
2357   case WMSZ_LEFT:\r
2358     SetWindowPos(hwndMain, NULL, \r
2359                  wrect.right - wpMain.width, wrect.top, \r
2360                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2361     break;\r
2362 \r
2363   case WMSZ_BOTTOMRIGHT:\r
2364   case WMSZ_BOTTOM:\r
2365   case WMSZ_RIGHT:\r
2366   default:\r
2367     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2368                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2369     break;\r
2370   }\r
2371 \r
2372   hwndPause = NULL;\r
2373   for (i = 0; i < N_BUTTONS; i++) {\r
2374     if (buttonDesc[i].hwnd != NULL) {\r
2375       DestroyWindow(buttonDesc[i].hwnd);\r
2376       buttonDesc[i].hwnd = NULL;\r
2377     }\r
2378     if (appData.showButtonBar) {\r
2379       buttonDesc[i].hwnd =\r
2380         CreateWindow("BUTTON", buttonDesc[i].label,\r
2381                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2382                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2383                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2384                      (HMENU) buttonDesc[i].id,\r
2385                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2386       if (tinyLayout) {\r
2387         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2388                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2389                     MAKELPARAM(FALSE, 0));\r
2390       }\r
2391       if (buttonDesc[i].id == IDM_Pause)\r
2392         hwndPause = buttonDesc[i].hwnd;\r
2393       buttonDesc[i].wndproc = (WNDPROC)\r
2394         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2395     }\r
2396   }\r
2397   if (gridPen != NULL) DeleteObject(gridPen);\r
2398   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2399   if (premovePen != NULL) DeleteObject(premovePen);\r
2400   if (lineGap != 0) {\r
2401     logbrush.lbStyle = BS_SOLID;\r
2402     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2403     gridPen =\r
2404       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2405                    lineGap, &logbrush, 0, NULL);\r
2406     logbrush.lbColor = highlightSquareColor;\r
2407     highlightPen =\r
2408       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2409                    lineGap, &logbrush, 0, NULL);\r
2410 \r
2411     logbrush.lbColor = premoveHighlightColor; \r
2412     premovePen =\r
2413       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2414                    lineGap, &logbrush, 0, NULL);\r
2415 \r
2416     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2417     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2418       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2419       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2420         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2421       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2422         BOARD_WIDTH * (squareSize + lineGap);\r
2423       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2424     }\r
2425     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2426       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2427       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2428         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2429         lineGap / 2 + (i * (squareSize + lineGap));\r
2430       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2431         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2432       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2433     }\r
2434   }\r
2435 \r
2436   /* [HGM] Licensing requirement */\r
2437 #ifdef GOTHIC\r
2438   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2439 #endif\r
2440 #ifdef FALCON\r
2441   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2442 #endif\r
2443   GothicPopUp( "", VariantNormal);\r
2444 \r
2445 \r
2446 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2447 \r
2448   /* Load piece bitmaps for this board size */\r
2449   for (i=0; i<=2; i++) {\r
2450     for (piece = WhitePawn;\r
2451          (int) piece < (int) BlackPawn;\r
2452          piece = (ChessSquare) ((int) piece + 1)) {\r
2453       if (pieceBitmap[i][piece] != NULL)\r
2454         DeleteObject(pieceBitmap[i][piece]);\r
2455     }\r
2456   }\r
2457 \r
2458   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2459   // Orthodox Chess pieces\r
2460   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2461   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2462   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2463   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2464   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2465   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2466   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2467   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2468   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2469   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2470   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2471   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2472   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2473   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2474   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2475   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2476     // in Shogi, Hijack the unused Queen for Lance\r
2477     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2478     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2479     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2480   } else {\r
2481     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2482     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2483     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2484   }\r
2485 \r
2486   if(squareSize <= 72 && squareSize >= 33) { \r
2487     /* A & C are available in most sizes now */\r
2488     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2489       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2490       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2491       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2492       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2493       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2494       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2495       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2496       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2497       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2498       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2499       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2500       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2501     } else { // Smirf-like\r
2502       if(gameInfo.variant == VariantSChess) {\r
2503         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2504         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2505         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2506       } else {\r
2507         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2508         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2509         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2510       }\r
2511     }\r
2512     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2513       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2514       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2515       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2516     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2517       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2518       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2519       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2520     } else { // WinBoard standard\r
2521       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2522       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2523       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2524     }\r
2525   }\r
2526 \r
2527 \r
2528   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2529     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2530     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2531     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2532     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2533     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2534     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2535     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2536     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2537     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2538     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2539     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2540     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2541     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2542     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2543     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2544     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2545     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2546     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2547     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2548     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2549     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2550     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2551     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2552     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2553     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2554     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2555     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2556     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2557     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2558     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2559 \r
2560     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2561       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2562       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2563       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2564       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2565       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2566       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2567       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2568       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2569       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2570       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2571       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2572       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2573     } else {\r
2574       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2575       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2576       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2577       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2578       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2579       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2580       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2581       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2582       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2583       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2584       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2585       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2586     }\r
2587 \r
2588   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2589     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2590     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2591     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2592     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2593     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2594     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2595     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2596     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2597     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2598     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2599     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2600     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2601     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2602     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2603   }\r
2604 \r
2605 \r
2606   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2607   /* special Shogi support in this size */\r
2608   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2609       for (piece = WhitePawn;\r
2610            (int) piece < (int) BlackPawn;\r
2611            piece = (ChessSquare) ((int) piece + 1)) {\r
2612         if (pieceBitmap[i][piece] != NULL)\r
2613           DeleteObject(pieceBitmap[i][piece]);\r
2614       }\r
2615     }\r
2616   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2617   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2618   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2619   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2620   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2621   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2622   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2623   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2624   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2625   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2626   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2627   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2628   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2629   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2630   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2631   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2632   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2633   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2634   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2635   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2636   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2637   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2638   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2639   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2640   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2641   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2642   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2643   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2644   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2645   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2646   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2647   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2648   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2649   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2650   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2651   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2652   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2653   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2654   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2655   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2656   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2657   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2658   minorSize = 0;\r
2659   }\r
2660 }\r
2661 \r
2662 HBITMAP\r
2663 PieceBitmap(ChessSquare p, int kind)\r
2664 {\r
2665   if ((int) p >= (int) BlackPawn)\r
2666     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2667 \r
2668   return pieceBitmap[kind][(int) p];\r
2669 }\r
2670 \r
2671 /***************************************************************/\r
2672 \r
2673 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2674 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2675 /*\r
2676 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2677 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2678 */\r
2679 \r
2680 VOID\r
2681 SquareToPos(int row, int column, int * x, int * y)\r
2682 {\r
2683   if (flipView) {\r
2684     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2685     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2686   } else {\r
2687     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2688     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2689   }\r
2690 }\r
2691 \r
2692 VOID\r
2693 DrawCoordsOnDC(HDC hdc)\r
2694 {\r
2695   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
2696   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
2697   char str[2] = { NULLCHAR, NULLCHAR };\r
2698   int oldMode, oldAlign, x, y, start, i;\r
2699   HFONT oldFont;\r
2700   HBRUSH oldBrush;\r
2701 \r
2702   if (!appData.showCoords)\r
2703     return;\r
2704 \r
2705   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2706 \r
2707   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2708   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2709   oldAlign = GetTextAlign(hdc);\r
2710   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2711 \r
2712   y = boardRect.top + lineGap;\r
2713   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2714 \r
2715   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2716   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2717     str[0] = files[start + i];\r
2718     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2719     y += squareSize + lineGap;\r
2720   }\r
2721 \r
2722   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2723 \r
2724   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2725   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2726     str[0] = ranks[start + i];\r
2727     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2728     x += squareSize + lineGap;\r
2729   }    \r
2730 \r
2731   SelectObject(hdc, oldBrush);\r
2732   SetBkMode(hdc, oldMode);\r
2733   SetTextAlign(hdc, oldAlign);\r
2734   SelectObject(hdc, oldFont);\r
2735 }\r
2736 \r
2737 VOID\r
2738 DrawGridOnDC(HDC hdc)\r
2739 {\r
2740   HPEN oldPen;\r
2741  \r
2742   if (lineGap != 0) {\r
2743     oldPen = SelectObject(hdc, gridPen);\r
2744     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2745     SelectObject(hdc, oldPen);\r
2746   }\r
2747 }\r
2748 \r
2749 #define HIGHLIGHT_PEN 0\r
2750 #define PREMOVE_PEN   1\r
2751 \r
2752 VOID\r
2753 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2754 {\r
2755   int x1, y1;\r
2756   HPEN oldPen, hPen;\r
2757   if (lineGap == 0) return;\r
2758   if (flipView) {\r
2759     x1 = boardRect.left +\r
2760       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2761     y1 = boardRect.top +\r
2762       lineGap/2 + y * (squareSize + lineGap);\r
2763   } else {\r
2764     x1 = boardRect.left +\r
2765       lineGap/2 + x * (squareSize + lineGap);\r
2766     y1 = boardRect.top +\r
2767       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2768   }\r
2769   hPen = pen ? premovePen : highlightPen;\r
2770   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2771   MoveToEx(hdc, x1, y1, NULL);\r
2772   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2773   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2774   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2775   LineTo(hdc, x1, y1);\r
2776   SelectObject(hdc, oldPen);\r
2777 }\r
2778 \r
2779 VOID\r
2780 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2781 {\r
2782   int i;\r
2783   for (i=0; i<2; i++) {\r
2784     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2785       DrawHighlightOnDC(hdc, TRUE,\r
2786                         h->sq[i].x, h->sq[i].y,\r
2787                         pen);\r
2788   }\r
2789 }\r
2790 \r
2791 /* Note: sqcolor is used only in monoMode */\r
2792 /* Note that this code is largely duplicated in woptions.c,\r
2793    function DrawSampleSquare, so that needs to be updated too */\r
2794 VOID\r
2795 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2796 {\r
2797   HBITMAP oldBitmap;\r
2798   HBRUSH oldBrush;\r
2799   int tmpSize;\r
2800 \r
2801   if (appData.blindfold) return;\r
2802 \r
2803   /* [AS] Use font-based pieces if needed */\r
2804   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2805     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2806     CreatePiecesFromFont();\r
2807 \r
2808     if( fontBitmapSquareSize == squareSize ) {\r
2809         int index = TranslatePieceToFontPiece(piece);\r
2810 \r
2811         SelectObject( tmphdc, hPieceMask[ index ] );\r
2812 \r
2813       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2814         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2815       else\r
2816         BitBlt( hdc,\r
2817             x, y,\r
2818             squareSize, squareSize,\r
2819             tmphdc,\r
2820             0, 0,\r
2821             SRCAND );\r
2822 \r
2823         SelectObject( tmphdc, hPieceFace[ index ] );\r
2824 \r
2825       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2826         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2827       else\r
2828         BitBlt( hdc,\r
2829             x, y,\r
2830             squareSize, squareSize,\r
2831             tmphdc,\r
2832             0, 0,\r
2833             SRCPAINT );\r
2834 \r
2835         return;\r
2836     }\r
2837   }\r
2838 \r
2839   if (appData.monoMode) {\r
2840     SelectObject(tmphdc, PieceBitmap(piece, \r
2841       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2842     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2843            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2844   } else {\r
2845     tmpSize = squareSize;\r
2846     if(minorSize &&\r
2847         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2848          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2849       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2850       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2851       x += (squareSize - minorSize)>>1;\r
2852       y += squareSize - minorSize - 2;\r
2853       tmpSize = minorSize;\r
2854     }\r
2855     if (color || appData.allWhite ) {\r
2856       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2857       if( color )\r
2858               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2859       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2860       if(appData.upsideDown && color==flipView)\r
2861         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2862       else\r
2863         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2864       /* Use black for outline of white pieces */\r
2865       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2866       if(appData.upsideDown && color==flipView)\r
2867         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2868       else\r
2869         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2870     } else {\r
2871       /* Use square color for details of black pieces */\r
2872       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2873       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2874       if(appData.upsideDown && !flipView)\r
2875         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2876       else\r
2877         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2878     }\r
2879     SelectObject(hdc, oldBrush);\r
2880     SelectObject(tmphdc, oldBitmap);\r
2881   }\r
2882 }\r
2883 \r
2884 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2885 int GetBackTextureMode( int algo )\r
2886 {\r
2887     int result = BACK_TEXTURE_MODE_DISABLED;\r
2888 \r
2889     switch( algo ) \r
2890     {\r
2891         case BACK_TEXTURE_MODE_PLAIN:\r
2892             result = 1; /* Always use identity map */\r
2893             break;\r
2894         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2895             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2896             break;\r
2897     }\r
2898 \r
2899     return result;\r
2900 }\r
2901 \r
2902 /* \r
2903     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2904     to handle redraws cleanly (as random numbers would always be different).\r
2905 */\r
2906 VOID RebuildTextureSquareInfo()\r
2907 {\r
2908     BITMAP bi;\r
2909     int lite_w = 0;\r
2910     int lite_h = 0;\r
2911     int dark_w = 0;\r
2912     int dark_h = 0;\r
2913     int row;\r
2914     int col;\r
2915 \r
2916     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2917 \r
2918     if( liteBackTexture != NULL ) {\r
2919         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2920             lite_w = bi.bmWidth;\r
2921             lite_h = bi.bmHeight;\r
2922         }\r
2923     }\r
2924 \r
2925     if( darkBackTexture != NULL ) {\r
2926         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2927             dark_w = bi.bmWidth;\r
2928             dark_h = bi.bmHeight;\r
2929         }\r
2930     }\r
2931 \r
2932     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2933         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2934             if( (col + row) & 1 ) {\r
2935                 /* Lite square */\r
2936                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2937                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2938                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2939                   else\r
2940                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2941                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2942                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2943                   else\r
2944                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2945                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2946                 }\r
2947             }\r
2948             else {\r
2949                 /* Dark square */\r
2950                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2951                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2952                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2953                   else\r
2954                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2955                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2956                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2957                   else\r
2958                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2959                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2960                 }\r
2961             }\r
2962         }\r
2963     }\r
2964 }\r
2965 \r
2966 /* [AS] Arrow highlighting support */\r
2967 \r
2968 static double A_WIDTH = 5; /* Width of arrow body */\r
2969 \r
2970 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2971 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2972 \r
2973 static double Sqr( double x )\r
2974 {\r
2975     return x*x;\r
2976 }\r
2977 \r
2978 static int Round( double x )\r
2979 {\r
2980     return (int) (x + 0.5);\r
2981 }\r
2982 \r
2983 /* Draw an arrow between two points using current settings */\r
2984 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2985 {\r
2986     POINT arrow[7];\r
2987     double dx, dy, j, k, x, y;\r
2988 \r
2989     if( d_x == s_x ) {\r
2990         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2991 \r
2992         arrow[0].x = s_x + A_WIDTH + 0.5;\r
2993         arrow[0].y = s_y;\r
2994 \r
2995         arrow[1].x = s_x + A_WIDTH + 0.5;\r
2996         arrow[1].y = d_y - h;\r
2997 \r
2998         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
2999         arrow[2].y = d_y - h;\r
3000 \r
3001         arrow[3].x = d_x;\r
3002         arrow[3].y = d_y;\r
3003 \r
3004         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3005         arrow[5].y = d_y - h;\r
3006 \r
3007         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3008         arrow[4].y = d_y - h;\r
3009 \r
3010         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3011         arrow[6].y = s_y;\r
3012     }\r
3013     else if( d_y == s_y ) {\r
3014         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3015 \r
3016         arrow[0].x = s_x;\r
3017         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3018 \r
3019         arrow[1].x = d_x - w;\r
3020         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3021 \r
3022         arrow[2].x = d_x - w;\r
3023         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3024 \r
3025         arrow[3].x = d_x;\r
3026         arrow[3].y = d_y;\r
3027 \r
3028         arrow[5].x = d_x - w;\r
3029         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3030 \r
3031         arrow[4].x = d_x - w;\r
3032         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3033 \r
3034         arrow[6].x = s_x;\r
3035         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3036     }\r
3037     else {\r
3038         /* [AS] Needed a lot of paper for this! :-) */\r
3039         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3040         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3041   \r
3042         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3043 \r
3044         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3045 \r
3046         x = s_x;\r
3047         y = s_y;\r
3048 \r
3049         arrow[0].x = Round(x - j);\r
3050         arrow[0].y = Round(y + j*dx);\r
3051 \r
3052         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3053         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3054 \r
3055         if( d_x > s_x ) {\r
3056             x = (double) d_x - k;\r
3057             y = (double) d_y - k*dy;\r
3058         }\r
3059         else {\r
3060             x = (double) d_x + k;\r
3061             y = (double) d_y + k*dy;\r
3062         }\r
3063 \r
3064         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3065 \r
3066         arrow[6].x = Round(x - j);\r
3067         arrow[6].y = Round(y + j*dx);\r
3068 \r
3069         arrow[2].x = Round(arrow[6].x + 2*j);\r
3070         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3071 \r
3072         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3073         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3074 \r
3075         arrow[4].x = d_x;\r
3076         arrow[4].y = d_y;\r
3077 \r
3078         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3079         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3080     }\r
3081 \r
3082     Polygon( hdc, arrow, 7 );\r
3083 }\r
3084 \r
3085 /* [AS] Draw an arrow between two squares */\r
3086 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3087 {\r
3088     int s_x, s_y, d_x, d_y;\r
3089     HPEN hpen;\r
3090     HPEN holdpen;\r
3091     HBRUSH hbrush;\r
3092     HBRUSH holdbrush;\r
3093     LOGBRUSH stLB;\r
3094 \r
3095     if( s_col == d_col && s_row == d_row ) {\r
3096         return;\r
3097     }\r
3098 \r
3099     /* Get source and destination points */\r
3100     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3101     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3102 \r
3103     if( d_y > s_y ) {\r
3104         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3105     }\r
3106     else if( d_y < s_y ) {\r
3107         d_y += squareSize / 2 + squareSize / 4;\r
3108     }\r
3109     else {\r
3110         d_y += squareSize / 2;\r
3111     }\r
3112 \r
3113     if( d_x > s_x ) {\r
3114         d_x += squareSize / 2 - squareSize / 4;\r
3115     }\r
3116     else if( d_x < s_x ) {\r
3117         d_x += squareSize / 2 + squareSize / 4;\r
3118     }\r
3119     else {\r
3120         d_x += squareSize / 2;\r
3121     }\r
3122 \r
3123     s_x += squareSize / 2;\r
3124     s_y += squareSize / 2;\r
3125 \r
3126     /* Adjust width */\r
3127     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3128 \r
3129     /* Draw */\r
3130     stLB.lbStyle = BS_SOLID;\r
3131     stLB.lbColor = appData.highlightArrowColor;\r
3132     stLB.lbHatch = 0;\r
3133 \r
3134     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3135     holdpen = SelectObject( hdc, hpen );\r
3136     hbrush = CreateBrushIndirect( &stLB );\r
3137     holdbrush = SelectObject( hdc, hbrush );\r
3138 \r
3139     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3140 \r
3141     SelectObject( hdc, holdpen );\r
3142     SelectObject( hdc, holdbrush );\r
3143     DeleteObject( hpen );\r
3144     DeleteObject( hbrush );\r
3145 }\r
3146 \r
3147 BOOL HasHighlightInfo()\r
3148 {\r
3149     BOOL result = FALSE;\r
3150 \r
3151     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3152         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3153     {\r
3154         result = TRUE;\r
3155     }\r
3156 \r
3157     return result;\r
3158 }\r
3159 \r
3160 BOOL IsDrawArrowEnabled()\r
3161 {\r
3162     BOOL result = FALSE;\r
3163 \r
3164     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3165         result = TRUE;\r
3166     }\r
3167 \r
3168     return result;\r
3169 }\r
3170 \r
3171 VOID DrawArrowHighlight( HDC hdc )\r
3172 {\r
3173     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3174         DrawArrowBetweenSquares( hdc,\r
3175             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3176             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3177     }\r
3178 }\r
3179 \r
3180 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3181 {\r
3182     HRGN result = NULL;\r
3183 \r
3184     if( HasHighlightInfo() ) {\r
3185         int x1, y1, x2, y2;\r
3186         int sx, sy, dx, dy;\r
3187 \r
3188         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3189         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3190 \r
3191         sx = MIN( x1, x2 );\r
3192         sy = MIN( y1, y2 );\r
3193         dx = MAX( x1, x2 ) + squareSize;\r
3194         dy = MAX( y1, y2 ) + squareSize;\r
3195 \r
3196         result = CreateRectRgn( sx, sy, dx, dy );\r
3197     }\r
3198 \r
3199     return result;\r
3200 }\r
3201 \r
3202 /*\r
3203     Warning: this function modifies the behavior of several other functions. \r
3204     \r
3205     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3206     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3207     repaint is scattered all over the place, which is not good for features such as\r
3208     "arrow highlighting" that require a full repaint of the board.\r
3209 \r
3210     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3211     user interaction, when speed is not so important) but especially to avoid errors\r
3212     in the displayed graphics.\r
3213 \r
3214     In such patched places, I always try refer to this function so there is a single\r
3215     place to maintain knowledge.\r
3216     \r
3217     To restore the original behavior, just return FALSE unconditionally.\r
3218 */\r
3219 BOOL IsFullRepaintPreferrable()\r
3220 {\r
3221     BOOL result = FALSE;\r
3222 \r
3223     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3224         /* Arrow may appear on the board */\r
3225         result = TRUE;\r
3226     }\r
3227 \r
3228     return result;\r
3229 }\r
3230 \r
3231 /* \r
3232     This function is called by DrawPosition to know whether a full repaint must\r
3233     be forced or not.\r
3234 \r
3235     Only DrawPosition may directly call this function, which makes use of \r
3236     some state information. Other function should call DrawPosition specifying \r
3237     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3238 */\r
3239 BOOL DrawPositionNeedsFullRepaint()\r
3240 {\r
3241     BOOL result = FALSE;\r
3242 \r
3243     /* \r
3244         Probably a slightly better policy would be to trigger a full repaint\r
3245         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3246         but animation is fast enough that it's difficult to notice.\r
3247     */\r
3248     if( animInfo.piece == EmptySquare ) {\r
3249         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3250             result = TRUE;\r
3251         }\r
3252     }\r
3253 \r
3254     return result;\r
3255 }\r
3256 \r
3257 VOID\r
3258 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3259 {\r
3260   int row, column, x, y, square_color, piece_color;\r
3261   ChessSquare piece;\r
3262   HBRUSH oldBrush;\r
3263   HDC texture_hdc = NULL;\r
3264 \r
3265   /* [AS] Initialize background textures if needed */\r
3266   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3267       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3268       if( backTextureSquareSize != squareSize \r
3269        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3270           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3271           backTextureSquareSize = squareSize;\r
3272           RebuildTextureSquareInfo();\r
3273       }\r
3274 \r
3275       texture_hdc = CreateCompatibleDC( hdc );\r
3276   }\r
3277 \r
3278   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3279     for (column = 0; column < BOARD_WIDTH; column++) {\r
3280   \r
3281       SquareToPos(row, column, &x, &y);\r
3282 \r
3283       piece = board[row][column];\r
3284 \r
3285       square_color = ((column + row) % 2) == 1;\r
3286       if( gameInfo.variant == VariantXiangqi ) {\r
3287           square_color = !InPalace(row, column);\r
3288           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3289           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3290       }\r
3291       piece_color = (int) piece < (int) BlackPawn;\r
3292 \r
3293 \r
3294       /* [HGM] holdings file: light square or black */\r
3295       if(column == BOARD_LEFT-2) {\r
3296             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3297                 square_color = 1;\r
3298             else {\r
3299                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3300                 continue;\r
3301             }\r
3302       } else\r
3303       if(column == BOARD_RGHT + 1 ) {\r
3304             if( row < gameInfo.holdingsSize )\r
3305                 square_color = 1;\r
3306             else {\r
3307                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3308                 continue;\r
3309             }\r
3310       }\r
3311       if(column == BOARD_LEFT-1 ) /* left align */\r
3312             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3313       else if( column == BOARD_RGHT) /* right align */\r
3314             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3315       else\r
3316       if (appData.monoMode) {\r
3317         if (piece == EmptySquare) {\r
3318           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3319                  square_color ? WHITENESS : BLACKNESS);\r
3320         } else {\r
3321           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3322         }\r
3323       } \r
3324       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3325           /* [AS] Draw the square using a texture bitmap */\r
3326           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3327           int r = row, c = column; // [HGM] do not flip board in flipView\r
3328           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3329 \r
3330           DrawTile( x, y, \r
3331               squareSize, squareSize, \r
3332               hdc, \r
3333               texture_hdc,\r
3334               backTextureSquareInfo[r][c].mode,\r
3335               backTextureSquareInfo[r][c].x,\r
3336               backTextureSquareInfo[r][c].y );\r
3337 \r
3338           SelectObject( texture_hdc, hbm );\r
3339 \r
3340           if (piece != EmptySquare) {\r
3341               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3342           }\r
3343       }\r
3344       else {\r
3345         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3346 \r
3347         oldBrush = SelectObject(hdc, brush );\r
3348         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3349         SelectObject(hdc, oldBrush);\r
3350         if (piece != EmptySquare)\r
3351           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3352       }\r
3353     }\r
3354   }\r
3355 \r
3356   if( texture_hdc != NULL ) {\r
3357     DeleteDC( texture_hdc );\r
3358   }\r
3359 }\r
3360 \r
3361 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3362 void fputDW(FILE *f, int x)\r
3363 {\r
3364         fputc(x     & 255, f);\r
3365         fputc(x>>8  & 255, f);\r
3366         fputc(x>>16 & 255, f);\r
3367         fputc(x>>24 & 255, f);\r
3368 }\r
3369 \r
3370 #define MAX_CLIPS 200   /* more than enough */\r
3371 \r
3372 VOID\r
3373 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3374 {\r
3375 //  HBITMAP bufferBitmap;\r
3376   BITMAP bi;\r
3377 //  RECT Rect;\r
3378   HDC tmphdc;\r
3379   HBITMAP hbm;\r
3380   int w = 100, h = 50;\r
3381 \r
3382   if(logo == NULL) return;\r
3383 //  GetClientRect(hwndMain, &Rect);\r
3384 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3385 //                                      Rect.bottom-Rect.top+1);\r
3386   tmphdc = CreateCompatibleDC(hdc);\r
3387   hbm = SelectObject(tmphdc, logo);\r
3388   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3389             w = bi.bmWidth;\r
3390             h = bi.bmHeight;\r
3391   }\r
3392   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3393                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3394   SelectObject(tmphdc, hbm);\r
3395   DeleteDC(tmphdc);\r
3396 }\r
3397 \r
3398 VOID\r
3399 DisplayLogos()\r
3400 {\r
3401   if(logoHeight) {\r
3402         HDC hdc = GetDC(hwndMain);\r
3403         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3404         if(appData.autoLogo) {\r
3405           \r
3406           switch(gameMode) { // pick logos based on game mode\r
3407             case IcsObserving:\r
3408                 whiteLogo = second.programLogo; // ICS logo\r
3409                 blackLogo = second.programLogo;\r
3410             default:\r
3411                 break;\r
3412             case IcsPlayingWhite:\r
3413                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3414                 blackLogo = second.programLogo; // ICS logo\r
3415                 break;\r
3416             case IcsPlayingBlack:\r
3417                 whiteLogo = second.programLogo; // ICS logo\r
3418                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3419                 break;\r
3420             case TwoMachinesPlay:\r
3421                 if(first.twoMachinesColor[0] == 'b') {\r
3422                     whiteLogo = second.programLogo;\r
3423                     blackLogo = first.programLogo;\r
3424                 }\r
3425                 break;\r
3426             case MachinePlaysWhite:\r
3427                 blackLogo = userLogo;\r
3428                 break;\r
3429             case MachinePlaysBlack:\r
3430                 whiteLogo = userLogo;\r
3431                 blackLogo = first.programLogo;\r
3432           }\r
3433         }\r
3434         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3435         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3436         ReleaseDC(hwndMain, hdc);\r
3437   }\r
3438 }\r
3439 \r
3440 static HDC hdcSeek;\r
3441 \r
3442 // [HGM] seekgraph\r
3443 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3444 {\r
3445     POINT stPt;\r
3446     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3447     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3448     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3449     SelectObject( hdcSeek, hp );\r
3450 }\r
3451 \r
3452 // front-end wrapper for drawing functions to do rectangles\r
3453 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3454 {\r
3455     HPEN hp;\r
3456     RECT rc;\r
3457 \r
3458     if (hdcSeek == NULL) {\r
3459     hdcSeek = GetDC(hwndMain);\r
3460       if (!appData.monoMode) {\r
3461         SelectPalette(hdcSeek, hPal, FALSE);\r
3462         RealizePalette(hdcSeek);\r
3463       }\r
3464     }\r
3465     hp = SelectObject( hdcSeek, gridPen );\r
3466     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3467     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3468     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3469     SelectObject( hdcSeek, hp );\r
3470 }\r
3471 \r
3472 // front-end wrapper for putting text in graph\r
3473 void DrawSeekText(char *buf, int x, int y)\r
3474 {\r
3475         SIZE stSize;\r
3476         SetBkMode( hdcSeek, TRANSPARENT );\r
3477         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3478         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3479 }\r
3480 \r
3481 void DrawSeekDot(int x, int y, int color)\r
3482 {\r
3483         int square = color & 0x80;\r
3484         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3485                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3486         color &= 0x7F;\r
3487         if(square)\r
3488             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3489                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3490         else\r
3491             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3492                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3493             SelectObject(hdcSeek, oldBrush);\r
3494 }\r
3495 \r
3496 VOID\r
3497 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3498 {\r
3499   static Board lastReq[2], lastDrawn[2];\r
3500   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3501   static int lastDrawnFlipView = 0;\r
3502   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3503   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3504   HDC tmphdc;\r
3505   HDC hdcmem;\r
3506   HBITMAP bufferBitmap;\r
3507   HBITMAP oldBitmap;\r
3508   RECT Rect;\r
3509   HRGN clips[MAX_CLIPS];\r
3510   ChessSquare dragged_piece = EmptySquare;\r
3511   int nr = twoBoards*partnerUp;\r
3512 \r
3513   /* I'm undecided on this - this function figures out whether a full\r
3514    * repaint is necessary on its own, so there's no real reason to have the\r
3515    * caller tell it that.  I think this can safely be set to FALSE - but\r
3516    * if we trust the callers not to request full repaints unnessesarily, then\r
3517    * we could skip some clipping work.  In other words, only request a full\r
3518    * redraw when the majority of pieces have changed positions (ie. flip, \r
3519    * gamestart and similar)  --Hawk\r
3520    */\r
3521   Boolean fullrepaint = repaint;\r
3522 \r
3523   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3524 \r
3525   if( DrawPositionNeedsFullRepaint() ) {\r
3526       fullrepaint = TRUE;\r
3527   }\r
3528 \r
3529   if (board == NULL) {\r
3530     if (!lastReqValid[nr]) {\r
3531       return;\r
3532     }\r
3533     board = lastReq[nr];\r
3534   } else {\r
3535     CopyBoard(lastReq[nr], board);\r
3536     lastReqValid[nr] = 1;\r
3537   }\r
3538 \r
3539   if (doingSizing) {\r
3540     return;\r
3541   }\r
3542 \r
3543   if (IsIconic(hwndMain)) {\r
3544     return;\r
3545   }\r
3546 \r
3547   if (hdc == NULL) {\r
3548     hdc = GetDC(hwndMain);\r
3549     if (!appData.monoMode) {\r
3550       SelectPalette(hdc, hPal, FALSE);\r
3551       RealizePalette(hdc);\r
3552     }\r
3553     releaseDC = TRUE;\r
3554   } else {\r
3555     releaseDC = FALSE;\r
3556   }\r
3557 \r
3558   /* Create some work-DCs */\r
3559   hdcmem = CreateCompatibleDC(hdc);\r
3560   tmphdc = CreateCompatibleDC(hdc);\r
3561 \r
3562   /* If dragging is in progress, we temporarely remove the piece */\r
3563   /* [HGM] or temporarily decrease count if stacked              */\r
3564   /*       !! Moved to before board compare !!                   */\r
3565   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3566     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3567     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3568             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3569         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3570     } else \r
3571     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3572             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3573         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3574     } else \r
3575         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3576   }\r
3577 \r
3578   /* Figure out which squares need updating by comparing the \r
3579    * newest board with the last drawn board and checking if\r
3580    * flipping has changed.\r
3581    */\r
3582   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3583     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3584       for (column = 0; column < BOARD_WIDTH; column++) {\r
3585         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3586           SquareToPos(row, column, &x, &y);\r
3587           clips[num_clips++] =\r
3588             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3589         }\r
3590       }\r
3591     }\r
3592    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3593     for (i=0; i<2; i++) {\r
3594       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3595           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3596         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3597             lastDrawnHighlight.sq[i].y >= 0) {\r
3598           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3599                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3600           clips[num_clips++] =\r
3601             CreateRectRgn(x - lineGap, y - lineGap, \r
3602                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3603         }\r
3604         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3605           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3606           clips[num_clips++] =\r
3607             CreateRectRgn(x - lineGap, y - lineGap, \r
3608                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3609         }\r
3610       }\r
3611     }\r
3612     for (i=0; i<2; i++) {\r
3613       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3614           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3615         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3616             lastDrawnPremove.sq[i].y >= 0) {\r
3617           SquareToPos(lastDrawnPremove.sq[i].y,\r
3618                       lastDrawnPremove.sq[i].x, &x, &y);\r
3619           clips[num_clips++] =\r
3620             CreateRectRgn(x - lineGap, y - lineGap, \r
3621                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3622         }\r
3623         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3624             premoveHighlightInfo.sq[i].y >= 0) {\r
3625           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3626                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3627           clips[num_clips++] =\r
3628             CreateRectRgn(x - lineGap, y - lineGap, \r
3629                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3630         }\r
3631       }\r
3632     }\r
3633    } else { // nr == 1\r
3634         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3635         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3636         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3637         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3638       for (i=0; i<2; i++) {\r
3639         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3640             partnerHighlightInfo.sq[i].y >= 0) {\r
3641           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3642                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3643           clips[num_clips++] =\r
3644             CreateRectRgn(x - lineGap, y - lineGap, \r
3645                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3646         }\r
3647         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3648             oldPartnerHighlight.sq[i].y >= 0) {\r
3649           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3650                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3651           clips[num_clips++] =\r
3652             CreateRectRgn(x - lineGap, y - lineGap, \r
3653                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3654         }\r
3655       }\r
3656    }\r
3657   } else {\r
3658     fullrepaint = TRUE;\r
3659   }\r
3660 \r
3661   /* Create a buffer bitmap - this is the actual bitmap\r
3662    * being written to.  When all the work is done, we can\r
3663    * copy it to the real DC (the screen).  This avoids\r
3664    * the problems with flickering.\r
3665    */\r
3666   GetClientRect(hwndMain, &Rect);\r
3667   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3668                                         Rect.bottom-Rect.top+1);\r
3669   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3670   if (!appData.monoMode) {\r
3671     SelectPalette(hdcmem, hPal, FALSE);\r
3672   }\r
3673 \r
3674   /* Create clips for dragging */\r
3675   if (!fullrepaint) {\r
3676     if (dragInfo.from.x >= 0) {\r
3677       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3678       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3679     }\r
3680     if (dragInfo.start.x >= 0) {\r
3681       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3682       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3683     }\r
3684     if (dragInfo.pos.x >= 0) {\r
3685       x = dragInfo.pos.x - squareSize / 2;\r
3686       y = dragInfo.pos.y - squareSize / 2;\r
3687       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3688     }\r
3689     if (dragInfo.lastpos.x >= 0) {\r
3690       x = dragInfo.lastpos.x - squareSize / 2;\r
3691       y = dragInfo.lastpos.y - squareSize / 2;\r
3692       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3693     }\r
3694   }\r
3695 \r
3696   /* Are we animating a move?  \r
3697    * If so, \r
3698    *   - remove the piece from the board (temporarely)\r
3699    *   - calculate the clipping region\r
3700    */\r
3701   if (!fullrepaint) {\r
3702     if (animInfo.piece != EmptySquare) {\r
3703       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3704       x = boardRect.left + animInfo.lastpos.x;\r
3705       y = boardRect.top + animInfo.lastpos.y;\r
3706       x2 = boardRect.left + animInfo.pos.x;\r
3707       y2 = boardRect.top + animInfo.pos.y;\r
3708       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3709       /* Slight kludge.  The real problem is that after AnimateMove is\r
3710          done, the position on the screen does not match lastDrawn.\r
3711          This currently causes trouble only on e.p. captures in\r
3712          atomic, where the piece moves to an empty square and then\r
3713          explodes.  The old and new positions both had an empty square\r
3714          at the destination, but animation has drawn a piece there and\r
3715          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3716       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3717     }\r
3718   }\r
3719 \r
3720   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3721   if (num_clips == 0)\r
3722     fullrepaint = TRUE;\r
3723 \r
3724   /* Set clipping on the memory DC */\r
3725   if (!fullrepaint) {\r
3726     SelectClipRgn(hdcmem, clips[0]);\r
3727     for (x = 1; x < num_clips; x++) {\r
3728       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3729         abort();  // this should never ever happen!\r
3730     }\r
3731   }\r
3732 \r
3733   /* Do all the drawing to the memory DC */\r
3734   if(explodeInfo.radius) { // [HGM] atomic\r
3735         HBRUSH oldBrush;\r
3736         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3737         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3738         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3739         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3740         x += squareSize/2;\r
3741         y += squareSize/2;\r
3742         if(!fullrepaint) {\r
3743           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3744           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3745         }\r
3746         DrawGridOnDC(hdcmem);\r
3747         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3748         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3749         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3750         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3751         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3752         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3753         SelectObject(hdcmem, oldBrush);\r
3754   } else {\r
3755     DrawGridOnDC(hdcmem);\r
3756     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3757         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3758         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3759     } else {\r
3760         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3761         oldPartnerHighlight = partnerHighlightInfo;\r
3762     }\r
3763     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3764   }\r
3765   if(nr == 0) // [HGM] dual: markers only on left board\r
3766   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3767     for (column = 0; column < BOARD_WIDTH; column++) {\r
3768         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3769             HBRUSH oldBrush = SelectObject(hdcmem, \r
3770                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3771             SquareToPos(row, column, &x, &y);\r
3772             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3773                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3774             SelectObject(hdcmem, oldBrush);\r
3775         }\r
3776     }\r
3777   }\r
3778 \r
3779   if( appData.highlightMoveWithArrow ) {\r
3780     DrawArrowHighlight(hdcmem);\r
3781   }\r
3782 \r
3783   DrawCoordsOnDC(hdcmem);\r
3784 \r
3785   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3786                  /* to make sure lastDrawn contains what is actually drawn */\r
3787 \r
3788   /* Put the dragged piece back into place and draw it (out of place!) */\r
3789     if (dragged_piece != EmptySquare) {\r
3790     /* [HGM] or restack */\r
3791     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3792                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3793     else\r
3794     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3795                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3796     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3797     x = dragInfo.pos.x - squareSize / 2;\r
3798     y = dragInfo.pos.y - squareSize / 2;\r
3799     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3800                   ((int) dragInfo.piece < (int) BlackPawn), \r
3801                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3802   }   \r
3803   \r
3804   /* Put the animated piece back into place and draw it */\r
3805   if (animInfo.piece != EmptySquare) {\r
3806     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3807     x = boardRect.left + animInfo.pos.x;\r
3808     y = boardRect.top + animInfo.pos.y;\r
3809     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3810                   ((int) animInfo.piece < (int) BlackPawn),\r
3811                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3812   }\r
3813 \r
3814   /* Release the bufferBitmap by selecting in the old bitmap \r
3815    * and delete the memory DC\r
3816    */\r
3817   SelectObject(hdcmem, oldBitmap);\r
3818   DeleteDC(hdcmem);\r
3819 \r
3820   /* Set clipping on the target DC */\r
3821   if (!fullrepaint) {\r
3822     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3823         RECT rect;\r
3824         GetRgnBox(clips[x], &rect);\r
3825         DeleteObject(clips[x]);\r
3826         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3827                           rect.right + wpMain.width/2, rect.bottom);\r
3828     }\r
3829     SelectClipRgn(hdc, clips[0]);\r
3830     for (x = 1; x < num_clips; x++) {\r
3831       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3832         abort();   // this should never ever happen!\r
3833     } \r
3834   }\r
3835 \r
3836   /* Copy the new bitmap onto the screen in one go.\r
3837    * This way we avoid any flickering\r
3838    */\r
3839   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3840   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3841          boardRect.right - boardRect.left,\r
3842          boardRect.bottom - boardRect.top,\r
3843          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3844   if(saveDiagFlag) { \r
3845     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3846     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3847 \r
3848     GetObject(bufferBitmap, sizeof(b), &b);\r
3849     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3850         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3851         bih.biWidth = b.bmWidth;\r
3852         bih.biHeight = b.bmHeight;\r
3853         bih.biPlanes = 1;\r
3854         bih.biBitCount = b.bmBitsPixel;\r
3855         bih.biCompression = 0;\r
3856         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3857         bih.biXPelsPerMeter = 0;\r
3858         bih.biYPelsPerMeter = 0;\r
3859         bih.biClrUsed = 0;\r
3860         bih.biClrImportant = 0;\r
3861 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3862 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3863         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3864 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3865 \r
3866         wb = b.bmWidthBytes;\r
3867         // count colors\r
3868         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3869                 int k = ((int*) pData)[i];\r
3870                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3871                 if(j >= 16) break;\r
3872                 color[j] = k;\r
3873                 if(j >= nrColors) nrColors = j+1;\r
3874         }\r
3875         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3876                 INT p = 0;\r
3877                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3878                     for(w=0; w<(wb>>2); w+=2) {\r
3879                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3880                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3881                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3882                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3883                         pData[p++] = m | j<<4;\r
3884                     }\r
3885                     while(p&3) pData[p++] = 0;\r
3886                 }\r
3887                 fac = 3;\r
3888                 wb = ((wb+31)>>5)<<2;\r
3889         }\r
3890         // write BITMAPFILEHEADER\r
3891         fprintf(diagFile, "BM");\r
3892         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3893         fputDW(diagFile, 0);\r
3894         fputDW(diagFile, 0x36 + (fac?64:0));\r
3895         // write BITMAPINFOHEADER\r
3896         fputDW(diagFile, 40);\r
3897         fputDW(diagFile, b.bmWidth);\r
3898         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3899         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3900         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3901         fputDW(diagFile, 0);\r
3902         fputDW(diagFile, 0);\r
3903         fputDW(diagFile, 0);\r
3904         fputDW(diagFile, 0);\r
3905         fputDW(diagFile, 0);\r
3906         fputDW(diagFile, 0);\r
3907         // write color table\r
3908         if(fac)\r
3909         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3910         // write bitmap data\r
3911         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3912                 fputc(pData[i], diagFile);\r
3913         free(pData);\r
3914      }\r
3915   }\r
3916 \r
3917   SelectObject(tmphdc, oldBitmap);\r
3918 \r
3919   /* Massive cleanup */\r
3920   for (x = 0; x < num_clips; x++)\r
3921     DeleteObject(clips[x]);\r
3922 \r
3923   DeleteDC(tmphdc);\r
3924   DeleteObject(bufferBitmap);\r
3925 \r
3926   if (releaseDC) \r
3927     ReleaseDC(hwndMain, hdc);\r
3928   \r
3929   if (lastDrawnFlipView != flipView && nr == 0) {\r
3930     if (flipView)\r
3931       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3932     else\r
3933       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3934   }\r
3935 \r
3936 /*  CopyBoard(lastDrawn, board);*/\r
3937   lastDrawnHighlight = highlightInfo;\r
3938   lastDrawnPremove   = premoveHighlightInfo;\r
3939   lastDrawnFlipView = flipView;\r
3940   lastDrawnValid[nr] = 1;\r
3941 }\r
3942 \r
3943 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3944 int\r
3945 SaveDiagram(f)\r
3946      FILE *f;\r
3947 {\r
3948     saveDiagFlag = 1; diagFile = f;\r
3949     HDCDrawPosition(NULL, TRUE, NULL);\r
3950     saveDiagFlag = 0;\r
3951 \r
3952     fclose(f);\r
3953     return TRUE;\r
3954 }\r
3955 \r
3956 \r
3957 /*---------------------------------------------------------------------------*\\r
3958 | CLIENT PAINT PROCEDURE\r
3959 |   This is the main event-handler for the WM_PAINT message.\r
3960 |\r
3961 \*---------------------------------------------------------------------------*/\r
3962 VOID\r
3963 PaintProc(HWND hwnd)\r
3964 {\r
3965   HDC         hdc;\r
3966   PAINTSTRUCT ps;\r
3967   HFONT       oldFont;\r
3968 \r
3969   if((hdc = BeginPaint(hwnd, &ps))) {\r
3970     if (IsIconic(hwnd)) {\r
3971       DrawIcon(hdc, 2, 2, iconCurrent);\r
3972     } else {\r
3973       if (!appData.monoMode) {\r
3974         SelectPalette(hdc, hPal, FALSE);\r
3975         RealizePalette(hdc);\r
3976       }\r
3977       HDCDrawPosition(hdc, 1, NULL);\r
3978       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3979         flipView = !flipView; partnerUp = !partnerUp;\r
3980         HDCDrawPosition(hdc, 1, NULL);\r
3981         flipView = !flipView; partnerUp = !partnerUp;\r
3982       }\r
3983       oldFont =\r
3984         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3985       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3986                  ETO_CLIPPED|ETO_OPAQUE,\r
3987                  &messageRect, messageText, strlen(messageText), NULL);\r
3988       SelectObject(hdc, oldFont);\r
3989       DisplayBothClocks();\r
3990       DisplayLogos();\r
3991     }\r
3992     EndPaint(hwnd,&ps);\r
3993   }\r
3994 \r
3995   return;\r
3996 }\r
3997 \r
3998 \r
3999 /*\r
4000  * If the user selects on a border boundary, return -1; if off the board,\r
4001  *   return -2.  Otherwise map the event coordinate to the square.\r
4002  * The offset boardRect.left or boardRect.top must already have been\r
4003  *   subtracted from x.\r
4004  */\r
4005 int EventToSquare(x, limit)\r
4006      int x, limit;\r
4007 {\r
4008   if (x <= 0)\r
4009     return -2;\r
4010   if (x < lineGap)\r
4011     return -1;\r
4012   x -= lineGap;\r
4013   if ((x % (squareSize + lineGap)) >= squareSize)\r
4014     return -1;\r
4015   x /= (squareSize + lineGap);\r
4016     if (x >= limit)\r
4017     return -2;\r
4018   return x;\r
4019 }\r
4020 \r
4021 typedef struct {\r
4022   char piece;\r
4023   int command;\r
4024   char* name;\r
4025 } DropEnable;\r
4026 \r
4027 DropEnable dropEnables[] = {\r
4028   { 'P', DP_Pawn, N_("Pawn") },\r
4029   { 'N', DP_Knight, N_("Knight") },\r
4030   { 'B', DP_Bishop, N_("Bishop") },\r
4031   { 'R', DP_Rook, N_("Rook") },\r
4032   { 'Q', DP_Queen, N_("Queen") },\r
4033 };\r
4034 \r
4035 VOID\r
4036 SetupDropMenu(HMENU hmenu)\r
4037 {\r
4038   int i, count, enable;\r
4039   char *p;\r
4040   extern char white_holding[], black_holding[];\r
4041   char item[MSG_SIZ];\r
4042 \r
4043   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4044     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4045                dropEnables[i].piece);\r
4046     count = 0;\r
4047     while (p && *p++ == dropEnables[i].piece) count++;\r
4048       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4049     enable = count > 0 || !appData.testLegality\r
4050       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4051                       && !appData.icsActive);\r
4052     ModifyMenu(hmenu, dropEnables[i].command,\r
4053                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4054                dropEnables[i].command, item);\r
4055   }\r
4056 }\r
4057 \r
4058 void DragPieceBegin(int x, int y)\r
4059 {\r
4060       dragInfo.lastpos.x = boardRect.left + x;\r
4061       dragInfo.lastpos.y = boardRect.top + y;\r
4062       dragInfo.from.x = fromX;\r
4063       dragInfo.from.y = fromY;\r
4064       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4065       dragInfo.start = dragInfo.from;\r
4066       SetCapture(hwndMain);\r
4067 }\r
4068 \r
4069 void DragPieceEnd(int x, int y)\r
4070 {\r
4071     ReleaseCapture();\r
4072     dragInfo.start.x = dragInfo.start.y = -1;\r
4073     dragInfo.from = dragInfo.start;\r
4074     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4075 }\r
4076 \r
4077 void ChangeDragPiece(ChessSquare piece)\r
4078 {\r
4079     dragInfo.piece = piece;\r
4080 }\r
4081 \r
4082 /* Event handler for mouse messages */\r
4083 VOID\r
4084 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4085 {\r
4086   int x, y, menuNr;\r
4087   POINT pt;\r
4088   static int recursive = 0;\r
4089   HMENU hmenu;\r
4090   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4091 \r
4092   if (recursive) {\r
4093     if (message == WM_MBUTTONUP) {\r
4094       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4095          to the middle button: we simulate pressing the left button too!\r
4096          */\r
4097       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4098       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4099     }\r
4100     return;\r
4101   }\r
4102   recursive++;\r
4103   \r
4104   pt.x = LOWORD(lParam);\r
4105   pt.y = HIWORD(lParam);\r
4106   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4107   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4108   if (!flipView && y >= 0) {\r
4109     y = BOARD_HEIGHT - 1 - y;\r
4110   }\r
4111   if (flipView && x >= 0) {\r
4112     x = BOARD_WIDTH - 1 - x;\r
4113   }\r
4114 \r
4115   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4116 \r
4117   switch (message) {\r
4118   case WM_LBUTTONDOWN:\r
4119       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4120         ClockClick(flipClock);\r
4121       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4122         ClockClick(!flipClock);\r
4123       }\r
4124       dragInfo.start.x = dragInfo.start.y = -1;\r
4125       dragInfo.from = dragInfo.start;\r
4126     if(fromX == -1 && frozen) { // not sure where this is for\r
4127                 fromX = fromY = -1; \r
4128       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4129       break;\r
4130     }\r
4131       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4132       DrawPosition(TRUE, NULL);\r
4133     break;\r
4134 \r
4135   case WM_LBUTTONUP:\r
4136       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4137       DrawPosition(TRUE, NULL);\r
4138     break;\r
4139 \r
4140   case WM_MOUSEMOVE:\r
4141     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4142     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4143     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4144     if ((appData.animateDragging || appData.highlightDragging)\r
4145         && (wParam & MK_LBUTTON)\r
4146         && dragInfo.from.x >= 0) \r
4147     {\r
4148       BOOL full_repaint = FALSE;\r
4149 \r
4150       if (appData.animateDragging) {\r
4151         dragInfo.pos = pt;\r
4152       }\r
4153       if (appData.highlightDragging) {\r
4154         SetHighlights(fromX, fromY, x, y);\r
4155         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4156             full_repaint = TRUE;\r
4157         }\r
4158       }\r
4159       \r
4160       DrawPosition( full_repaint, NULL);\r
4161       \r
4162       dragInfo.lastpos = dragInfo.pos;\r
4163     }\r
4164     break;\r
4165 \r
4166   case WM_MOUSEWHEEL: // [DM]\r
4167     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4168        /* Mouse Wheel is being rolled forward\r
4169         * Play moves forward\r
4170         */\r
4171        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4172                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4173        /* Mouse Wheel is being rolled backward\r
4174         * Play moves backward\r
4175         */\r
4176        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4177                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4178     }\r
4179     break;\r
4180 \r
4181   case WM_MBUTTONUP:\r
4182   case WM_RBUTTONUP:\r
4183     ReleaseCapture();\r
4184     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4185     break;\r
4186  \r
4187   case WM_MBUTTONDOWN:\r
4188   case WM_RBUTTONDOWN:\r
4189     ErrorPopDown();\r
4190     ReleaseCapture();\r
4191     fromX = fromY = -1;\r
4192     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4193     dragInfo.start.x = dragInfo.start.y = -1;\r
4194     dragInfo.from = dragInfo.start;\r
4195     dragInfo.lastpos = dragInfo.pos;\r
4196     if (appData.highlightDragging) {\r
4197       ClearHighlights();\r
4198     }\r
4199     if(y == -2) {\r
4200       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4201       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4202           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4203       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4204           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4205       }\r
4206       break;\r
4207     }\r
4208     DrawPosition(TRUE, NULL);\r
4209 \r
4210     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4211     switch (menuNr) {\r
4212     case 0:\r
4213       if (message == WM_MBUTTONDOWN) {\r
4214         buttonCount = 3;  /* even if system didn't think so */\r
4215         if (wParam & MK_SHIFT) \r
4216           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4217         else\r
4218           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4219       } else { /* message == WM_RBUTTONDOWN */\r
4220         /* Just have one menu, on the right button.  Windows users don't\r
4221            think to try the middle one, and sometimes other software steals\r
4222            it, or it doesn't really exist. */\r
4223         if(gameInfo.variant != VariantShogi)\r
4224             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4225         else\r
4226             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4227       }\r
4228       break;\r
4229     case 2:\r
4230       SetCapture(hwndMain);
4231       break;\r
4232     case 1:\r
4233       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4234       SetupDropMenu(hmenu);\r
4235       MenuPopup(hwnd, pt, hmenu, -1);\r
4236     default:\r
4237       break;\r
4238     }\r
4239     break;\r
4240   }\r
4241 \r
4242   recursive--;\r
4243 }\r
4244 \r
4245 /* Preprocess messages for buttons in main window */\r
4246 LRESULT CALLBACK\r
4247 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4248 {\r
4249   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4250   int i, dir;\r
4251 \r
4252   for (i=0; i<N_BUTTONS; i++) {\r
4253     if (buttonDesc[i].id == id) break;\r
4254   }\r
4255   if (i == N_BUTTONS) return 0;\r
4256   switch (message) {\r
4257   case WM_KEYDOWN:\r
4258     switch (wParam) {\r
4259     case VK_LEFT:\r
4260     case VK_RIGHT:\r
4261       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4262       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4263       return TRUE;\r
4264     }\r
4265     break;\r
4266   case WM_CHAR:\r
4267     switch (wParam) {\r
4268     case '\r':\r
4269       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4270       return TRUE;\r
4271     default:\r
4272       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4273         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4274         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4275         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4276         SetFocus(h);\r
4277         SendMessage(h, WM_CHAR, wParam, lParam);\r
4278         return TRUE;\r
4279       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4280         TypeInEvent((char)wParam);\r
4281       }\r
4282       break;\r
4283     }\r
4284     break;\r
4285   }\r
4286   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4287 }\r
4288 \r
4289 /* Process messages for Promotion dialog box */\r
4290 LRESULT CALLBACK\r
4291 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4292 {\r
4293   char promoChar;\r
4294 \r
4295   switch (message) {\r
4296   case WM_INITDIALOG: /* message: initialize dialog box */\r
4297     /* Center the dialog over the application window */\r
4298     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4299     Translate(hDlg, DLG_PromotionKing);\r
4300     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4301       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4302        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4303        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4304                SW_SHOW : SW_HIDE);\r
4305     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4306     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4307        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4308          PieceToChar(WhiteAngel) != '~') ||\r
4309         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4310          PieceToChar(BlackAngel) != '~')   ) ?\r
4311                SW_SHOW : SW_HIDE);\r
4312     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4313        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4314          PieceToChar(WhiteMarshall) != '~') ||\r
4315         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4316          PieceToChar(BlackMarshall) != '~')   ) ?\r
4317                SW_SHOW : SW_HIDE);\r
4318     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4319     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4320        gameInfo.variant != VariantShogi ?\r
4321                SW_SHOW : SW_HIDE);\r
4322     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4323        gameInfo.variant != VariantShogi ?\r
4324                SW_SHOW : SW_HIDE);\r
4325     if(gameInfo.variant == VariantShogi) {\r
4326         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4327         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4328         SetWindowText(hDlg, "Promote?");\r
4329     }\r
4330     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4331        gameInfo.variant == VariantSuper ?\r
4332                SW_SHOW : SW_HIDE);\r
4333     return TRUE;\r
4334 \r
4335   case WM_COMMAND: /* message: received a command */\r
4336     switch (LOWORD(wParam)) {\r
4337     case IDCANCEL:\r
4338       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4339       ClearHighlights();\r
4340       DrawPosition(FALSE, NULL);\r
4341       return TRUE;\r
4342     case PB_King:\r
4343       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4344       break;\r
4345     case PB_Queen:\r
4346       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4347       break;\r
4348     case PB_Rook:\r
4349       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4350       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4351       break;\r
4352     case PB_Bishop:\r
4353       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4354       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4355       break;\r
4356     case PB_Chancellor:\r
4357       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4358       break;\r
4359     case PB_Archbishop:\r
4360       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4361       break;\r
4362     case PB_Knight:\r
4363       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4364       break;\r
4365     default:\r
4366       return FALSE;\r
4367     }\r
4368     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4369     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4370     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4371     fromX = fromY = -1;\r
4372     if (!appData.highlightLastMove) {\r
4373       ClearHighlights();\r
4374       DrawPosition(FALSE, NULL);\r
4375     }\r
4376     return TRUE;\r
4377   }\r
4378   return FALSE;\r
4379 }\r
4380 \r
4381 /* Pop up promotion dialog */\r
4382 VOID\r
4383 PromotionPopup(HWND hwnd)\r
4384 {\r
4385   FARPROC lpProc;\r
4386 \r
4387   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4388   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4389     hwnd, (DLGPROC)lpProc);\r
4390   FreeProcInstance(lpProc);\r
4391 }\r
4392 \r
4393 void\r
4394 PromotionPopUp()\r
4395 {\r
4396   DrawPosition(TRUE, NULL);\r
4397   PromotionPopup(hwndMain);\r
4398 }\r
4399 \r
4400 /* Toggle ShowThinking */\r
4401 VOID\r
4402 ToggleShowThinking()\r
4403 {\r
4404   appData.showThinking = !appData.showThinking;\r
4405   ShowThinkingEvent();\r
4406 }\r
4407 \r
4408 VOID\r
4409 LoadGameDialog(HWND hwnd, char* title)\r
4410 {\r
4411   UINT number = 0;\r
4412   FILE *f;\r
4413   char fileTitle[MSG_SIZ];\r
4414   f = OpenFileDialog(hwnd, "rb", "",\r
4415                      appData.oldSaveStyle ? "gam" : "pgn",\r
4416                      GAME_FILT,\r
4417                      title, &number, fileTitle, NULL);\r
4418   if (f != NULL) {\r
4419     cmailMsgLoaded = FALSE;\r
4420     if (number == 0) {\r
4421       int error = GameListBuild(f);\r
4422       if (error) {\r
4423         DisplayError(_("Cannot build game list"), error);\r
4424       } else if (!ListEmpty(&gameList) &&\r
4425                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4426         GameListPopUp(f, fileTitle);\r
4427         return;\r
4428       }\r
4429       GameListDestroy();\r
4430       number = 1;\r
4431     }\r
4432     LoadGame(f, number, fileTitle, FALSE);\r
4433   }\r
4434 }\r
4435 \r
4436 int get_term_width()\r
4437 {\r
4438     HDC hdc;\r
4439     TEXTMETRIC tm;\r
4440     RECT rc;\r
4441     HFONT hfont, hold_font;\r
4442     LOGFONT lf;\r
4443     HWND hText;\r
4444 \r
4445     if (hwndConsole)\r
4446         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4447     else\r
4448         return 79;\r
4449 \r
4450     // get the text metrics\r
4451     hdc = GetDC(hText);\r
4452     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4453     if (consoleCF.dwEffects & CFE_BOLD)\r
4454         lf.lfWeight = FW_BOLD;\r
4455     if (consoleCF.dwEffects & CFE_ITALIC)\r
4456         lf.lfItalic = TRUE;\r
4457     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4458         lf.lfStrikeOut = TRUE;\r
4459     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4460         lf.lfUnderline = TRUE;\r
4461     hfont = CreateFontIndirect(&lf);\r
4462     hold_font = SelectObject(hdc, hfont);\r
4463     GetTextMetrics(hdc, &tm);\r
4464     SelectObject(hdc, hold_font);\r
4465     DeleteObject(hfont);\r
4466     ReleaseDC(hText, hdc);\r
4467 \r
4468     // get the rectangle\r
4469     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4470 \r
4471     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4472 }\r
4473 \r
4474 void UpdateICSWidth(HWND hText)\r
4475 {\r
4476     LONG old_width, new_width;\r
4477 \r
4478     new_width = get_term_width(hText, FALSE);\r
4479     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4480     if (new_width != old_width)\r
4481     {\r
4482         ics_update_width(new_width);\r
4483         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4484     }\r
4485 }\r
4486 \r
4487 VOID\r
4488 ChangedConsoleFont()\r
4489 {\r
4490   CHARFORMAT cfmt;\r
4491   CHARRANGE tmpsel, sel;\r
4492   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4493   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4494   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4495   PARAFORMAT paraf;\r
4496 \r
4497   cfmt.cbSize = sizeof(CHARFORMAT);\r
4498   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4499     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4500                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4501   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4502    * size.  This was undocumented in the version of MSVC++ that I had\r
4503    * when I wrote the code, but is apparently documented now.\r
4504    */\r
4505   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4506   cfmt.bCharSet = f->lf.lfCharSet;\r
4507   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4508   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4509   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4510   /* Why are the following seemingly needed too? */\r
4511   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4512   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4513   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4514   tmpsel.cpMin = 0;\r
4515   tmpsel.cpMax = -1; /*999999?*/\r
4516   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4517   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4518   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4519    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4520    */\r
4521   paraf.cbSize = sizeof(paraf);\r
4522   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4523   paraf.dxStartIndent = 0;\r
4524   paraf.dxOffset = WRAP_INDENT;\r
4525   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4526   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4527   UpdateICSWidth(hText);\r
4528 }\r
4529 \r
4530 /*---------------------------------------------------------------------------*\\r
4531  *\r
4532  * Window Proc for main window\r
4533  *\r
4534 \*---------------------------------------------------------------------------*/\r
4535 \r
4536 /* Process messages for main window, etc. */\r
4537 LRESULT CALLBACK\r
4538 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4539 {\r
4540   FARPROC lpProc;\r
4541   int wmId, wmEvent;\r
4542   char *defName;\r
4543   FILE *f;\r
4544   UINT number;\r
4545   char fileTitle[MSG_SIZ];\r
4546   char buf[MSG_SIZ];\r
4547   static SnapData sd;\r
4548 \r
4549   switch (message) {\r
4550 \r
4551   case WM_PAINT: /* message: repaint portion of window */\r
4552     PaintProc(hwnd);\r
4553     break;\r
4554 \r
4555   case WM_ERASEBKGND:\r
4556     if (IsIconic(hwnd)) {\r
4557       /* Cheat; change the message */\r
4558       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4559     } else {\r
4560       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4561     }\r
4562     break;\r
4563 \r
4564   case WM_LBUTTONDOWN:\r
4565   case WM_MBUTTONDOWN:\r
4566   case WM_RBUTTONDOWN:\r
4567   case WM_LBUTTONUP:\r
4568   case WM_MBUTTONUP:\r
4569   case WM_RBUTTONUP:\r
4570   case WM_MOUSEMOVE:\r
4571   case WM_MOUSEWHEEL:\r
4572     MouseEvent(hwnd, message, wParam, lParam);\r
4573     break;\r
4574 \r
4575   JAWS_KB_NAVIGATION\r
4576 \r
4577   case WM_CHAR:\r
4578     \r
4579     JAWS_ALT_INTERCEPT\r
4580 \r
4581     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4582         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4583         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4584         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4585         SetFocus(h);\r
4586         SendMessage(h, message, wParam, lParam);\r
4587     } else if(lParam != KF_REPEAT) {\r
4588         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4589                 TypeInEvent((char)wParam);\r
4590         } else if((char)wParam == 003) CopyGameToClipboard();\r
4591          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4592     }\r
4593 \r
4594     break;\r
4595 \r
4596   case WM_PALETTECHANGED:\r
4597     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4598       int nnew;\r
4599       HDC hdc = GetDC(hwndMain);\r
4600       SelectPalette(hdc, hPal, TRUE);\r
4601       nnew = RealizePalette(hdc);\r
4602       if (nnew > 0) {\r
4603         paletteChanged = TRUE;\r
4604         InvalidateRect(hwnd, &boardRect, FALSE);\r
4605       }\r
4606       ReleaseDC(hwnd, hdc);\r
4607     }\r
4608     break;\r
4609 \r
4610   case WM_QUERYNEWPALETTE:\r
4611     if (!appData.monoMode /*&& paletteChanged*/) {\r
4612       int nnew;\r
4613       HDC hdc = GetDC(hwndMain);\r
4614       paletteChanged = FALSE;\r
4615       SelectPalette(hdc, hPal, FALSE);\r
4616       nnew = RealizePalette(hdc);\r
4617       if (nnew > 0) {\r
4618         InvalidateRect(hwnd, &boardRect, FALSE);\r
4619       }\r
4620       ReleaseDC(hwnd, hdc);\r
4621       return TRUE;\r
4622     }\r
4623     return FALSE;\r
4624 \r
4625   case WM_COMMAND: /* message: command from application menu */\r
4626     wmId    = LOWORD(wParam);\r
4627     wmEvent = HIWORD(wParam);\r
4628 \r
4629     switch (wmId) {\r
4630     case IDM_NewGame:\r
4631       ResetGameEvent();\r
4632       SAY("new game enter a move to play against the computer with white");\r
4633       break;\r
4634 \r
4635     case IDM_NewGameFRC:\r
4636       if( NewGameFRC() == 0 ) {\r
4637         ResetGameEvent();\r
4638       }\r
4639       break;\r
4640 \r
4641     case IDM_NewVariant:\r
4642       NewVariantPopup(hwnd);\r
4643       break;\r
4644 \r
4645     case IDM_LoadGame:\r
4646       LoadGameDialog(hwnd, _("Load Game from File"));\r
4647       break;\r
4648 \r
4649     case IDM_LoadNextGame:\r
4650       ReloadGame(1);\r
4651       break;\r
4652 \r
4653     case IDM_LoadPrevGame:\r
4654       ReloadGame(-1);\r
4655       break;\r
4656 \r
4657     case IDM_ReloadGame:\r
4658       ReloadGame(0);\r
4659       break;\r
4660 \r
4661     case IDM_LoadPosition:\r
4662       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4663         Reset(FALSE, TRUE);\r
4664       }\r
4665       number = 1;\r
4666       f = OpenFileDialog(hwnd, "rb", "",\r
4667                          appData.oldSaveStyle ? "pos" : "fen",\r
4668                          POSITION_FILT,\r
4669                          _("Load Position from File"), &number, fileTitle, NULL);\r
4670       if (f != NULL) {\r
4671         LoadPosition(f, number, fileTitle);\r
4672       }\r
4673       break;\r
4674 \r
4675     case IDM_LoadNextPosition:\r
4676       ReloadPosition(1);\r
4677       break;\r
4678 \r
4679     case IDM_LoadPrevPosition:\r
4680       ReloadPosition(-1);\r
4681       break;\r
4682 \r
4683     case IDM_ReloadPosition:\r
4684       ReloadPosition(0);\r
4685       break;\r
4686 \r
4687     case IDM_SaveGame:\r
4688       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4689       f = OpenFileDialog(hwnd, "a", defName,\r
4690                          appData.oldSaveStyle ? "gam" : "pgn",\r
4691                          GAME_FILT,\r
4692                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4693       if (f != NULL) {\r
4694         SaveGame(f, 0, "");\r
4695       }\r
4696       break;\r
4697 \r
4698     case IDM_SavePosition:\r
4699       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4700       f = OpenFileDialog(hwnd, "a", defName,\r
4701                          appData.oldSaveStyle ? "pos" : "fen",\r
4702                          POSITION_FILT,\r
4703                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4704       if (f != NULL) {\r
4705         SavePosition(f, 0, "");\r
4706       }\r
4707       break;\r
4708 \r
4709     case IDM_SaveDiagram:\r
4710       defName = "diagram";\r
4711       f = OpenFileDialog(hwnd, "wb", defName,\r
4712                          "bmp",\r
4713                          DIAGRAM_FILT,\r
4714                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4715       if (f != NULL) {\r
4716         SaveDiagram(f);\r
4717       }\r
4718       break;\r
4719 \r
4720     case IDM_CopyGame:\r
4721       CopyGameToClipboard();\r
4722       break;\r
4723 \r
4724     case IDM_PasteGame:\r
4725       PasteGameFromClipboard();\r
4726       break;\r
4727 \r
4728     case IDM_CopyGameListToClipboard:\r
4729       CopyGameListToClipboard();\r
4730       break;\r
4731 \r
4732     /* [AS] Autodetect FEN or PGN data */\r
4733     case IDM_PasteAny:\r
4734       PasteGameOrFENFromClipboard();\r
4735       break;\r
4736 \r
4737     /* [AS] Move history */\r
4738     case IDM_ShowMoveHistory:\r
4739         if( MoveHistoryIsUp() ) {\r
4740             MoveHistoryPopDown();\r
4741         }\r
4742         else {\r
4743             MoveHistoryPopUp();\r
4744         }\r
4745         break;\r
4746 \r
4747     /* [AS] Eval graph */\r
4748     case IDM_ShowEvalGraph:\r
4749         if( EvalGraphIsUp() ) {\r
4750             EvalGraphPopDown();\r
4751         }\r
4752         else {\r
4753             EvalGraphPopUp();\r
4754             SetFocus(hwndMain);\r
4755         }\r
4756         break;\r
4757 \r
4758     /* [AS] Engine output */\r
4759     case IDM_ShowEngineOutput:\r
4760         if( EngineOutputIsUp() ) {\r
4761             EngineOutputPopDown();\r
4762         }\r
4763         else {\r
4764             EngineOutputPopUp();\r
4765         }\r
4766         break;\r
4767 \r
4768     /* [AS] User adjudication */\r
4769     case IDM_UserAdjudication_White:\r
4770         UserAdjudicationEvent( +1 );\r
4771         break;\r
4772 \r
4773     case IDM_UserAdjudication_Black:\r
4774         UserAdjudicationEvent( -1 );\r
4775         break;\r
4776 \r
4777     case IDM_UserAdjudication_Draw:\r
4778         UserAdjudicationEvent( 0 );\r
4779         break;\r
4780 \r
4781     /* [AS] Game list options dialog */\r
4782     case IDM_GameListOptions:\r
4783       GameListOptions();\r
4784       break;\r
4785 \r
4786     case IDM_NewChat:\r
4787       ChatPopUp(NULL);\r
4788       break;\r
4789 \r
4790     case IDM_CopyPosition:\r
4791       CopyFENToClipboard();\r
4792       break;\r
4793 \r
4794     case IDM_PastePosition:\r
4795       PasteFENFromClipboard();\r
4796       break;\r
4797 \r
4798     case IDM_MailMove:\r
4799       MailMoveEvent();\r
4800       break;\r
4801 \r
4802     case IDM_ReloadCMailMsg:\r
4803       Reset(TRUE, TRUE);\r
4804       ReloadCmailMsgEvent(FALSE);\r
4805       break;\r
4806 \r
4807     case IDM_Minimize:\r
4808       ShowWindow(hwnd, SW_MINIMIZE);\r
4809       break;\r
4810 \r
4811     case IDM_Exit:\r
4812       ExitEvent(0);\r
4813       break;\r
4814 \r
4815     case IDM_MachineWhite:\r
4816       MachineWhiteEvent();\r
4817       /*\r
4818        * refresh the tags dialog only if it's visible\r
4819        */\r
4820       if (gameMode == MachinePlaysWhite && 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 white");\r
4827       break;\r
4828 \r
4829     case IDM_MachineBlack:\r
4830       MachineBlackEvent();\r
4831       /*\r
4832        * refresh the tags dialog only if it's visible\r
4833        */\r
4834       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4835           char *tags;\r
4836           tags = PGNTags(&gameInfo);\r
4837           TagsPopUp(tags, CmailMsg());\r
4838           free(tags);\r
4839       }\r
4840       SAY("computer starts playing black");\r
4841       break;\r
4842 \r
4843     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4844       if(gameMode != BeginningOfGame) { // allow menu item to remain enabled for better mode highligting\r
4845         DisplayError(_("You can only start a match from the initial position."), 0); break;\r
4846       }\r
4847       appData.matchGames = appData.defaultMatchGames;
4848       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
4849       break;\r
4850 \r
4851     case IDM_TwoMachines:\r
4852       TwoMachinesEvent();\r
4853       /*\r
4854        * refresh the tags dialog only if it's visible\r
4855        */\r
4856       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4857           char *tags;\r
4858           tags = PGNTags(&gameInfo);\r
4859           TagsPopUp(tags, CmailMsg());\r
4860           free(tags);\r
4861       }\r
4862       SAY("computer starts playing both sides");\r
4863       break;\r
4864 \r
4865     case IDM_AnalysisMode:\r
4866       if (!first.analysisSupport) {\r
4867         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4868         DisplayError(buf, 0);\r
4869       } else {\r
4870         SAY("analyzing current position");\r
4871         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4872         if (appData.icsActive) {\r
4873                if (gameMode != IcsObserving) {\r
4874                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4875                        DisplayError(buf, 0);\r
4876                        /* secure check */\r
4877                        if (appData.icsEngineAnalyze) {\r
4878                                if (appData.debugMode) \r
4879                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4880                                ExitAnalyzeMode();\r
4881                                ModeHighlight();\r
4882                                break;\r
4883                        }\r
4884                        break;\r
4885                } else {\r
4886                        /* if enable, user want disable icsEngineAnalyze */\r
4887                        if (appData.icsEngineAnalyze) {\r
4888                                ExitAnalyzeMode();\r
4889                                ModeHighlight();\r
4890                                break;\r
4891                        }\r
4892                        appData.icsEngineAnalyze = TRUE;\r
4893                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4894                }\r
4895         } \r
4896         if (!appData.showThinking) ToggleShowThinking();\r
4897         AnalyzeModeEvent();\r
4898       }\r
4899       break;\r
4900 \r
4901     case IDM_AnalyzeFile:\r
4902       if (!first.analysisSupport) {\r
4903         char buf[MSG_SIZ];\r
4904           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4905         DisplayError(buf, 0);\r
4906       } else {\r
4907         if (!appData.showThinking) ToggleShowThinking();\r
4908         AnalyzeFileEvent();\r
4909         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4910         AnalysisPeriodicEvent(1);\r
4911       }\r
4912       break;\r
4913 \r
4914     case IDM_IcsClient:\r
4915       IcsClientEvent();\r
4916       break;\r
4917 \r
4918     case IDM_EditGame:\r
4919     case IDM_EditGame2:\r
4920       EditGameEvent();\r
4921       SAY("edit game");\r
4922       break;\r
4923 \r
4924     case IDM_EditPosition:\r
4925     case IDM_EditPosition2:\r
4926       EditPositionEvent();\r
4927       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4928       break;\r
4929 \r
4930     case IDM_Training:\r
4931       TrainingEvent();\r
4932       break;\r
4933 \r
4934     case IDM_ShowGameList:\r
4935       ShowGameListProc();\r
4936       break;\r
4937 \r
4938     case IDM_EditProgs1:\r
4939       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4940       break;\r
4941 \r
4942     case IDM_EditProgs2:\r
4943       EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);\r
4944       break;\r
4945 \r
4946     case IDM_EditServers:\r
4947       EditTagsPopUp(icsNames, &icsNames);\r
4948       break;\r
4949 \r
4950     case IDM_EditTags:\r
4951     case IDM_Tags:\r
4952       EditTagsProc();\r
4953       break;\r
4954 \r
4955     case IDM_EditComment:\r
4956     case IDM_Comment:\r
4957       if (commentUp && editComment) {\r
4958         CommentPopDown();\r
4959       } else {\r
4960         EditCommentEvent();\r
4961       }\r
4962       break;\r
4963 \r
4964     case IDM_Pause:\r
4965       PauseEvent();\r
4966       break;\r
4967 \r
4968     case IDM_Accept:\r
4969       AcceptEvent();\r
4970       break;\r
4971 \r
4972     case IDM_Decline:\r
4973       DeclineEvent();\r
4974       break;\r
4975 \r
4976     case IDM_Rematch:\r
4977       RematchEvent();\r
4978       break;\r
4979 \r
4980     case IDM_CallFlag:\r
4981       CallFlagEvent();\r
4982       break;\r
4983 \r
4984     case IDM_Draw:\r
4985       DrawEvent();\r
4986       break;\r
4987 \r
4988     case IDM_Adjourn:\r
4989       AdjournEvent();\r
4990       break;\r
4991 \r
4992     case IDM_Abort:\r
4993       AbortEvent();\r
4994       break;\r
4995 \r
4996     case IDM_Resign:\r
4997       ResignEvent();\r
4998       break;\r
4999 \r
5000     case IDM_StopObserving:\r
5001       StopObservingEvent();\r
5002       break;\r
5003 \r
5004     case IDM_StopExamining:\r
5005       StopExaminingEvent();\r
5006       break;\r
5007 \r
5008     case IDM_Upload:\r
5009       UploadGameEvent();\r
5010       break;\r
5011 \r
5012     case IDM_TypeInMove:\r
5013       TypeInEvent('\000');\r
5014       break;\r
5015 \r
5016     case IDM_TypeInName:\r
5017       PopUpNameDialog('\000');\r
5018       break;\r
5019 \r
5020     case IDM_Backward:\r
5021       BackwardEvent();\r
5022       SetFocus(hwndMain);\r
5023       break;\r
5024 \r
5025     JAWS_MENU_ITEMS\r
5026 \r
5027     case IDM_Forward:\r
5028       ForwardEvent();\r
5029       SetFocus(hwndMain);\r
5030       break;\r
5031 \r
5032     case IDM_ToStart:\r
5033       ToStartEvent();\r
5034       SetFocus(hwndMain);\r
5035       break;\r
5036 \r
5037     case IDM_ToEnd:\r
5038       ToEndEvent();\r
5039       SetFocus(hwndMain);\r
5040       break;\r
5041 \r
5042     case IDM_Revert:\r
5043       RevertEvent(FALSE);\r
5044       break;\r
5045 \r
5046     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5047       RevertEvent(TRUE);\r
5048       break;\r
5049 \r
5050     case IDM_TruncateGame:\r
5051       TruncateGameEvent();\r
5052       break;\r
5053 \r
5054     case IDM_MoveNow:\r
5055       MoveNowEvent();\r
5056       break;\r
5057 \r
5058     case IDM_RetractMove:\r
5059       RetractMoveEvent();\r
5060       break;\r
5061 \r
5062     case IDM_FlipView:\r
5063       flipView = !flipView;\r
5064       DrawPosition(FALSE, NULL);\r
5065       break;\r
5066 \r
5067     case IDM_FlipClock:\r
5068       flipClock = !flipClock;\r
5069       DisplayBothClocks();\r
5070       DisplayLogos();\r
5071       break;\r
5072 \r
5073     case IDM_MuteSounds:\r
5074       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5075       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5076                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5077       break;\r
5078 \r
5079     case IDM_GeneralOptions:\r
5080       GeneralOptionsPopup(hwnd);\r
5081       DrawPosition(TRUE, NULL);\r
5082       break;\r
5083 \r
5084     case IDM_BoardOptions:\r
5085       BoardOptionsPopup(hwnd);\r
5086       break;\r
5087 \r
5088     case IDM_EnginePlayOptions:\r
5089       EnginePlayOptionsPopup(hwnd);\r
5090       break;\r
5091 \r
5092     case IDM_Engine1Options:\r
5093       EngineOptionsPopup(hwnd, &first);\r
5094       break;\r
5095 \r
5096     case IDM_Engine2Options:\r
5097       savedHwnd = hwnd;\r
5098       if(WaitForSecond(SettingsMenuIfReady)) break;\r
5099       EngineOptionsPopup(hwnd, &second);\r
5100       break;\r
5101 \r
5102     case IDM_OptionsUCI:\r
5103       UciOptionsPopup(hwnd);\r
5104       break;\r
5105 \r
5106     case IDM_IcsOptions:\r
5107       IcsOptionsPopup(hwnd);\r
5108       break;\r
5109 \r
5110     case IDM_Fonts:\r
5111       FontsOptionsPopup(hwnd);\r
5112       break;\r
5113 \r
5114     case IDM_Sounds:\r
5115       SoundOptionsPopup(hwnd);\r
5116       break;\r
5117 \r
5118     case IDM_CommPort:\r
5119       CommPortOptionsPopup(hwnd);\r
5120       break;\r
5121 \r
5122     case IDM_LoadOptions:\r
5123       LoadOptionsPopup(hwnd);\r
5124       break;\r
5125 \r
5126     case IDM_SaveOptions:\r
5127       SaveOptionsPopup(hwnd);\r
5128       break;\r
5129 \r
5130     case IDM_TimeControl:\r
5131       TimeControlOptionsPopup(hwnd);\r
5132       break;\r
5133 \r
5134     case IDM_SaveSettings:\r
5135       SaveSettings(settingsFileName);\r
5136       break;\r
5137 \r
5138     case IDM_SaveSettingsOnExit:\r
5139       saveSettingsOnExit = !saveSettingsOnExit;\r
5140       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5141                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5142                                          MF_CHECKED : MF_UNCHECKED));\r
5143       break;\r
5144 \r
5145     case IDM_Hint:\r
5146       HintEvent();\r
5147       break;\r
5148 \r
5149     case IDM_Book:\r
5150       BookEvent();\r
5151       break;\r
5152 \r
5153     case IDM_AboutGame:\r
5154       AboutGameEvent();\r
5155       break;\r
5156 \r
5157     case IDM_Debug:\r
5158       appData.debugMode = !appData.debugMode;\r
5159       if (appData.debugMode) {\r
5160         char dir[MSG_SIZ];\r
5161         GetCurrentDirectory(MSG_SIZ, dir);\r
5162         SetCurrentDirectory(installDir);\r
5163         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5164         SetCurrentDirectory(dir);\r
5165         setbuf(debugFP, NULL);\r
5166       } else {\r
5167         fclose(debugFP);\r
5168         debugFP = NULL;\r
5169       }\r
5170       break;\r
5171 \r
5172     case IDM_HELPCONTENTS:\r
5173       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5174           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5175           MessageBox (GetFocus(),\r
5176                     _("Unable to activate help"),\r
5177                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5178       }\r
5179       break;\r
5180 \r
5181     case IDM_HELPSEARCH:\r
5182         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5183             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5184         MessageBox (GetFocus(),\r
5185                     _("Unable to activate help"),\r
5186                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5187       }\r
5188       break;\r
5189 \r
5190     case IDM_HELPHELP:\r
5191       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5192         MessageBox (GetFocus(),\r
5193                     _("Unable to activate help"),\r
5194                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5195       }\r
5196       break;\r
5197 \r
5198     case IDM_ABOUT:\r
5199       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5200       DialogBox(hInst, \r
5201         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5202         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5203       FreeProcInstance(lpProc);\r
5204       break;\r
5205 \r
5206     case IDM_DirectCommand1:\r
5207       AskQuestionEvent(_("Direct Command"),\r
5208                        _("Send to chess program:"), "", "1");\r
5209       break;\r
5210     case IDM_DirectCommand2:\r
5211       AskQuestionEvent(_("Direct Command"),\r
5212                        _("Send to second chess program:"), "", "2");\r
5213       break;\r
5214 \r
5215     case EP_WhitePawn:\r
5216       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5217       fromX = fromY = -1;\r
5218       break;\r
5219 \r
5220     case EP_WhiteKnight:\r
5221       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5222       fromX = fromY = -1;\r
5223       break;\r
5224 \r
5225     case EP_WhiteBishop:\r
5226       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5227       fromX = fromY = -1;\r
5228       break;\r
5229 \r
5230     case EP_WhiteRook:\r
5231       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5232       fromX = fromY = -1;\r
5233       break;\r
5234 \r
5235     case EP_WhiteQueen:\r
5236       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5237       fromX = fromY = -1;\r
5238       break;\r
5239 \r
5240     case EP_WhiteFerz:\r
5241       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5242       fromX = fromY = -1;\r
5243       break;\r
5244 \r
5245     case EP_WhiteWazir:\r
5246       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5247       fromX = fromY = -1;\r
5248       break;\r
5249 \r
5250     case EP_WhiteAlfil:\r
5251       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5252       fromX = fromY = -1;\r
5253       break;\r
5254 \r
5255     case EP_WhiteCannon:\r
5256       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5257       fromX = fromY = -1;\r
5258       break;\r
5259 \r
5260     case EP_WhiteCardinal:\r
5261       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5262       fromX = fromY = -1;\r
5263       break;\r
5264 \r
5265     case EP_WhiteMarshall:\r
5266       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5267       fromX = fromY = -1;\r
5268       break;\r
5269 \r
5270     case EP_WhiteKing:\r
5271       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5272       fromX = fromY = -1;\r
5273       break;\r
5274 \r
5275     case EP_BlackPawn:\r
5276       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5277       fromX = fromY = -1;\r
5278       break;\r
5279 \r
5280     case EP_BlackKnight:\r
5281       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5282       fromX = fromY = -1;\r
5283       break;\r
5284 \r
5285     case EP_BlackBishop:\r
5286       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5287       fromX = fromY = -1;\r
5288       break;\r
5289 \r
5290     case EP_BlackRook:\r
5291       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5292       fromX = fromY = -1;\r
5293       break;\r
5294 \r
5295     case EP_BlackQueen:\r
5296       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5297       fromX = fromY = -1;\r
5298       break;\r
5299 \r
5300     case EP_BlackFerz:\r
5301       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5302       fromX = fromY = -1;\r
5303       break;\r
5304 \r
5305     case EP_BlackWazir:\r
5306       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5307       fromX = fromY = -1;\r
5308       break;\r
5309 \r
5310     case EP_BlackAlfil:\r
5311       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5312       fromX = fromY = -1;\r
5313       break;\r
5314 \r
5315     case EP_BlackCannon:\r
5316       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5317       fromX = fromY = -1;\r
5318       break;\r
5319 \r
5320     case EP_BlackCardinal:\r
5321       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5322       fromX = fromY = -1;\r
5323       break;\r
5324 \r
5325     case EP_BlackMarshall:\r
5326       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5327       fromX = fromY = -1;\r
5328       break;\r
5329 \r
5330     case EP_BlackKing:\r
5331       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5332       fromX = fromY = -1;\r
5333       break;\r
5334 \r
5335     case EP_EmptySquare:\r
5336       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5337       fromX = fromY = -1;\r
5338       break;\r
5339 \r
5340     case EP_ClearBoard:\r
5341       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5342       fromX = fromY = -1;\r
5343       break;\r
5344 \r
5345     case EP_White:\r
5346       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5347       fromX = fromY = -1;\r
5348       break;\r
5349 \r
5350     case EP_Black:\r
5351       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5352       fromX = fromY = -1;\r
5353       break;\r
5354 \r
5355     case EP_Promote:\r
5356       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5357       fromX = fromY = -1;\r
5358       break;\r
5359 \r
5360     case EP_Demote:\r
5361       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5362       fromX = fromY = -1;\r
5363       break;\r
5364 \r
5365     case DP_Pawn:\r
5366       DropMenuEvent(WhitePawn, fromX, fromY);\r
5367       fromX = fromY = -1;\r
5368       break;\r
5369 \r
5370     case DP_Knight:\r
5371       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5372       fromX = fromY = -1;\r
5373       break;\r
5374 \r
5375     case DP_Bishop:\r
5376       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5377       fromX = fromY = -1;\r
5378       break;\r
5379 \r
5380     case DP_Rook:\r
5381       DropMenuEvent(WhiteRook, fromX, fromY);\r
5382       fromX = fromY = -1;\r
5383       break;\r
5384 \r
5385     case DP_Queen:\r
5386       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5387       fromX = fromY = -1;\r
5388       break;\r
5389 \r
5390     case IDM_English:\r
5391       barbaric = 0; appData.language = "";\r
5392       TranslateMenus(0);\r
5393       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5394       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5395       lastChecked = wmId;\r
5396       break;\r
5397 \r
5398     default:\r
5399       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5400           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5401           TranslateMenus(0);\r
5402           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5403           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5404           lastChecked = wmId;\r
5405           break;\r
5406       }\r
5407       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5408     }\r
5409     break;\r
5410 \r
5411   case WM_TIMER:\r
5412     switch (wParam) {\r
5413     case CLOCK_TIMER_ID:\r
5414       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5415       clockTimerEvent = 0;\r
5416       DecrementClocks(); /* call into back end */\r
5417       break;\r
5418     case LOAD_GAME_TIMER_ID:\r
5419       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5420       loadGameTimerEvent = 0;\r
5421       AutoPlayGameLoop(); /* call into back end */\r
5422       break;\r
5423     case ANALYSIS_TIMER_ID:\r
5424       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5425                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5426         AnalysisPeriodicEvent(0);\r
5427       } else {\r
5428         KillTimer(hwnd, analysisTimerEvent);\r
5429         analysisTimerEvent = 0;\r
5430       }\r
5431       break;\r
5432     case DELAYED_TIMER_ID:\r
5433       KillTimer(hwnd, delayedTimerEvent);\r
5434       delayedTimerEvent = 0;\r
5435       delayedTimerCallback();\r
5436       break;\r
5437     }\r
5438     break;\r
5439 \r
5440   case WM_USER_Input:\r
5441     InputEvent(hwnd, message, wParam, lParam);\r
5442     break;\r
5443 \r
5444   /* [AS] Also move "attached" child windows */\r
5445   case WM_WINDOWPOSCHANGING:\r
5446 \r
5447     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5448         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5449 \r
5450         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5451             /* Window is moving */\r
5452             RECT rcMain;\r
5453 \r
5454 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5455             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5456             rcMain.right  = wpMain.x + wpMain.width;\r
5457             rcMain.top    = wpMain.y;\r
5458             rcMain.bottom = wpMain.y + wpMain.height;\r
5459             \r
5460             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5461             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5462             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5463             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5464             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5465             wpMain.x = lpwp->x;\r
5466             wpMain.y = lpwp->y;\r
5467         }\r
5468     }\r
5469     break;\r
5470 \r
5471   /* [AS] Snapping */\r
5472   case WM_ENTERSIZEMOVE:\r
5473     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5474     if (hwnd == hwndMain) {\r
5475       doingSizing = TRUE;\r
5476       lastSizing = 0;\r
5477     }\r
5478     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5479     break;\r
5480 \r
5481   case WM_SIZING:\r
5482     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5483     if (hwnd == hwndMain) {\r
5484       lastSizing = wParam;\r
5485     }\r
5486     break;\r
5487 \r
5488   case WM_MOVING:\r
5489     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5490       return OnMoving( &sd, hwnd, wParam, lParam );\r
5491 \r
5492   case WM_EXITSIZEMOVE:\r
5493     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5494     if (hwnd == hwndMain) {\r
5495       RECT client;\r
5496       doingSizing = FALSE;\r
5497       InvalidateRect(hwnd, &boardRect, FALSE);\r
5498       GetClientRect(hwnd, &client);\r
5499       ResizeBoard(client.right, client.bottom, lastSizing);\r
5500       lastSizing = 0;\r
5501       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5502     }\r
5503     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5504     break;\r
5505 \r
5506   case WM_DESTROY: /* message: window being destroyed */\r
5507     PostQuitMessage(0);\r
5508     break;\r
5509 \r
5510   case WM_CLOSE:\r
5511     if (hwnd == hwndMain) {\r
5512       ExitEvent(0);\r
5513     }\r
5514     break;\r
5515 \r
5516   default:      /* Passes it on if unprocessed */\r
5517     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5518   }\r
5519   return 0;\r
5520 }\r
5521 \r
5522 /*---------------------------------------------------------------------------*\\r
5523  *\r
5524  * Misc utility routines\r
5525  *\r
5526 \*---------------------------------------------------------------------------*/\r
5527 \r
5528 /*\r
5529  * Decent random number generator, at least not as bad as Windows\r
5530  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5531  */\r
5532 unsigned int randstate;\r
5533 \r
5534 int\r
5535 myrandom(void)\r
5536 {\r
5537   randstate = randstate * 1664525 + 1013904223;\r
5538   return (int) randstate & 0x7fffffff;\r
5539 }\r
5540 \r
5541 void\r
5542 mysrandom(unsigned int seed)\r
5543 {\r
5544   randstate = seed;\r
5545 }\r
5546 \r
5547 \r
5548 /* \r
5549  * returns TRUE if user selects a different color, FALSE otherwise \r
5550  */\r
5551 \r
5552 BOOL\r
5553 ChangeColor(HWND hwnd, COLORREF *which)\r
5554 {\r
5555   static BOOL firstTime = TRUE;\r
5556   static DWORD customColors[16];\r
5557   CHOOSECOLOR cc;\r
5558   COLORREF newcolor;\r
5559   int i;\r
5560   ColorClass ccl;\r
5561 \r
5562   if (firstTime) {\r
5563     /* Make initial colors in use available as custom colors */\r
5564     /* Should we put the compiled-in defaults here instead? */\r
5565     i = 0;\r
5566     customColors[i++] = lightSquareColor & 0xffffff;\r
5567     customColors[i++] = darkSquareColor & 0xffffff;\r
5568     customColors[i++] = whitePieceColor & 0xffffff;\r
5569     customColors[i++] = blackPieceColor & 0xffffff;\r
5570     customColors[i++] = highlightSquareColor & 0xffffff;\r
5571     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5572 \r
5573     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5574       customColors[i++] = textAttribs[ccl].color;\r
5575     }\r
5576     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5577     firstTime = FALSE;\r
5578   }\r
5579 \r
5580   cc.lStructSize = sizeof(cc);\r
5581   cc.hwndOwner = hwnd;\r
5582   cc.hInstance = NULL;\r
5583   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5584   cc.lpCustColors = (LPDWORD) customColors;\r
5585   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5586 \r
5587   if (!ChooseColor(&cc)) return FALSE;\r
5588 \r
5589   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5590   if (newcolor == *which) return FALSE;\r
5591   *which = newcolor;\r
5592   return TRUE;\r
5593 \r
5594   /*\r
5595   InitDrawingColors();\r
5596   InvalidateRect(hwnd, &boardRect, FALSE);\r
5597   */\r
5598 }\r
5599 \r
5600 BOOLEAN\r
5601 MyLoadSound(MySound *ms)\r
5602 {\r
5603   BOOL ok = FALSE;\r
5604   struct stat st;\r
5605   FILE *f;\r
5606 \r
5607   if (ms->data) free(ms->data);\r
5608   ms->data = NULL;\r
5609 \r
5610   switch (ms->name[0]) {\r
5611   case NULLCHAR:\r
5612     /* Silence */\r
5613     ok = TRUE;\r
5614     break;\r
5615   case '$':\r
5616     /* System sound from Control Panel.  Don't preload here. */\r
5617     ok = TRUE;\r
5618     break;\r
5619   case '!':\r
5620     if (ms->name[1] == NULLCHAR) {\r
5621       /* "!" alone = silence */\r
5622       ok = TRUE;\r
5623     } else {\r
5624       /* Builtin wave resource.  Error if not found. */\r
5625       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5626       if (h == NULL) break;\r
5627       ms->data = (void *)LoadResource(hInst, h);\r
5628       if (h == NULL) break;\r
5629       ok = TRUE;\r
5630     }\r
5631     break;\r
5632   default:\r
5633     /* .wav file.  Error if not found. */\r
5634     f = fopen(ms->name, "rb");\r
5635     if (f == NULL) break;\r
5636     if (fstat(fileno(f), &st) < 0) break;\r
5637     ms->data = malloc(st.st_size);\r
5638     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5639     fclose(f);\r
5640     ok = TRUE;\r
5641     break;\r
5642   }\r
5643   if (!ok) {\r
5644     char buf[MSG_SIZ];\r
5645       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5646     DisplayError(buf, GetLastError());\r
5647   }\r
5648   return ok;\r
5649 }\r
5650 \r
5651 BOOLEAN\r
5652 MyPlaySound(MySound *ms)\r
5653 {\r
5654   BOOLEAN ok = FALSE;\r
5655 \r
5656   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5657   switch (ms->name[0]) {\r
5658   case NULLCHAR:\r
5659         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5660     /* Silence */\r
5661     ok = TRUE;\r
5662     break;\r
5663   case '$':\r
5664     /* System sound from Control Panel (deprecated feature).\r
5665        "$" alone or an unset sound name gets default beep (still in use). */\r
5666     if (ms->name[1]) {\r
5667       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5668     }\r
5669     if (!ok) ok = MessageBeep(MB_OK);\r
5670     break; \r
5671   case '!':\r
5672     /* Builtin wave resource, or "!" alone for silence */\r
5673     if (ms->name[1]) {\r
5674       if (ms->data == NULL) return FALSE;\r
5675       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5676     } else {\r
5677       ok = TRUE;\r
5678     }\r
5679     break;\r
5680   default:\r
5681     /* .wav file.  Error if not found. */\r
5682     if (ms->data == NULL) return FALSE;\r
5683     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5684     break;\r
5685   }\r
5686   /* Don't print an error: this can happen innocently if the sound driver\r
5687      is busy; for instance, if another instance of WinBoard is playing\r
5688      a sound at about the same time. */\r
5689   return ok;\r
5690 }\r
5691 \r
5692 \r
5693 LRESULT CALLBACK\r
5694 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5695 {\r
5696   BOOL ok;\r
5697   OPENFILENAME *ofn;\r
5698   static UINT *number; /* gross that this is static */\r
5699 \r
5700   switch (message) {\r
5701   case WM_INITDIALOG: /* message: initialize dialog box */\r
5702     /* Center the dialog over the application window */\r
5703     ofn = (OPENFILENAME *) lParam;\r
5704     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5705       number = (UINT *) ofn->lCustData;\r
5706       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5707     } else {\r
5708       number = NULL;\r
5709     }\r
5710     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5711     Translate(hDlg, 1536);\r
5712     return FALSE;  /* Allow for further processing */\r
5713 \r
5714   case WM_COMMAND:\r
5715     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5716       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5717     }\r
5718     return FALSE;  /* Allow for further processing */\r
5719   }\r
5720   return FALSE;\r
5721 }\r
5722 \r
5723 UINT APIENTRY\r
5724 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5725 {\r
5726   static UINT *number;\r
5727   OPENFILENAME *ofname;\r
5728   OFNOTIFY *ofnot;\r
5729   switch (uiMsg) {\r
5730   case WM_INITDIALOG:\r
5731     Translate(hdlg, DLG_IndexNumber);\r
5732     ofname = (OPENFILENAME *)lParam;\r
5733     number = (UINT *)(ofname->lCustData);\r
5734     break;\r
5735   case WM_NOTIFY:\r
5736     ofnot = (OFNOTIFY *)lParam;\r
5737     if (ofnot->hdr.code == CDN_FILEOK) {\r
5738       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5739     }\r
5740     break;\r
5741   }\r
5742   return 0;\r
5743 }\r
5744 \r
5745 \r
5746 FILE *\r
5747 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5748                char *nameFilt, char *dlgTitle, UINT *number,\r
5749                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5750 {\r
5751   OPENFILENAME openFileName;\r
5752   char buf1[MSG_SIZ];\r
5753   FILE *f;\r
5754 \r
5755   if (fileName == NULL) fileName = buf1;\r
5756   if (defName == NULL) {\r
5757     safeStrCpy(fileName, "*.", 3 );\r
5758     strcat(fileName, defExt);\r
5759   } else {\r
5760     safeStrCpy(fileName, defName, MSG_SIZ );\r
5761   }\r
5762     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5763   if (number) *number = 0;\r
5764 \r
5765   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5766   openFileName.hwndOwner         = hwnd;\r
5767   openFileName.hInstance         = (HANDLE) hInst;\r
5768   openFileName.lpstrFilter       = nameFilt;\r
5769   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5770   openFileName.nMaxCustFilter    = 0L;\r
5771   openFileName.nFilterIndex      = 1L;\r
5772   openFileName.lpstrFile         = fileName;\r
5773   openFileName.nMaxFile          = MSG_SIZ;\r
5774   openFileName.lpstrFileTitle    = fileTitle;\r
5775   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5776   openFileName.lpstrInitialDir   = NULL;\r
5777   openFileName.lpstrTitle        = dlgTitle;\r
5778   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5779     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5780     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5781     | (oldDialog ? 0 : OFN_EXPLORER);\r
5782   openFileName.nFileOffset       = 0;\r
5783   openFileName.nFileExtension    = 0;\r
5784   openFileName.lpstrDefExt       = defExt;\r
5785   openFileName.lCustData         = (LONG) number;\r
5786   openFileName.lpfnHook          = oldDialog ?\r
5787     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5788   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5789 \r
5790   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5791                         GetOpenFileName(&openFileName)) {\r
5792     /* open the file */\r
5793     f = fopen(openFileName.lpstrFile, write);\r
5794     if (f == NULL) {\r
5795       MessageBox(hwnd, _("File open failed"), NULL,\r
5796                  MB_OK|MB_ICONEXCLAMATION);\r
5797       return NULL;\r
5798     }\r
5799   } else {\r
5800     int err = CommDlgExtendedError();\r
5801     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5802     return FALSE;\r
5803   }\r
5804   return f;\r
5805 }\r
5806 \r
5807 \r
5808 \r
5809 VOID APIENTRY\r
5810 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5811 {\r
5812   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5813 \r
5814   /*\r
5815    * Get the first pop-up menu in the menu template. This is the\r
5816    * menu that TrackPopupMenu displays.\r
5817    */\r
5818   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5819   TranslateOneMenu(10, hmenuTrackPopup);\r
5820 \r
5821   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5822 \r
5823   /*\r
5824    * TrackPopup uses screen coordinates, so convert the\r
5825    * coordinates of the mouse click to screen coordinates.\r
5826    */\r
5827   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5828 \r
5829   /* Draw and track the floating pop-up menu. */\r
5830   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5831                  pt.x, pt.y, 0, hwnd, NULL);\r
5832 \r
5833   /* Destroy the menu.*/\r
5834   DestroyMenu(hmenu);\r
5835 }\r
5836    \r
5837 typedef struct {\r
5838   HWND hDlg, hText;\r
5839   int sizeX, sizeY, newSizeX, newSizeY;\r
5840   HDWP hdwp;\r
5841 } ResizeEditPlusButtonsClosure;\r
5842 \r
5843 BOOL CALLBACK\r
5844 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5845 {\r
5846   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5847   RECT rect;\r
5848   POINT pt;\r
5849 \r
5850   if (hChild == cl->hText) return TRUE;\r
5851   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5852   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5853   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5854   ScreenToClient(cl->hDlg, &pt);\r
5855   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5856     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5857   return TRUE;\r
5858 }\r
5859 \r
5860 /* Resize a dialog that has a (rich) edit field filling most of\r
5861    the top, with a row of buttons below */\r
5862 VOID\r
5863 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5864 {\r
5865   RECT rectText;\r
5866   int newTextHeight, newTextWidth;\r
5867   ResizeEditPlusButtonsClosure cl;\r
5868   \r
5869   /*if (IsIconic(hDlg)) return;*/\r
5870   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5871   \r
5872   cl.hdwp = BeginDeferWindowPos(8);\r
5873 \r
5874   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5875   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5876   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5877   if (newTextHeight < 0) {\r
5878     newSizeY += -newTextHeight;\r
5879     newTextHeight = 0;\r
5880   }\r
5881   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5882     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5883 \r
5884   cl.hDlg = hDlg;\r
5885   cl.hText = hText;\r
5886   cl.sizeX = sizeX;\r
5887   cl.sizeY = sizeY;\r
5888   cl.newSizeX = newSizeX;\r
5889   cl.newSizeY = newSizeY;\r
5890   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5891 \r
5892   EndDeferWindowPos(cl.hdwp);\r
5893 }\r
5894 \r
5895 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5896 {\r
5897     RECT    rChild, rParent;\r
5898     int     wChild, hChild, wParent, hParent;\r
5899     int     wScreen, hScreen, xNew, yNew;\r
5900     HDC     hdc;\r
5901 \r
5902     /* Get the Height and Width of the child window */\r
5903     GetWindowRect (hwndChild, &rChild);\r
5904     wChild = rChild.right - rChild.left;\r
5905     hChild = rChild.bottom - rChild.top;\r
5906 \r
5907     /* Get the Height and Width of the parent window */\r
5908     GetWindowRect (hwndParent, &rParent);\r
5909     wParent = rParent.right - rParent.left;\r
5910     hParent = rParent.bottom - rParent.top;\r
5911 \r
5912     /* Get the display limits */\r
5913     hdc = GetDC (hwndChild);\r
5914     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5915     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5916     ReleaseDC(hwndChild, hdc);\r
5917 \r
5918     /* Calculate new X position, then adjust for screen */\r
5919     xNew = rParent.left + ((wParent - wChild) /2);\r
5920     if (xNew < 0) {\r
5921         xNew = 0;\r
5922     } else if ((xNew+wChild) > wScreen) {\r
5923         xNew = wScreen - wChild;\r
5924     }\r
5925 \r
5926     /* Calculate new Y position, then adjust for screen */\r
5927     if( mode == 0 ) {\r
5928         yNew = rParent.top  + ((hParent - hChild) /2);\r
5929     }\r
5930     else {\r
5931         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5932     }\r
5933 \r
5934     if (yNew < 0) {\r
5935         yNew = 0;\r
5936     } else if ((yNew+hChild) > hScreen) {\r
5937         yNew = hScreen - hChild;\r
5938     }\r
5939 \r
5940     /* Set it, and return */\r
5941     return SetWindowPos (hwndChild, NULL,\r
5942                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5943 }\r
5944 \r
5945 /* Center one window over another */\r
5946 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5947 {\r
5948     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5949 }\r
5950 \r
5951 /*---------------------------------------------------------------------------*\\r
5952  *\r
5953  * Startup Dialog functions\r
5954  *\r
5955 \*---------------------------------------------------------------------------*/\r
5956 void\r
5957 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5958 {\r
5959   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5960 \r
5961   while (*cd != NULL) {\r
5962     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5963     cd++;\r
5964   }\r
5965 }\r
5966 \r
5967 void\r
5968 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5969 {\r
5970   char buf1[MAX_ARG_LEN];\r
5971   int len;\r
5972 \r
5973   if (str[0] == '@') {\r
5974     FILE* f = fopen(str + 1, "r");\r
5975     if (f == NULL) {\r
5976       DisplayFatalError(str + 1, errno, 2);\r
5977       return;\r
5978     }\r
5979     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5980     fclose(f);\r
5981     buf1[len] = NULLCHAR;\r
5982     str = buf1;\r
5983   }\r
5984 \r
5985   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5986 \r
5987   for (;;) {\r
5988     char buf[MSG_SIZ];\r
5989     char *end = strchr(str, '\n');\r
5990     if (end == NULL) return;\r
5991     memcpy(buf, str, end - str);\r
5992     buf[end - str] = NULLCHAR;\r
5993     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5994     str = end + 1;\r
5995   }\r
5996 }\r
5997 \r
5998 void\r
5999 SetStartupDialogEnables(HWND hDlg)\r
6000 {\r
6001   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6002     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6003     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6004   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6005     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6006   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6007     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6008   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6009     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6010   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6011     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6012     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6013     IsDlgButtonChecked(hDlg, OPT_View));\r
6014 }\r
6015 \r
6016 char *\r
6017 QuoteForFilename(char *filename)\r
6018 {\r
6019   int dquote, space;\r
6020   dquote = strchr(filename, '"') != NULL;\r
6021   space = strchr(filename, ' ') != NULL;\r
6022   if (dquote || space) {\r
6023     if (dquote) {\r
6024       return "'";\r
6025     } else {\r
6026       return "\"";\r
6027     }\r
6028   } else {\r
6029     return "";\r
6030   }\r
6031 }\r
6032 \r
6033 VOID\r
6034 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6035 {\r
6036   char buf[MSG_SIZ];\r
6037   char *q;\r
6038 \r
6039   InitComboStringsFromOption(hwndCombo, nthnames);\r
6040   q = QuoteForFilename(nthcp);\r
6041     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6042   if (*nthdir != NULLCHAR) {\r
6043     q = QuoteForFilename(nthdir);\r
6044       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6045   }\r
6046   if (*nthcp == NULLCHAR) {\r
6047     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6048   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6049     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6050     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6051   }\r
6052 }\r
6053 \r
6054 LRESULT CALLBACK\r
6055 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6056 {\r
6057   char buf[MSG_SIZ];\r
6058   HANDLE hwndCombo;\r
6059   char *p;\r
6060 \r
6061   switch (message) {\r
6062   case WM_INITDIALOG:\r
6063     /* Center the dialog */\r
6064     CenterWindow (hDlg, GetDesktopWindow());\r
6065     Translate(hDlg, DLG_Startup);\r
6066     /* Initialize the dialog items */\r
6067     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6068                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6069                   firstChessProgramNames);\r
6070     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6071                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6072                   secondChessProgramNames);\r
6073     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6074     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6075       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6076     if (*appData.icsHelper != NULLCHAR) {\r
6077       char *q = QuoteForFilename(appData.icsHelper);\r
6078       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6079     }\r
6080     if (*appData.icsHost == NULLCHAR) {\r
6081       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6082       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6083     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6084       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6085       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6086     }\r
6087 \r
6088     if (appData.icsActive) {\r
6089       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6090     }\r
6091     else if (appData.noChessProgram) {\r
6092       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6093     }\r
6094     else {\r
6095       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6096     }\r
6097 \r
6098     SetStartupDialogEnables(hDlg);\r
6099     return TRUE;\r
6100 \r
6101   case WM_COMMAND:\r
6102     switch (LOWORD(wParam)) {\r
6103     case IDOK:\r
6104       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6105         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6106         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6107         p = buf;\r
6108         ParseArgs(StringGet, &p);\r
6109         safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6110         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6111         p = buf;\r
6112         ParseArgs(StringGet, &p);\r
6113         appData.noChessProgram = FALSE;\r
6114         appData.icsActive = FALSE;\r
6115       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6116         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6117         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6118         p = buf;\r
6119         ParseArgs(StringGet, &p);\r
6120         if (appData.zippyPlay) {\r
6121           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6122           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6123           p = buf;\r
6124           ParseArgs(StringGet, &p);\r
6125         }\r
6126       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6127         appData.noChessProgram = TRUE;\r
6128         appData.icsActive = FALSE;\r
6129       } else {\r
6130         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6131                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6132         return TRUE;\r
6133       }\r
6134       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6135         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6136         p = buf;\r
6137         ParseArgs(StringGet, &p);\r
6138       }\r
6139       EndDialog(hDlg, TRUE);\r
6140       return TRUE;\r
6141 \r
6142     case IDCANCEL:\r
6143       ExitEvent(0);\r
6144       return TRUE;\r
6145 \r
6146     case IDM_HELPCONTENTS:\r
6147       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6148         MessageBox (GetFocus(),\r
6149                     _("Unable to activate help"),\r
6150                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6151       }\r
6152       break;\r
6153 \r
6154     default:\r
6155       SetStartupDialogEnables(hDlg);\r
6156       break;\r
6157     }\r
6158     break;\r
6159   }\r
6160   return FALSE;\r
6161 }\r
6162 \r
6163 /*---------------------------------------------------------------------------*\\r
6164  *\r
6165  * About box dialog functions\r
6166  *\r
6167 \*---------------------------------------------------------------------------*/\r
6168 \r
6169 /* Process messages for "About" dialog box */\r
6170 LRESULT CALLBACK\r
6171 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6172 {\r
6173   switch (message) {\r
6174   case WM_INITDIALOG: /* message: initialize dialog box */\r
6175     /* Center the dialog over the application window */\r
6176     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6177     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6178     Translate(hDlg, ABOUTBOX);\r
6179     JAWS_COPYRIGHT\r
6180     return (TRUE);\r
6181 \r
6182   case WM_COMMAND: /* message: received a command */\r
6183     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6184         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6185       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6186       return (TRUE);\r
6187     }\r
6188     break;\r
6189   }\r
6190   return (FALSE);\r
6191 }\r
6192 \r
6193 /*---------------------------------------------------------------------------*\\r
6194  *\r
6195  * Comment Dialog functions\r
6196  *\r
6197 \*---------------------------------------------------------------------------*/\r
6198 \r
6199 LRESULT CALLBACK\r
6200 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6201 {\r
6202   static HANDLE hwndText = NULL;\r
6203   int len, newSizeX, newSizeY, flags;\r
6204   static int sizeX, sizeY;\r
6205   char *str;\r
6206   RECT rect;\r
6207   MINMAXINFO *mmi;\r
6208 \r
6209   switch (message) {\r
6210   case WM_INITDIALOG: /* message: initialize dialog box */\r
6211     /* Initialize the dialog items */\r
6212     Translate(hDlg, DLG_EditComment);\r
6213     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6214     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6215     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6216     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6217     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6218     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6219     SetWindowText(hDlg, commentTitle);\r
6220     if (editComment) {\r
6221       SetFocus(hwndText);\r
6222     } else {\r
6223       SetFocus(GetDlgItem(hDlg, IDOK));\r
6224     }\r
6225     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6226                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6227                 MAKELPARAM(FALSE, 0));\r
6228     /* Size and position the dialog */\r
6229     if (!commentDialog) {\r
6230       commentDialog = hDlg;\r
6231       flags = SWP_NOZORDER;\r
6232       GetClientRect(hDlg, &rect);\r
6233       sizeX = rect.right;\r
6234       sizeY = rect.bottom;\r
6235       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6236           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6237         WINDOWPLACEMENT wp;\r
6238         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6239         wp.length = sizeof(WINDOWPLACEMENT);\r
6240         wp.flags = 0;\r
6241         wp.showCmd = SW_SHOW;\r
6242         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6243         wp.rcNormalPosition.left = wpComment.x;\r
6244         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6245         wp.rcNormalPosition.top = wpComment.y;\r
6246         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6247         SetWindowPlacement(hDlg, &wp);\r
6248 \r
6249         GetClientRect(hDlg, &rect);\r
6250         newSizeX = rect.right;\r
6251         newSizeY = rect.bottom;\r
6252         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6253                               newSizeX, newSizeY);\r
6254         sizeX = newSizeX;\r
6255         sizeY = newSizeY;\r
6256       }\r
6257     }\r
6258     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6259     return FALSE;\r
6260 \r
6261   case WM_COMMAND: /* message: received a command */\r
6262     switch (LOWORD(wParam)) {\r
6263     case IDOK:\r
6264       if (editComment) {\r
6265         char *p, *q;\r
6266         /* Read changed options from the dialog box */\r
6267         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6268         len = GetWindowTextLength(hwndText);\r
6269         str = (char *) malloc(len + 1);\r
6270         GetWindowText(hwndText, str, len + 1);\r
6271         p = q = str;\r
6272         while (*q) {\r
6273           if (*q == '\r')\r
6274             q++;\r
6275           else\r
6276             *p++ = *q++;\r
6277         }\r
6278         *p = NULLCHAR;\r
6279         ReplaceComment(commentIndex, str);\r
6280         free(str);\r
6281       }\r
6282       CommentPopDown();\r
6283       return TRUE;\r
6284 \r
6285     case IDCANCEL:\r
6286     case OPT_CancelComment:\r
6287       CommentPopDown();\r
6288       return TRUE;\r
6289 \r
6290     case OPT_ClearComment:\r
6291       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6292       break;\r
6293 \r
6294     case OPT_EditComment:\r
6295       EditCommentEvent();\r
6296       return TRUE;\r
6297 \r
6298     default:\r
6299       break;\r
6300     }\r
6301     break;\r
6302 \r
6303   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6304         if( wParam == OPT_CommentText ) {\r
6305             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6306 \r
6307             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6308                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6309                 POINTL pt;\r
6310                 LRESULT index;\r
6311 \r
6312                 pt.x = LOWORD( lpMF->lParam );\r
6313                 pt.y = HIWORD( lpMF->lParam );\r
6314 \r
6315                 if(lpMF->msg == WM_CHAR) {\r
6316                         CHARRANGE sel;\r
6317                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6318                         index = sel.cpMin;\r
6319                 } else\r
6320                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6321 \r
6322                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6323                 len = GetWindowTextLength(hwndText);\r
6324                 str = (char *) malloc(len + 1);\r
6325                 GetWindowText(hwndText, str, len + 1);\r
6326                 ReplaceComment(commentIndex, str);\r
6327                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6328                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6329                 free(str);\r
6330 \r
6331                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6332                 lpMF->msg = WM_USER;\r
6333 \r
6334                 return TRUE;\r
6335             }\r
6336         }\r
6337         break;\r
6338 \r
6339   case WM_SIZE:\r
6340     newSizeX = LOWORD(lParam);\r
6341     newSizeY = HIWORD(lParam);\r
6342     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6343     sizeX = newSizeX;\r
6344     sizeY = newSizeY;\r
6345     break;\r
6346 \r
6347   case WM_GETMINMAXINFO:\r
6348     /* Prevent resizing window too small */\r
6349     mmi = (MINMAXINFO *) lParam;\r
6350     mmi->ptMinTrackSize.x = 100;\r
6351     mmi->ptMinTrackSize.y = 100;\r
6352     break;\r
6353   }\r
6354   return FALSE;\r
6355 }\r
6356 \r
6357 VOID\r
6358 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6359 {\r
6360   FARPROC lpProc;\r
6361   char *p, *q;\r
6362 \r
6363   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6364 \r
6365   if (str == NULL) str = "";\r
6366   p = (char *) malloc(2 * strlen(str) + 2);\r
6367   q = p;\r
6368   while (*str) {\r
6369     if (*str == '\n') *q++ = '\r';\r
6370     *q++ = *str++;\r
6371   }\r
6372   *q = NULLCHAR;\r
6373   if (commentText != NULL) free(commentText);\r
6374 \r
6375   commentIndex = index;\r
6376   commentTitle = title;\r
6377   commentText = p;\r
6378   editComment = edit;\r
6379 \r
6380   if (commentDialog) {\r
6381     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6382     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6383   } else {\r
6384     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6385     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6386                  hwndMain, (DLGPROC)lpProc);\r
6387     FreeProcInstance(lpProc);\r
6388   }\r
6389   commentUp = TRUE;\r
6390 }\r
6391 \r
6392 \r
6393 /*---------------------------------------------------------------------------*\\r
6394  *\r
6395  * Type-in move dialog functions\r
6396  * \r
6397 \*---------------------------------------------------------------------------*/\r
6398 \r
6399 LRESULT CALLBACK\r
6400 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6401 {\r
6402   char move[MSG_SIZ];\r
6403   HWND hInput;\r
6404 \r
6405   switch (message) {\r
6406   case WM_INITDIALOG:\r
6407     move[0] = (char) lParam;\r
6408     move[1] = NULLCHAR;\r
6409     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6410     Translate(hDlg, DLG_TypeInMove);\r
6411     hInput = GetDlgItem(hDlg, OPT_Move);\r
6412     SetWindowText(hInput, move);\r
6413     SetFocus(hInput);\r
6414     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6415     return FALSE;\r
6416 \r
6417   case WM_COMMAND:\r
6418     switch (LOWORD(wParam)) {\r
6419     case IDOK:
6420 \r
6421       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6422       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6423       TypeInDoneEvent(move);\r
6424       EndDialog(hDlg, TRUE);\r
6425       return TRUE;\r
6426     case IDCANCEL:\r
6427       EndDialog(hDlg, FALSE);\r
6428       return TRUE;\r
6429     default:\r
6430       break;\r
6431     }\r
6432     break;\r
6433   }\r
6434   return FALSE;\r
6435 }\r
6436 \r
6437 VOID\r
6438 PopUpMoveDialog(char firstchar)\r
6439 {\r
6440     FARPROC lpProc;\r
6441 \r
6442       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6443       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6444         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6445       FreeProcInstance(lpProc);\r
6446 }\r
6447 \r
6448 /*---------------------------------------------------------------------------*\\r
6449  *\r
6450  * Type-in name dialog functions\r
6451  * \r
6452 \*---------------------------------------------------------------------------*/\r
6453 \r
6454 LRESULT CALLBACK\r
6455 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6456 {\r
6457   char move[MSG_SIZ];\r
6458   HWND hInput;\r
6459 \r
6460   switch (message) {\r
6461   case WM_INITDIALOG:\r
6462     move[0] = (char) lParam;\r
6463     move[1] = NULLCHAR;\r
6464     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6465     Translate(hDlg, DLG_TypeInName);\r
6466     hInput = GetDlgItem(hDlg, OPT_Name);\r
6467     SetWindowText(hInput, move);\r
6468     SetFocus(hInput);\r
6469     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6470     return FALSE;\r
6471 \r
6472   case WM_COMMAND:\r
6473     switch (LOWORD(wParam)) {\r
6474     case IDOK:\r
6475       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6476       appData.userName = strdup(move);\r
6477       SetUserLogo();\r
6478       SetGameInfo();\r
6479       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6480         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6481         DisplayTitle(move);\r
6482       }\r
6483 \r
6484 \r
6485       EndDialog(hDlg, TRUE);\r
6486       return TRUE;\r
6487     case IDCANCEL:\r
6488       EndDialog(hDlg, FALSE);\r
6489       return TRUE;\r
6490     default:\r
6491       break;\r
6492     }\r
6493     break;\r
6494   }\r
6495   return FALSE;\r
6496 }\r
6497 \r
6498 VOID\r
6499 PopUpNameDialog(char firstchar)\r
6500 {\r
6501     FARPROC lpProc;\r
6502     \r
6503       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6504       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6505         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6506       FreeProcInstance(lpProc);\r
6507 }\r
6508 \r
6509 /*---------------------------------------------------------------------------*\\r
6510  *\r
6511  *  Error dialogs\r
6512  * \r
6513 \*---------------------------------------------------------------------------*/\r
6514 \r
6515 /* Nonmodal error box */\r
6516 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6517                              WPARAM wParam, LPARAM lParam);\r
6518 \r
6519 VOID\r
6520 ErrorPopUp(char *title, char *content)\r
6521 {\r
6522   FARPROC lpProc;\r
6523   char *p, *q;\r
6524   BOOLEAN modal = hwndMain == NULL;\r
6525 \r
6526   p = content;\r
6527   q = errorMessage;\r
6528   while (*p) {\r
6529     if (*p == '\n') {\r
6530       if (modal) {\r
6531         *q++ = ' ';\r
6532         p++;\r
6533       } else {\r
6534         *q++ = '\r';\r
6535         *q++ = *p++;\r
6536       }\r
6537     } else {\r
6538       *q++ = *p++;\r
6539     }\r
6540   }\r
6541   *q = NULLCHAR;\r
6542   strncpy(errorTitle, title, sizeof(errorTitle));\r
6543   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6544   \r
6545   if (modal) {\r
6546     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6547   } else {\r
6548     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6549     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6550                  hwndMain, (DLGPROC)lpProc);\r
6551     FreeProcInstance(lpProc);\r
6552   }\r
6553 }\r
6554 \r
6555 VOID\r
6556 ErrorPopDown()\r
6557 {\r
6558   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6559   if (errorDialog == NULL) return;\r
6560   DestroyWindow(errorDialog);\r
6561   errorDialog = NULL;\r
6562   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6563 }\r
6564 \r
6565 LRESULT CALLBACK\r
6566 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6567 {\r
6568   HANDLE hwndText;\r
6569   RECT rChild;\r
6570 \r
6571   switch (message) {\r
6572   case WM_INITDIALOG:\r
6573     GetWindowRect(hDlg, &rChild);\r
6574 \r
6575     /*\r
6576     SetWindowPos(hDlg, NULL, rChild.left,\r
6577       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6578       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6579     */\r
6580 \r
6581     /* \r
6582         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6583         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6584         and it doesn't work when you resize the dialog.\r
6585         For now, just give it a default position.\r
6586     */\r
6587     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6588     Translate(hDlg, DLG_Error);\r
6589 \r
6590     errorDialog = hDlg;\r
6591     SetWindowText(hDlg, errorTitle);\r
6592     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6593     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6594     return FALSE;\r
6595 \r
6596   case WM_COMMAND:\r
6597     switch (LOWORD(wParam)) {\r
6598     case IDOK:\r
6599     case IDCANCEL:\r
6600       if (errorDialog == hDlg) errorDialog = NULL;\r
6601       DestroyWindow(hDlg);\r
6602       return TRUE;\r
6603 \r
6604     default:\r
6605       break;\r
6606     }\r
6607     break;\r
6608   }\r
6609   return FALSE;\r
6610 }\r
6611 \r
6612 #ifdef GOTHIC\r
6613 HWND gothicDialog = NULL;\r
6614 \r
6615 LRESULT CALLBACK\r
6616 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6617 {\r
6618   HANDLE hwndText;\r
6619   RECT rChild;\r
6620   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6621 \r
6622   switch (message) {\r
6623   case WM_INITDIALOG:\r
6624     GetWindowRect(hDlg, &rChild);\r
6625 \r
6626     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6627                                                              SWP_NOZORDER);\r
6628 \r
6629     /* \r
6630         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6631         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6632         and it doesn't work when you resize the dialog.\r
6633         For now, just give it a default position.\r
6634     */\r
6635     gothicDialog = hDlg;\r
6636     SetWindowText(hDlg, errorTitle);\r
6637     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6638     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6639     return FALSE;\r
6640 \r
6641   case WM_COMMAND:\r
6642     switch (LOWORD(wParam)) {\r
6643     case IDOK:\r
6644     case IDCANCEL:\r
6645       if (errorDialog == hDlg) errorDialog = NULL;\r
6646       DestroyWindow(hDlg);\r
6647       return TRUE;\r
6648 \r
6649     default:\r
6650       break;\r
6651     }\r
6652     break;\r
6653   }\r
6654   return FALSE;\r
6655 }\r
6656 \r
6657 VOID\r
6658 GothicPopUp(char *title, VariantClass variant)\r
6659 {\r
6660   FARPROC lpProc;\r
6661   static char *lastTitle;\r
6662 \r
6663   strncpy(errorTitle, title, sizeof(errorTitle));\r
6664   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6665 \r
6666   if(lastTitle != title && gothicDialog != NULL) {\r
6667     DestroyWindow(gothicDialog);\r
6668     gothicDialog = NULL;\r
6669   }\r
6670   if(variant != VariantNormal && gothicDialog == NULL) {\r
6671     title = lastTitle;\r
6672     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6673     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6674                  hwndMain, (DLGPROC)lpProc);\r
6675     FreeProcInstance(lpProc);\r
6676   }\r
6677 }\r
6678 #endif\r
6679 \r
6680 /*---------------------------------------------------------------------------*\\r
6681  *\r
6682  *  Ics Interaction console functions\r
6683  *\r
6684 \*---------------------------------------------------------------------------*/\r
6685 \r
6686 #define HISTORY_SIZE 64\r
6687 static char *history[HISTORY_SIZE];\r
6688 int histIn = 0, histP = 0;\r
6689 \r
6690 VOID\r
6691 SaveInHistory(char *cmd)\r
6692 {\r
6693   if (history[histIn] != NULL) {\r
6694     free(history[histIn]);\r
6695     history[histIn] = NULL;\r
6696   }\r
6697   if (*cmd == NULLCHAR) return;\r
6698   history[histIn] = StrSave(cmd);\r
6699   histIn = (histIn + 1) % HISTORY_SIZE;\r
6700   if (history[histIn] != NULL) {\r
6701     free(history[histIn]);\r
6702     history[histIn] = NULL;\r
6703   }\r
6704   histP = histIn;\r
6705 }\r
6706 \r
6707 char *\r
6708 PrevInHistory(char *cmd)\r
6709 {\r
6710   int newhp;\r
6711   if (histP == histIn) {\r
6712     if (history[histIn] != NULL) free(history[histIn]);\r
6713     history[histIn] = StrSave(cmd);\r
6714   }\r
6715   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6716   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6717   histP = newhp;\r
6718   return history[histP];\r
6719 }\r
6720 \r
6721 char *\r
6722 NextInHistory()\r
6723 {\r
6724   if (histP == histIn) return NULL;\r
6725   histP = (histP + 1) % HISTORY_SIZE;\r
6726   return history[histP];   \r
6727 }\r
6728 \r
6729 HMENU\r
6730 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6731 {\r
6732   HMENU hmenu, h;\r
6733   int i = 0;\r
6734   hmenu = LoadMenu(hInst, "TextMenu");\r
6735   h = GetSubMenu(hmenu, 0);\r
6736   while (e->item) {\r
6737     if (strcmp(e->item, "-") == 0) {\r
6738       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6739     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6740       int flags = MF_STRING, j = 0;\r
6741       if (e->item[0] == '|') {\r
6742         flags |= MF_MENUBARBREAK;\r
6743         j++;\r
6744       }\r
6745       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6746       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6747     }\r
6748     e++;\r
6749     i++;\r
6750   } \r
6751   return hmenu;\r
6752 }\r
6753 \r
6754 WNDPROC consoleTextWindowProc;\r
6755 \r
6756 void\r
6757 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6758 {\r
6759   char buf[MSG_SIZ], name[MSG_SIZ];\r
6760   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6761   CHARRANGE sel;\r
6762 \r
6763   if (!getname) {\r
6764     SetWindowText(hInput, command);\r
6765     if (immediate) {\r
6766       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6767     } else {\r
6768       sel.cpMin = 999999;\r
6769       sel.cpMax = 999999;\r
6770       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6771       SetFocus(hInput);\r
6772     }\r
6773     return;\r
6774   }    \r
6775   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6776   if (sel.cpMin == sel.cpMax) {\r
6777     /* Expand to surrounding word */\r
6778     TEXTRANGE tr;\r
6779     do {\r
6780       tr.chrg.cpMax = sel.cpMin;\r
6781       tr.chrg.cpMin = --sel.cpMin;\r
6782       if (sel.cpMin < 0) break;\r
6783       tr.lpstrText = name;\r
6784       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6785     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6786     sel.cpMin++;\r
6787 \r
6788     do {\r
6789       tr.chrg.cpMin = sel.cpMax;\r
6790       tr.chrg.cpMax = ++sel.cpMax;\r
6791       tr.lpstrText = name;\r
6792       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6793     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6794     sel.cpMax--;\r
6795 \r
6796     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6797       MessageBeep(MB_ICONEXCLAMATION);\r
6798       return;\r
6799     }\r
6800     tr.chrg = sel;\r
6801     tr.lpstrText = name;\r
6802     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6803   } else {\r
6804     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6805       MessageBeep(MB_ICONEXCLAMATION);\r
6806       return;\r
6807     }\r
6808     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6809   }\r
6810   if (immediate) {\r
6811     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6812     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6813     SetWindowText(hInput, buf);\r
6814     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6815   } else {\r
6816     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6817       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6818     SetWindowText(hInput, buf);\r
6819     sel.cpMin = 999999;\r
6820     sel.cpMax = 999999;\r
6821     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6822     SetFocus(hInput);\r
6823   }\r
6824 }\r
6825 \r
6826 LRESULT CALLBACK \r
6827 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6828 {\r
6829   HWND hInput;\r
6830   CHARRANGE sel;\r
6831 \r
6832   switch (message) {\r
6833   case WM_KEYDOWN:\r
6834     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6835     if(wParam=='R') return 0;\r
6836     switch (wParam) {\r
6837     case VK_PRIOR:\r
6838       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6839       return 0;\r
6840     case VK_NEXT:\r
6841       sel.cpMin = 999999;\r
6842       sel.cpMax = 999999;\r
6843       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6844       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6845       return 0;\r
6846     }\r
6847     break;\r
6848   case WM_CHAR:\r
6849    if(wParam != '\022') {\r
6850     if (wParam == '\t') {\r
6851       if (GetKeyState(VK_SHIFT) < 0) {\r
6852         /* shifted */\r
6853         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6854         if (buttonDesc[0].hwnd) {\r
6855           SetFocus(buttonDesc[0].hwnd);\r
6856         } else {\r
6857           SetFocus(hwndMain);\r
6858         }\r
6859       } else {\r
6860         /* unshifted */\r
6861         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6862       }\r
6863     } else {\r
6864       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6865       JAWS_DELETE( SetFocus(hInput); )\r
6866       SendMessage(hInput, message, wParam, lParam);\r
6867     }\r
6868     return 0;\r
6869    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6870    lParam = -1;\r
6871   case WM_RBUTTONDOWN:\r
6872     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6873       /* Move selection here if it was empty */\r
6874       POINT pt;\r
6875       pt.x = LOWORD(lParam);\r
6876       pt.y = HIWORD(lParam);\r
6877       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6878       if (sel.cpMin == sel.cpMax) {\r
6879         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6880         sel.cpMax = sel.cpMin;\r
6881         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6882       }\r
6883       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6884 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6885       POINT pt;\r
6886       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6887       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6888       if (sel.cpMin == sel.cpMax) {\r
6889         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6890         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6891       }\r
6892       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6893         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6894       }\r
6895       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6896       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6897       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6898       MenuPopup(hwnd, pt, hmenu, -1);\r
6899 }\r
6900     }\r
6901     return 0;\r
6902   case WM_RBUTTONUP:\r
6903     if (GetKeyState(VK_SHIFT) & ~1) {\r
6904       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6905         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6906     }\r
6907     return 0;\r
6908   case WM_PASTE:\r
6909     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6910     SetFocus(hInput);\r
6911     return SendMessage(hInput, message, wParam, lParam);\r
6912   case WM_MBUTTONDOWN:\r
6913     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6914   case WM_COMMAND:\r
6915     switch (LOWORD(wParam)) {\r
6916     case IDM_QuickPaste:\r
6917       {\r
6918         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6919         if (sel.cpMin == sel.cpMax) {\r
6920           MessageBeep(MB_ICONEXCLAMATION);\r
6921           return 0;\r
6922         }\r
6923         SendMessage(hwnd, WM_COPY, 0, 0);\r
6924         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6925         SendMessage(hInput, WM_PASTE, 0, 0);\r
6926         SetFocus(hInput);\r
6927         return 0;\r
6928       }\r
6929     case IDM_Cut:\r
6930       SendMessage(hwnd, WM_CUT, 0, 0);\r
6931       return 0;\r
6932     case IDM_Paste:\r
6933       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6934       return 0;\r
6935     case IDM_Copy:\r
6936       SendMessage(hwnd, WM_COPY, 0, 0);\r
6937       return 0;\r
6938     default:\r
6939       {\r
6940         int i = LOWORD(wParam) - IDM_CommandX;\r
6941         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6942             icsTextMenuEntry[i].command != NULL) {\r
6943           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6944                    icsTextMenuEntry[i].getname,\r
6945                    icsTextMenuEntry[i].immediate);\r
6946           return 0;\r
6947         }\r
6948       }\r
6949       break;\r
6950     }\r
6951     break;\r
6952   }\r
6953   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6954 }\r
6955 \r
6956 WNDPROC consoleInputWindowProc;\r
6957 \r
6958 LRESULT CALLBACK\r
6959 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6960 {\r
6961   char buf[MSG_SIZ];\r
6962   char *p;\r
6963   static BOOL sendNextChar = FALSE;\r
6964   static BOOL quoteNextChar = FALSE;\r
6965   InputSource *is = consoleInputSource;\r
6966   CHARFORMAT cf;\r
6967   CHARRANGE sel;\r
6968 \r
6969   switch (message) {\r
6970   case WM_CHAR:\r
6971     if (!appData.localLineEditing || sendNextChar) {\r
6972       is->buf[0] = (CHAR) wParam;\r
6973       is->count = 1;\r
6974       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6975       sendNextChar = FALSE;\r
6976       return 0;\r
6977     }\r
6978     if (quoteNextChar) {\r
6979       buf[0] = (char) wParam;\r
6980       buf[1] = NULLCHAR;\r
6981       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
6982       quoteNextChar = FALSE;\r
6983       return 0;\r
6984     }\r
6985     switch (wParam) {\r
6986     case '\r':   /* Enter key */\r
6987       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
6988       if (consoleEcho) SaveInHistory(is->buf);\r
6989       is->buf[is->count++] = '\n';\r
6990       is->buf[is->count] = NULLCHAR;\r
6991       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6992       if (consoleEcho) {\r
6993         ConsoleOutput(is->buf, is->count, TRUE);\r
6994       } else if (appData.localLineEditing) {\r
6995         ConsoleOutput("\n", 1, TRUE);\r
6996       }\r
6997       /* fall thru */\r
6998     case '\033': /* Escape key */\r
6999       SetWindowText(hwnd, "");\r
7000       cf.cbSize = sizeof(CHARFORMAT);\r
7001       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7002       if (consoleEcho) {\r
7003         cf.crTextColor = textAttribs[ColorNormal].color;\r
7004       } else {\r
7005         cf.crTextColor = COLOR_ECHOOFF;\r
7006       }\r
7007       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7008       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7009       return 0;\r
7010     case '\t':   /* Tab key */\r
7011       if (GetKeyState(VK_SHIFT) < 0) {\r
7012         /* shifted */\r
7013         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7014       } else {\r
7015         /* unshifted */\r
7016         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7017         if (buttonDesc[0].hwnd) {\r
7018           SetFocus(buttonDesc[0].hwnd);\r
7019         } else {\r
7020           SetFocus(hwndMain);\r
7021         }\r
7022       }\r
7023       return 0;\r
7024     case '\023': /* Ctrl+S */\r
7025       sendNextChar = TRUE;\r
7026       return 0;\r
7027     case '\021': /* Ctrl+Q */\r
7028       quoteNextChar = TRUE;\r
7029       return 0;\r
7030     JAWS_REPLAY\r
7031     default:\r
7032       break;\r
7033     }\r
7034     break;\r
7035   case WM_KEYDOWN:\r
7036     switch (wParam) {\r
7037     case VK_UP:\r
7038       GetWindowText(hwnd, buf, MSG_SIZ);\r
7039       p = PrevInHistory(buf);\r
7040       if (p != NULL) {\r
7041         SetWindowText(hwnd, p);\r
7042         sel.cpMin = 999999;\r
7043         sel.cpMax = 999999;\r
7044         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7045         return 0;\r
7046       }\r
7047       break;\r
7048     case VK_DOWN:\r
7049       p = NextInHistory();\r
7050       if (p != NULL) {\r
7051         SetWindowText(hwnd, p);\r
7052         sel.cpMin = 999999;\r
7053         sel.cpMax = 999999;\r
7054         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7055         return 0;\r
7056       }\r
7057       break;\r
7058     case VK_HOME:\r
7059     case VK_END:\r
7060       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7061       /* fall thru */\r
7062     case VK_PRIOR:\r
7063     case VK_NEXT:\r
7064       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7065       return 0;\r
7066     }\r
7067     break;\r
7068   case WM_MBUTTONDOWN:\r
7069     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7070       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7071     break;\r
7072   case WM_RBUTTONUP:\r
7073     if (GetKeyState(VK_SHIFT) & ~1) {\r
7074       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7075         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7076     } else {\r
7077       POINT pt;\r
7078       HMENU hmenu;\r
7079       hmenu = LoadMenu(hInst, "InputMenu");\r
7080       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7081       if (sel.cpMin == sel.cpMax) {\r
7082         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7083         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7084       }\r
7085       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7086         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7087       }\r
7088       pt.x = LOWORD(lParam);\r
7089       pt.y = HIWORD(lParam);\r
7090       MenuPopup(hwnd, pt, hmenu, -1);\r
7091     }\r
7092     return 0;\r
7093   case WM_COMMAND:\r
7094     switch (LOWORD(wParam)) { \r
7095     case IDM_Undo:\r
7096       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7097       return 0;\r
7098     case IDM_SelectAll:\r
7099       sel.cpMin = 0;\r
7100       sel.cpMax = -1; /*999999?*/\r
7101       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7102       return 0;\r
7103     case IDM_Cut:\r
7104       SendMessage(hwnd, WM_CUT, 0, 0);\r
7105       return 0;\r
7106     case IDM_Paste:\r
7107       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7108       return 0;\r
7109     case IDM_Copy:\r
7110       SendMessage(hwnd, WM_COPY, 0, 0);\r
7111       return 0;\r
7112     }\r
7113     break;\r
7114   }\r
7115   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7116 }\r
7117 \r
7118 #define CO_MAX  100000\r
7119 #define CO_TRIM   1000\r
7120 \r
7121 LRESULT CALLBACK\r
7122 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7123 {\r
7124   static SnapData sd;\r
7125   HWND hText, hInput;\r
7126   RECT rect;\r
7127   static int sizeX, sizeY;\r
7128   int newSizeX, newSizeY;\r
7129   MINMAXINFO *mmi;\r
7130   WORD wMask;\r
7131 \r
7132   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7133   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7134 \r
7135   switch (message) {\r
7136   case WM_NOTIFY:\r
7137     if (((NMHDR*)lParam)->code == EN_LINK)\r
7138     {\r
7139       ENLINK *pLink = (ENLINK*)lParam;\r
7140       if (pLink->msg == WM_LBUTTONUP)\r
7141       {\r
7142         TEXTRANGE tr;\r
7143 \r
7144         tr.chrg = pLink->chrg;\r
7145         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7146         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7147         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7148         free(tr.lpstrText);\r
7149       }\r
7150     }\r
7151     break;\r
7152   case WM_INITDIALOG: /* message: initialize dialog box */\r
7153     hwndConsole = hDlg;\r
7154     SetFocus(hInput);\r
7155     consoleTextWindowProc = (WNDPROC)\r
7156       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7157     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7158     consoleInputWindowProc = (WNDPROC)\r
7159       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7160     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7161     Colorize(ColorNormal, TRUE);\r
7162     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7163     ChangedConsoleFont();\r
7164     GetClientRect(hDlg, &rect);\r
7165     sizeX = rect.right;\r
7166     sizeY = rect.bottom;\r
7167     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7168         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7169       WINDOWPLACEMENT wp;\r
7170       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7171       wp.length = sizeof(WINDOWPLACEMENT);\r
7172       wp.flags = 0;\r
7173       wp.showCmd = SW_SHOW;\r
7174       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7175       wp.rcNormalPosition.left = wpConsole.x;\r
7176       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7177       wp.rcNormalPosition.top = wpConsole.y;\r
7178       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7179       SetWindowPlacement(hDlg, &wp);\r
7180     }\r
7181 \r
7182    // [HGM] Chessknight's change 2004-07-13\r
7183    else { /* Determine Defaults */\r
7184        WINDOWPLACEMENT wp;\r
7185        wpConsole.x = wpMain.width + 1;\r
7186        wpConsole.y = wpMain.y;\r
7187        wpConsole.width = screenWidth -  wpMain.width;\r
7188        wpConsole.height = wpMain.height;\r
7189        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7190        wp.length = sizeof(WINDOWPLACEMENT);\r
7191        wp.flags = 0;\r
7192        wp.showCmd = SW_SHOW;\r
7193        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7194        wp.rcNormalPosition.left = wpConsole.x;\r
7195        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7196        wp.rcNormalPosition.top = wpConsole.y;\r
7197        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7198        SetWindowPlacement(hDlg, &wp);\r
7199     }\r
7200 \r
7201    // Allow hText to highlight URLs and send notifications on them\r
7202    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7203    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7204    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7205    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7206 \r
7207     return FALSE;\r
7208 \r
7209   case WM_SETFOCUS:\r
7210     SetFocus(hInput);\r
7211     return 0;\r
7212 \r
7213   case WM_CLOSE:\r
7214     ExitEvent(0);\r
7215     /* not reached */\r
7216     break;\r
7217 \r
7218   case WM_SIZE:\r
7219     if (IsIconic(hDlg)) break;\r
7220     newSizeX = LOWORD(lParam);\r
7221     newSizeY = HIWORD(lParam);\r
7222     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7223       RECT rectText, rectInput;\r
7224       POINT pt;\r
7225       int newTextHeight, newTextWidth;\r
7226       GetWindowRect(hText, &rectText);\r
7227       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7228       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7229       if (newTextHeight < 0) {\r
7230         newSizeY += -newTextHeight;\r
7231         newTextHeight = 0;\r
7232       }\r
7233       SetWindowPos(hText, NULL, 0, 0,\r
7234         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7235       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7236       pt.x = rectInput.left;\r
7237       pt.y = rectInput.top + newSizeY - sizeY;\r
7238       ScreenToClient(hDlg, &pt);\r
7239       SetWindowPos(hInput, NULL, \r
7240         pt.x, pt.y, /* needs client coords */   \r
7241         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7242         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7243     }\r
7244     sizeX = newSizeX;\r
7245     sizeY = newSizeY;\r
7246     break;\r
7247 \r
7248   case WM_GETMINMAXINFO:\r
7249     /* Prevent resizing window too small */\r
7250     mmi = (MINMAXINFO *) lParam;\r
7251     mmi->ptMinTrackSize.x = 100;\r
7252     mmi->ptMinTrackSize.y = 100;\r
7253     break;\r
7254 \r
7255   /* [AS] Snapping */\r
7256   case WM_ENTERSIZEMOVE:\r
7257     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7258 \r
7259   case WM_SIZING:\r
7260     return OnSizing( &sd, hDlg, wParam, lParam );\r
7261 \r
7262   case WM_MOVING:\r
7263     return OnMoving( &sd, hDlg, wParam, lParam );\r
7264 \r
7265   case WM_EXITSIZEMOVE:\r
7266         UpdateICSWidth(hText);\r
7267     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7268   }\r
7269 \r
7270   return DefWindowProc(hDlg, message, wParam, lParam);\r
7271 }\r
7272 \r
7273 \r
7274 VOID\r
7275 ConsoleCreate()\r
7276 {\r
7277   HWND hCons;\r
7278   if (hwndConsole) return;\r
7279   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7280   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7281 }\r
7282 \r
7283 \r
7284 VOID\r
7285 ConsoleOutput(char* data, int length, int forceVisible)\r
7286 {\r
7287   HWND hText;\r
7288   int trim, exlen;\r
7289   char *p, *q;\r
7290   char buf[CO_MAX+1];\r
7291   POINT pEnd;\r
7292   RECT rect;\r
7293   static int delayLF = 0;\r
7294   CHARRANGE savesel, sel;\r
7295 \r
7296   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7297   p = data;\r
7298   q = buf;\r
7299   if (delayLF) {\r
7300     *q++ = '\r';\r
7301     *q++ = '\n';\r
7302     delayLF = 0;\r
7303   }\r
7304   while (length--) {\r
7305     if (*p == '\n') {\r
7306       if (*++p) {\r
7307         *q++ = '\r';\r
7308         *q++ = '\n';\r
7309       } else {\r
7310         delayLF = 1;\r
7311       }\r
7312     } else if (*p == '\007') {\r
7313        MyPlaySound(&sounds[(int)SoundBell]);\r
7314        p++;\r
7315     } else {\r
7316       *q++ = *p++;\r
7317     }\r
7318   }\r
7319   *q = NULLCHAR;\r
7320   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7321   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7322   /* Save current selection */\r
7323   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7324   exlen = GetWindowTextLength(hText);\r
7325   /* Find out whether current end of text is visible */\r
7326   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7327   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7328   /* Trim existing text if it's too long */\r
7329   if (exlen + (q - buf) > CO_MAX) {\r
7330     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7331     sel.cpMin = 0;\r
7332     sel.cpMax = trim;\r
7333     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7334     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7335     exlen -= trim;\r
7336     savesel.cpMin -= trim;\r
7337     savesel.cpMax -= trim;\r
7338     if (exlen < 0) exlen = 0;\r
7339     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7340     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7341   }\r
7342   /* Append the new text */\r
7343   sel.cpMin = exlen;\r
7344   sel.cpMax = exlen;\r
7345   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7346   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7347   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7348   if (forceVisible || exlen == 0 ||\r
7349       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7350        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7351     /* Scroll to make new end of text visible if old end of text\r
7352        was visible or new text is an echo of user typein */\r
7353     sel.cpMin = 9999999;\r
7354     sel.cpMax = 9999999;\r
7355     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7356     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7357     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7358     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7359   }\r
7360   if (savesel.cpMax == exlen || forceVisible) {\r
7361     /* Move insert point to new end of text if it was at the old\r
7362        end of text or if the new text is an echo of user typein */\r
7363     sel.cpMin = 9999999;\r
7364     sel.cpMax = 9999999;\r
7365     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7366   } else {\r
7367     /* Restore previous selection */\r
7368     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7369   }\r
7370   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7371 }\r
7372 \r
7373 /*---------*/\r
7374 \r
7375 \r
7376 void\r
7377 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7378 {\r
7379   char buf[100];\r
7380   char *str;\r
7381   COLORREF oldFg, oldBg;\r
7382   HFONT oldFont;\r
7383   RECT rect;\r
7384 \r
7385   if(copyNumber > 1)
7386     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7387 \r
7388   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7389   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7390   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7391 \r
7392   rect.left = x;\r
7393   rect.right = x + squareSize;\r
7394   rect.top  = y;\r
7395   rect.bottom = y + squareSize;\r
7396   str = buf;\r
7397 \r
7398   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7399                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7400              y, ETO_CLIPPED|ETO_OPAQUE,\r
7401              &rect, str, strlen(str), NULL);\r
7402 \r
7403   (void) SetTextColor(hdc, oldFg);\r
7404   (void) SetBkColor(hdc, oldBg);\r
7405   (void) SelectObject(hdc, oldFont);\r
7406 }\r
7407 \r
7408 void\r
7409 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7410               RECT *rect, char *color, char *flagFell)\r
7411 {\r
7412   char buf[100];\r
7413   char *str;\r
7414   COLORREF oldFg, oldBg;\r
7415   HFONT oldFont;\r
7416 \r
7417   if (appData.clockMode) {\r
7418     if (tinyLayout)\r
7419       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7420     else\r
7421       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7422     str = buf;\r
7423   } else {\r
7424     str = color;\r
7425   }\r
7426 \r
7427   if (highlight) {\r
7428     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7429     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7430   } else {\r
7431     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7432     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7433   }\r
7434   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7435 \r
7436   JAWS_SILENCE\r
7437 \r
7438   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7439              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7440              rect, str, strlen(str), NULL);\r
7441   if(logoHeight > 0 && appData.clockMode) {\r
7442       RECT r;\r
7443       str += strlen(color)+2;\r
7444       r.top = rect->top + logoHeight/2;\r
7445       r.left = rect->left;\r
7446       r.right = rect->right;\r
7447       r.bottom = rect->bottom;\r
7448       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7449                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7450                  &r, str, strlen(str), NULL);\r
7451   }\r
7452   (void) SetTextColor(hdc, oldFg);\r
7453   (void) SetBkColor(hdc, oldBg);\r
7454   (void) SelectObject(hdc, oldFont);\r
7455 }\r
7456 \r
7457 \r
7458 int\r
7459 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7460            OVERLAPPED *ovl)\r
7461 {\r
7462   int ok, err;\r
7463 \r
7464   /* [AS]  */\r
7465   if( count <= 0 ) {\r
7466     if (appData.debugMode) {\r
7467       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7468     }\r
7469 \r
7470     return ERROR_INVALID_USER_BUFFER;\r
7471   }\r
7472 \r
7473   ResetEvent(ovl->hEvent);\r
7474   ovl->Offset = ovl->OffsetHigh = 0;\r
7475   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7476   if (ok) {\r
7477     err = NO_ERROR;\r
7478   } else {\r
7479     err = GetLastError();\r
7480     if (err == ERROR_IO_PENDING) {\r
7481       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7482       if (ok)\r
7483         err = NO_ERROR;\r
7484       else\r
7485         err = GetLastError();\r
7486     }\r
7487   }\r
7488   return err;\r
7489 }\r
7490 \r
7491 int\r
7492 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7493             OVERLAPPED *ovl)\r
7494 {\r
7495   int ok, err;\r
7496 \r
7497   ResetEvent(ovl->hEvent);\r
7498   ovl->Offset = ovl->OffsetHigh = 0;\r
7499   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7500   if (ok) {\r
7501     err = NO_ERROR;\r
7502   } else {\r
7503     err = GetLastError();\r
7504     if (err == ERROR_IO_PENDING) {\r
7505       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7506       if (ok)\r
7507         err = NO_ERROR;\r
7508       else\r
7509         err = GetLastError();\r
7510     }\r
7511   }\r
7512   return err;\r
7513 }\r
7514 \r
7515 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7516 void CheckForInputBufferFull( InputSource * is )\r
7517 {\r
7518     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7519         /* Look for end of line */\r
7520         char * p = is->buf;\r
7521         \r
7522         while( p < is->next && *p != '\n' ) {\r
7523             p++;\r
7524         }\r
7525 \r
7526         if( p >= is->next ) {\r
7527             if (appData.debugMode) {\r
7528                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7529             }\r
7530 \r
7531             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7532             is->count = (DWORD) -1;\r
7533             is->next = is->buf;\r
7534         }\r
7535     }\r
7536 }\r
7537 \r
7538 DWORD\r
7539 InputThread(LPVOID arg)\r
7540 {\r
7541   InputSource *is;\r
7542   OVERLAPPED ovl;\r
7543 \r
7544   is = (InputSource *) arg;\r
7545   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7546   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7547   while (is->hThread != NULL) {\r
7548     is->error = DoReadFile(is->hFile, is->next,\r
7549                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7550                            &is->count, &ovl);\r
7551     if (is->error == NO_ERROR) {\r
7552       is->next += is->count;\r
7553     } else {\r
7554       if (is->error == ERROR_BROKEN_PIPE) {\r
7555         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7556         is->count = 0;\r
7557       } else {\r
7558         is->count = (DWORD) -1;\r
7559         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7560         break; \r
7561       }\r
7562     }\r
7563 \r
7564     CheckForInputBufferFull( is );\r
7565 \r
7566     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7567 \r
7568     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7569 \r
7570     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7571   }\r
7572 \r
7573   CloseHandle(ovl.hEvent);\r
7574   CloseHandle(is->hFile);\r
7575 \r
7576   if (appData.debugMode) {\r
7577     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7578   }\r
7579 \r
7580   return 0;\r
7581 }\r
7582 \r
7583 \r
7584 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7585 DWORD\r
7586 NonOvlInputThread(LPVOID arg)\r
7587 {\r
7588   InputSource *is;\r
7589   char *p, *q;\r
7590   int i;\r
7591   char prev;\r
7592 \r
7593   is = (InputSource *) arg;\r
7594   while (is->hThread != NULL) {\r
7595     is->error = ReadFile(is->hFile, is->next,\r
7596                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7597                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7598     if (is->error == NO_ERROR) {\r
7599       /* Change CRLF to LF */\r
7600       if (is->next > is->buf) {\r
7601         p = is->next - 1;\r
7602         i = is->count + 1;\r
7603       } else {\r
7604         p = is->next;\r
7605         i = is->count;\r
7606       }\r
7607       q = p;\r
7608       prev = NULLCHAR;\r
7609       while (i > 0) {\r
7610         if (prev == '\r' && *p == '\n') {\r
7611           *(q-1) = '\n';\r
7612           is->count--;\r
7613         } else { \r
7614           *q++ = *p;\r
7615         }\r
7616         prev = *p++;\r
7617         i--;\r
7618       }\r
7619       *q = NULLCHAR;\r
7620       is->next = q;\r
7621     } else {\r
7622       if (is->error == ERROR_BROKEN_PIPE) {\r
7623         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7624         is->count = 0; \r
7625       } else {\r
7626         is->count = (DWORD) -1;\r
7627       }\r
7628     }\r
7629 \r
7630     CheckForInputBufferFull( is );\r
7631 \r
7632     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7633 \r
7634     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7635 \r
7636     if (is->count < 0) break;  /* Quit on error */\r
7637   }\r
7638   CloseHandle(is->hFile);\r
7639   return 0;\r
7640 }\r
7641 \r
7642 DWORD\r
7643 SocketInputThread(LPVOID arg)\r
7644 {\r
7645   InputSource *is;\r
7646 \r
7647   is = (InputSource *) arg;\r
7648   while (is->hThread != NULL) {\r
7649     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7650     if ((int)is->count == SOCKET_ERROR) {\r
7651       is->count = (DWORD) -1;\r
7652       is->error = WSAGetLastError();\r
7653     } else {\r
7654       is->error = NO_ERROR;\r
7655       is->next += is->count;\r
7656       if (is->count == 0 && is->second == is) {\r
7657         /* End of file on stderr; quit with no message */\r
7658         break;\r
7659       }\r
7660     }\r
7661     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7662 \r
7663     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7664 \r
7665     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7666   }\r
7667   return 0;\r
7668 }\r
7669 \r
7670 VOID\r
7671 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7672 {\r
7673   InputSource *is;\r
7674 \r
7675   is = (InputSource *) lParam;\r
7676   if (is->lineByLine) {\r
7677     /* Feed in lines one by one */\r
7678     char *p = is->buf;\r
7679     char *q = p;\r
7680     while (q < is->next) {\r
7681       if (*q++ == '\n') {\r
7682         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7683         p = q;\r
7684       }\r
7685     }\r
7686     \r
7687     /* Move any partial line to the start of the buffer */\r
7688     q = is->buf;\r
7689     while (p < is->next) {\r
7690       *q++ = *p++;\r
7691     }\r
7692     is->next = q;\r
7693 \r
7694     if (is->error != NO_ERROR || is->count == 0) {\r
7695       /* Notify backend of the error.  Note: If there was a partial\r
7696          line at the end, it is not flushed through. */\r
7697       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7698     }\r
7699   } else {\r
7700     /* Feed in the whole chunk of input at once */\r
7701     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7702     is->next = is->buf;\r
7703   }\r
7704 }\r
7705 \r
7706 /*---------------------------------------------------------------------------*\\r
7707  *\r
7708  *  Menu enables. Used when setting various modes.\r
7709  *\r
7710 \*---------------------------------------------------------------------------*/\r
7711 \r
7712 typedef struct {\r
7713   int item;\r
7714   int flags;\r
7715 } Enables;\r
7716 \r
7717 VOID\r
7718 GreyRevert(Boolean grey)\r
7719 { // [HGM] vari: for retracting variations in local mode\r
7720   HMENU hmenu = GetMenu(hwndMain);\r
7721   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7722   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7723 }\r
7724 \r
7725 VOID\r
7726 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7727 {\r
7728   while (enab->item > 0) {\r
7729     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7730     enab++;\r
7731   }\r
7732 }\r
7733 \r
7734 Enables gnuEnables[] = {\r
7735   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7736   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7737   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7738   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7739   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7740   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7741   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7742   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7743   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7744   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7745   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7746   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7747   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7748   { -1, -1 }\r
7749 };\r
7750 \r
7751 Enables icsEnables[] = {\r
7752   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7753   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7754   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7755   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7756   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7757   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7758   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7759   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7760   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7761   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7762   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7763   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7764   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7765   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7766   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7767   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7768   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7769   { -1, -1 }\r
7770 };\r
7771 \r
7772 #if ZIPPY\r
7773 Enables zippyEnables[] = {\r
7774   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7775   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7776   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7777   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7778   { -1, -1 }\r
7779 };\r
7780 #endif\r
7781 \r
7782 Enables ncpEnables[] = {\r
7783   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7784   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7785   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7786   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7787   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7788   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7789   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7790   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7791   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7792   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7793   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7794   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7795   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7796   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7797   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7798   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7799   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7800   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7801   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7802   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7803   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7804   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7805   { -1, -1 }\r
7806 };\r
7807 \r
7808 Enables trainingOnEnables[] = {\r
7809   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7810   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7811   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7812   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7813   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7814   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7815   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7816   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7817   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7818   { -1, -1 }\r
7819 };\r
7820 \r
7821 Enables trainingOffEnables[] = {\r
7822   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7823   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7824   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7825   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7826   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7827   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7828   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7829   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7830   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7831   { -1, -1 }\r
7832 };\r
7833 \r
7834 /* These modify either ncpEnables or gnuEnables */\r
7835 Enables cmailEnables[] = {\r
7836   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7837   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7838   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7839   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7840   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7841   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7842   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7843   { -1, -1 }\r
7844 };\r
7845 \r
7846 Enables machineThinkingEnables[] = {\r
7847   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7848   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7849   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7850   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7851   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7852   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7853   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7854   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7855   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7856   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7857   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7858   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7859   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7860   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7861   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7862   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7863   { -1, -1 }\r
7864 };\r
7865 \r
7866 Enables userThinkingEnables[] = {\r
7867   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7868   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7869   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7870   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7871   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7872   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7873   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7874   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7875   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7876   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7877   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7878   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7879   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7880   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7881   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7882   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7883   { -1, -1 }\r
7884 };\r
7885 \r
7886 /*---------------------------------------------------------------------------*\\r
7887  *\r
7888  *  Front-end interface functions exported by XBoard.\r
7889  *  Functions appear in same order as prototypes in frontend.h.\r
7890  * \r
7891 \*---------------------------------------------------------------------------*/\r
7892 VOID\r
7893 ModeHighlight()\r
7894 {\r
7895   static UINT prevChecked = 0;\r
7896   static int prevPausing = 0;\r
7897   UINT nowChecked;\r
7898 \r
7899   if (pausing != prevPausing) {\r
7900     prevPausing = pausing;\r
7901     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7902                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7903     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7904   }\r
7905 \r
7906   switch (gameMode) {\r
7907   case BeginningOfGame:\r
7908     if (appData.icsActive)\r
7909       nowChecked = IDM_IcsClient;\r
7910     else if (appData.noChessProgram)\r
7911       nowChecked = IDM_EditGame;\r
7912     else\r
7913       nowChecked = IDM_MachineBlack;\r
7914     break;\r
7915   case MachinePlaysBlack:\r
7916     nowChecked = IDM_MachineBlack;\r
7917     break;\r
7918   case MachinePlaysWhite:\r
7919     nowChecked = IDM_MachineWhite;\r
7920     break;\r
7921   case TwoMachinesPlay:\r
7922     nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match\r
7923     break;\r
7924   case AnalyzeMode:\r
7925     nowChecked = IDM_AnalysisMode;\r
7926     break;\r
7927   case AnalyzeFile:\r
7928     nowChecked = IDM_AnalyzeFile;\r
7929     break;\r
7930   case EditGame:\r
7931     nowChecked = IDM_EditGame;\r
7932     break;\r
7933   case PlayFromGameFile:\r
7934     nowChecked = IDM_LoadGame;\r
7935     break;\r
7936   case EditPosition:\r
7937     nowChecked = IDM_EditPosition;\r
7938     break;\r
7939   case Training:\r
7940     nowChecked = IDM_Training;\r
7941     break;\r
7942   case IcsPlayingWhite:\r
7943   case IcsPlayingBlack:\r
7944   case IcsObserving:\r
7945   case IcsIdle:\r
7946     nowChecked = IDM_IcsClient;\r
7947     break;\r
7948   default:\r
7949   case EndOfGame:\r
7950     nowChecked = 0;\r
7951     break;\r
7952   }\r
7953   if (prevChecked != 0)\r
7954     (void) CheckMenuItem(GetMenu(hwndMain),\r
7955                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7956   if (nowChecked != 0)\r
7957     (void) CheckMenuItem(GetMenu(hwndMain),\r
7958                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
7959 \r
7960   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7961     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7962                           MF_BYCOMMAND|MF_ENABLED);\r
7963   } else {\r
7964     (void) EnableMenuItem(GetMenu(hwndMain), \r
7965                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7966   }\r
7967 \r
7968   prevChecked = nowChecked;\r
7969 \r
7970   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
7971   if (appData.icsActive) {\r
7972        if (appData.icsEngineAnalyze) {\r
7973                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7974                        MF_BYCOMMAND|MF_CHECKED);\r
7975        } else {\r
7976                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7977                        MF_BYCOMMAND|MF_UNCHECKED);\r
7978        }\r
7979   }\r
7980   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
7981 }\r
7982 \r
7983 VOID\r
7984 SetICSMode()\r
7985 {\r
7986   HMENU hmenu = GetMenu(hwndMain);\r
7987   SetMenuEnables(hmenu, icsEnables);\r
7988   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
7989     MF_BYCOMMAND|MF_ENABLED);\r
7990 #if ZIPPY\r
7991   if (appData.zippyPlay) {\r
7992     SetMenuEnables(hmenu, zippyEnables);\r
7993     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
7994          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7995           MF_BYCOMMAND|MF_ENABLED);\r
7996   }\r
7997 #endif\r
7998 }\r
7999 \r
8000 VOID\r
8001 SetGNUMode()\r
8002 {\r
8003   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8004 }\r
8005 \r
8006 VOID\r
8007 SetNCPMode()\r
8008 {\r
8009   HMENU hmenu = GetMenu(hwndMain);\r
8010   SetMenuEnables(hmenu, ncpEnables);\r
8011     DrawMenuBar(hwndMain);\r
8012 }\r
8013 \r
8014 VOID\r
8015 SetCmailMode()\r
8016 {\r
8017   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8018 }\r
8019 \r
8020 VOID \r
8021 SetTrainingModeOn()\r
8022 {\r
8023   int i;\r
8024   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8025   for (i = 0; i < N_BUTTONS; i++) {\r
8026     if (buttonDesc[i].hwnd != NULL)\r
8027       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8028   }\r
8029   CommentPopDown();\r
8030 }\r
8031 \r
8032 VOID SetTrainingModeOff()\r
8033 {\r
8034   int i;\r
8035   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8036   for (i = 0; i < N_BUTTONS; i++) {\r
8037     if (buttonDesc[i].hwnd != NULL)\r
8038       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8039   }\r
8040 }\r
8041 \r
8042 \r
8043 VOID\r
8044 SetUserThinkingEnables()\r
8045 {\r
8046   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8047 }\r
8048 \r
8049 VOID\r
8050 SetMachineThinkingEnables()\r
8051 {\r
8052   HMENU hMenu = GetMenu(hwndMain);\r
8053   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8054 \r
8055   SetMenuEnables(hMenu, machineThinkingEnables);\r
8056 \r
8057   if (gameMode == MachinePlaysBlack) {\r
8058     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8059   } else if (gameMode == MachinePlaysWhite) {\r
8060     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8061   } else if (gameMode == TwoMachinesPlay) {\r
8062     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8063   }\r
8064 }\r
8065 \r
8066 \r
8067 VOID\r
8068 DisplayTitle(char *str)\r
8069 {\r
8070   char title[MSG_SIZ], *host;\r
8071   if (str[0] != NULLCHAR) {\r
8072     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8073   } else if (appData.icsActive) {\r
8074     if (appData.icsCommPort[0] != NULLCHAR)\r
8075       host = "ICS";\r
8076     else \r
8077       host = appData.icsHost;\r
8078       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8079   } else if (appData.noChessProgram) {\r
8080     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8081   } else {\r
8082     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8083     strcat(title, ": ");\r
8084     strcat(title, first.tidy);\r
8085   }\r
8086   SetWindowText(hwndMain, title);\r
8087 }\r
8088 \r
8089 \r
8090 VOID\r
8091 DisplayMessage(char *str1, char *str2)\r
8092 {\r
8093   HDC hdc;\r
8094   HFONT oldFont;\r
8095   int remain = MESSAGE_TEXT_MAX - 1;\r
8096   int len;\r
8097 \r
8098   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8099   messageText[0] = NULLCHAR;\r
8100   if (*str1) {\r
8101     len = strlen(str1);\r
8102     if (len > remain) len = remain;\r
8103     strncpy(messageText, str1, len);\r
8104     messageText[len] = NULLCHAR;\r
8105     remain -= len;\r
8106   }\r
8107   if (*str2 && remain >= 2) {\r
8108     if (*str1) {\r
8109       strcat(messageText, "  ");\r
8110       remain -= 2;\r
8111     }\r
8112     len = strlen(str2);\r
8113     if (len > remain) len = remain;\r
8114     strncat(messageText, str2, len);\r
8115   }\r
8116   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8117 \r
8118   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8119 \r
8120   SAYMACHINEMOVE();\r
8121 \r
8122   hdc = GetDC(hwndMain);\r
8123   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8124   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8125              &messageRect, messageText, strlen(messageText), NULL);\r
8126   (void) SelectObject(hdc, oldFont);\r
8127   (void) ReleaseDC(hwndMain, hdc);\r
8128 }\r
8129 \r
8130 VOID\r
8131 DisplayError(char *str, int error)\r
8132 {\r
8133   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8134   int len;\r
8135 \r
8136   if (error == 0) {\r
8137     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8138   } else {\r
8139     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8140                         NULL, error, LANG_NEUTRAL,\r
8141                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8142     if (len > 0) {\r
8143       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8144     } else {\r
8145       ErrorMap *em = errmap;\r
8146       while (em->err != 0 && em->err != error) em++;\r
8147       if (em->err != 0) {\r
8148         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8149       } else {\r
8150         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8151       }\r
8152     }\r
8153   }\r
8154   \r
8155   ErrorPopUp(_("Error"), buf);\r
8156 }\r
8157 \r
8158 \r
8159 VOID\r
8160 DisplayMoveError(char *str)\r
8161 {\r
8162   fromX = fromY = -1;\r
8163   ClearHighlights();\r
8164   DrawPosition(FALSE, NULL);\r
8165   if (appData.popupMoveErrors) {\r
8166     ErrorPopUp(_("Error"), str);\r
8167   } else {\r
8168     DisplayMessage(str, "");\r
8169     moveErrorMessageUp = TRUE;\r
8170   }\r
8171 }\r
8172 \r
8173 VOID\r
8174 DisplayFatalError(char *str, int error, int exitStatus)\r
8175 {\r
8176   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8177   int len;\r
8178   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8179 \r
8180   if (error != 0) {\r
8181     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8182                         NULL, error, LANG_NEUTRAL,\r
8183                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8184     if (len > 0) {\r
8185       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8186     } else {\r
8187       ErrorMap *em = errmap;\r
8188       while (em->err != 0 && em->err != error) em++;\r
8189       if (em->err != 0) {\r
8190         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8191       } else {\r
8192         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8193       }\r
8194     }\r
8195     str = buf;\r
8196   }\r
8197   if (appData.debugMode) {\r
8198     fprintf(debugFP, "%s: %s\n", label, str);\r
8199   }\r
8200   if (appData.popupExitMessage) {\r
8201     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8202                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8203   }\r
8204   ExitEvent(exitStatus);\r
8205 }\r
8206 \r
8207 \r
8208 VOID\r
8209 DisplayInformation(char *str)\r
8210 {\r
8211   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8212 }\r
8213 \r
8214 \r
8215 VOID\r
8216 DisplayNote(char *str)\r
8217 {\r
8218   ErrorPopUp(_("Note"), str);\r
8219 }\r
8220 \r
8221 \r
8222 typedef struct {\r
8223   char *title, *question, *replyPrefix;\r
8224   ProcRef pr;\r
8225 } QuestionParams;\r
8226 \r
8227 LRESULT CALLBACK\r
8228 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8229 {\r
8230   static QuestionParams *qp;\r
8231   char reply[MSG_SIZ];\r
8232   int len, err;\r
8233 \r
8234   switch (message) {\r
8235   case WM_INITDIALOG:\r
8236     qp = (QuestionParams *) lParam;\r
8237     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8238     Translate(hDlg, DLG_Question);\r
8239     SetWindowText(hDlg, qp->title);\r
8240     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8241     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8242     return FALSE;\r
8243 \r
8244   case WM_COMMAND:\r
8245     switch (LOWORD(wParam)) {\r
8246     case IDOK:\r
8247       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8248       if (*reply) strcat(reply, " ");\r
8249       len = strlen(reply);\r
8250       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8251       strcat(reply, "\n");\r
8252       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8253       EndDialog(hDlg, TRUE);\r
8254       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8255       return TRUE;\r
8256     case IDCANCEL:\r
8257       EndDialog(hDlg, FALSE);\r
8258       return TRUE;\r
8259     default:\r
8260       break;\r
8261     }\r
8262     break;\r
8263   }\r
8264   return FALSE;\r
8265 }\r
8266 \r
8267 VOID\r
8268 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8269 {\r
8270     QuestionParams qp;\r
8271     FARPROC lpProc;\r
8272     \r
8273     qp.title = title;\r
8274     qp.question = question;\r
8275     qp.replyPrefix = replyPrefix;\r
8276     qp.pr = pr;\r
8277     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8278     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8279       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8280     FreeProcInstance(lpProc);\r
8281 }\r
8282 \r
8283 /* [AS] Pick FRC position */\r
8284 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8285 {\r
8286     static int * lpIndexFRC;\r
8287     BOOL index_is_ok;\r
8288     char buf[16];\r
8289 \r
8290     switch( message )\r
8291     {\r
8292     case WM_INITDIALOG:\r
8293         lpIndexFRC = (int *) lParam;\r
8294 \r
8295         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8296         Translate(hDlg, DLG_NewGameFRC);\r
8297 \r
8298         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8299         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8300         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8301         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8302 \r
8303         break;\r
8304 \r
8305     case WM_COMMAND:\r
8306         switch( LOWORD(wParam) ) {\r
8307         case IDOK:\r
8308             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8309             EndDialog( hDlg, 0 );\r
8310             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8311             return TRUE;\r
8312         case IDCANCEL:\r
8313             EndDialog( hDlg, 1 );   \r
8314             return TRUE;\r
8315         case IDC_NFG_Edit:\r
8316             if( HIWORD(wParam) == EN_CHANGE ) {\r
8317                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8318 \r
8319                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8320             }\r
8321             return TRUE;\r
8322         case IDC_NFG_Random:\r
8323           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8324             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8325             return TRUE;\r
8326         }\r
8327 \r
8328         break;\r
8329     }\r
8330 \r
8331     return FALSE;\r
8332 }\r
8333 \r
8334 int NewGameFRC()\r
8335 {\r
8336     int result;\r
8337     int index = appData.defaultFrcPosition;\r
8338     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8339 \r
8340     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8341 \r
8342     if( result == 0 ) {\r
8343         appData.defaultFrcPosition = index;\r
8344     }\r
8345 \r
8346     return result;\r
8347 }\r
8348 \r
8349 /* [AS] Game list options. Refactored by HGM */\r
8350 \r
8351 HWND gameListOptionsDialog;\r
8352 \r
8353 // low-level front-end: clear text edit / list widget\r
8354 void\r
8355 GLT_ClearList()\r
8356 {\r
8357     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8358 }\r
8359 \r
8360 // low-level front-end: clear text edit / list widget\r
8361 void\r
8362 GLT_DeSelectList()\r
8363 {\r
8364     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8365 }\r
8366 \r
8367 // low-level front-end: append line to text edit / list widget\r
8368 void\r
8369 GLT_AddToList( char *name )\r
8370 {\r
8371     if( name != 0 ) {\r
8372             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8373     }\r
8374 }\r
8375 \r
8376 // low-level front-end: get line from text edit / list widget\r
8377 Boolean\r
8378 GLT_GetFromList( int index, char *name )\r
8379 {\r
8380     if( name != 0 ) {\r
8381             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8382                 return TRUE;\r
8383     }\r
8384     return FALSE;\r
8385 }\r
8386 \r
8387 void GLT_MoveSelection( HWND hDlg, int delta )\r
8388 {\r
8389     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8390     int idx2 = idx1 + delta;\r
8391     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8392 \r
8393     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8394         char buf[128];\r
8395 \r
8396         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8397         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8398         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8399         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8400     }\r
8401 }\r
8402 \r
8403 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8404 {\r
8405     switch( message )\r
8406     {\r
8407     case WM_INITDIALOG:\r
8408         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8409         \r
8410         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8411         Translate(hDlg, DLG_GameListOptions);\r
8412 \r
8413         /* Initialize list */\r
8414         GLT_TagsToList( lpUserGLT );\r
8415 \r
8416         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8417 \r
8418         break;\r
8419 \r
8420     case WM_COMMAND:\r
8421         switch( LOWORD(wParam) ) {\r
8422         case IDOK:\r
8423             GLT_ParseList();\r
8424             EndDialog( hDlg, 0 );\r
8425             return TRUE;\r
8426         case IDCANCEL:\r
8427             EndDialog( hDlg, 1 );\r
8428             return TRUE;\r
8429 \r
8430         case IDC_GLT_Default:\r
8431             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8432             return TRUE;\r
8433 \r
8434         case IDC_GLT_Restore:\r
8435             GLT_TagsToList( appData.gameListTags );\r
8436             return TRUE;\r
8437 \r
8438         case IDC_GLT_Up:\r
8439             GLT_MoveSelection( hDlg, -1 );\r
8440             return TRUE;\r
8441 \r
8442         case IDC_GLT_Down:\r
8443             GLT_MoveSelection( hDlg, +1 );\r
8444             return TRUE;\r
8445         }\r
8446 \r
8447         break;\r
8448     }\r
8449 \r
8450     return FALSE;\r
8451 }\r
8452 \r
8453 int GameListOptions()\r
8454 {\r
8455     int result;\r
8456     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8457 \r
8458       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8459 \r
8460     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8461 \r
8462     if( result == 0 ) {\r
8463         /* [AS] Memory leak here! */\r
8464         appData.gameListTags = strdup( lpUserGLT ); \r
8465     }\r
8466 \r
8467     return result;\r
8468 }\r
8469 \r
8470 VOID\r
8471 DisplayIcsInteractionTitle(char *str)\r
8472 {\r
8473   char consoleTitle[MSG_SIZ];\r
8474 \r
8475     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8476   SetWindowText(hwndConsole, consoleTitle);\r
8477 }\r
8478 \r
8479 void\r
8480 DrawPosition(int fullRedraw, Board board)\r
8481 {\r
8482   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8483 }\r
8484 \r
8485 void NotifyFrontendLogin()\r
8486 {\r
8487         if (hwndConsole)\r
8488                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8489 }\r
8490 \r
8491 VOID\r
8492 ResetFrontEnd()\r
8493 {\r
8494   fromX = fromY = -1;\r
8495   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8496     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8497     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8498     dragInfo.lastpos = dragInfo.pos;\r
8499     dragInfo.start.x = dragInfo.start.y = -1;\r
8500     dragInfo.from = dragInfo.start;\r
8501     ReleaseCapture();\r
8502     DrawPosition(TRUE, NULL);\r
8503   }\r
8504   TagsPopDown();\r
8505 }\r
8506 \r
8507 \r
8508 VOID\r
8509 CommentPopUp(char *title, char *str)\r
8510 {\r
8511   HWND hwnd = GetActiveWindow();\r
8512   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8513   SAY(str);\r
8514   SetActiveWindow(hwnd);\r
8515 }\r
8516 \r
8517 VOID\r
8518 CommentPopDown(void)\r
8519 {\r
8520   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8521   if (commentDialog) {\r
8522     ShowWindow(commentDialog, SW_HIDE);\r
8523   }\r
8524   commentUp = FALSE;\r
8525 }\r
8526 \r
8527 VOID\r
8528 EditCommentPopUp(int index, char *title, char *str)\r
8529 {\r
8530   EitherCommentPopUp(index, title, str, TRUE);\r
8531 }\r
8532 \r
8533 \r
8534 VOID\r
8535 RingBell()\r
8536 {\r
8537   MyPlaySound(&sounds[(int)SoundMove]);\r
8538 }\r
8539 \r
8540 VOID PlayIcsWinSound()\r
8541 {\r
8542   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8543 }\r
8544 \r
8545 VOID PlayIcsLossSound()\r
8546 {\r
8547   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8548 }\r
8549 \r
8550 VOID PlayIcsDrawSound()\r
8551 {\r
8552   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8553 }\r
8554 \r
8555 VOID PlayIcsUnfinishedSound()\r
8556 {\r
8557   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8558 }\r
8559 \r
8560 VOID\r
8561 PlayAlarmSound()\r
8562 {\r
8563   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8564 }\r
8565 \r
8566 \r
8567 VOID\r
8568 EchoOn()\r
8569 {\r
8570   HWND hInput;\r
8571   consoleEcho = TRUE;\r
8572   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8573   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8574   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8575 }\r
8576 \r
8577 \r
8578 VOID\r
8579 EchoOff()\r
8580 {\r
8581   CHARFORMAT cf;\r
8582   HWND hInput;\r
8583   consoleEcho = FALSE;\r
8584   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8585   /* This works OK: set text and background both to the same color */\r
8586   cf = consoleCF;\r
8587   cf.crTextColor = COLOR_ECHOOFF;\r
8588   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8589   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8590 }\r
8591 \r
8592 /* No Raw()...? */\r
8593 \r
8594 void Colorize(ColorClass cc, int continuation)\r
8595 {\r
8596   currentColorClass = cc;\r
8597   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8598   consoleCF.crTextColor = textAttribs[cc].color;\r
8599   consoleCF.dwEffects = textAttribs[cc].effects;\r
8600   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8601 }\r
8602 \r
8603 char *\r
8604 UserName()\r
8605 {\r
8606   static char buf[MSG_SIZ];\r
8607   DWORD bufsiz = MSG_SIZ;\r
8608 \r
8609   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8610         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8611   }\r
8612   if (!GetUserName(buf, &bufsiz)) {\r
8613     /*DisplayError("Error getting user name", GetLastError());*/\r
8614     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8615   }\r
8616   return buf;\r
8617 }\r
8618 \r
8619 char *\r
8620 HostName()\r
8621 {\r
8622   static char buf[MSG_SIZ];\r
8623   DWORD bufsiz = MSG_SIZ;\r
8624 \r
8625   if (!GetComputerName(buf, &bufsiz)) {\r
8626     /*DisplayError("Error getting host name", GetLastError());*/\r
8627     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8628   }\r
8629   return buf;\r
8630 }\r
8631 \r
8632 \r
8633 int\r
8634 ClockTimerRunning()\r
8635 {\r
8636   return clockTimerEvent != 0;\r
8637 }\r
8638 \r
8639 int\r
8640 StopClockTimer()\r
8641 {\r
8642   if (clockTimerEvent == 0) return FALSE;\r
8643   KillTimer(hwndMain, clockTimerEvent);\r
8644   clockTimerEvent = 0;\r
8645   return TRUE;\r
8646 }\r
8647 \r
8648 void\r
8649 StartClockTimer(long millisec)\r
8650 {\r
8651   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8652                              (UINT) millisec, NULL);\r
8653 }\r
8654 \r
8655 void\r
8656 DisplayWhiteClock(long timeRemaining, int highlight)\r
8657 {\r
8658   HDC hdc;\r
8659   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8660 \r
8661   if(appData.noGUI) return;\r
8662   hdc = GetDC(hwndMain);\r
8663   if (!IsIconic(hwndMain)) {\r
8664     DisplayAClock(hdc, timeRemaining, highlight, \r
8665                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8666   }\r
8667   if (highlight && iconCurrent == iconBlack) {\r
8668     iconCurrent = iconWhite;\r
8669     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8670     if (IsIconic(hwndMain)) {\r
8671       DrawIcon(hdc, 2, 2, iconCurrent);\r
8672     }\r
8673   }\r
8674   (void) ReleaseDC(hwndMain, hdc);\r
8675   if (hwndConsole)\r
8676     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8677 }\r
8678 \r
8679 void\r
8680 DisplayBlackClock(long timeRemaining, int highlight)\r
8681 {\r
8682   HDC hdc;\r
8683   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8684 \r
8685   if(appData.noGUI) return;\r
8686   hdc = GetDC(hwndMain);\r
8687   if (!IsIconic(hwndMain)) {\r
8688     DisplayAClock(hdc, timeRemaining, highlight, \r
8689                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8690   }\r
8691   if (highlight && iconCurrent == iconWhite) {\r
8692     iconCurrent = iconBlack;\r
8693     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8694     if (IsIconic(hwndMain)) {\r
8695       DrawIcon(hdc, 2, 2, iconCurrent);\r
8696     }\r
8697   }\r
8698   (void) ReleaseDC(hwndMain, hdc);\r
8699   if (hwndConsole)\r
8700     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8701 }\r
8702 \r
8703 \r
8704 int\r
8705 LoadGameTimerRunning()\r
8706 {\r
8707   return loadGameTimerEvent != 0;\r
8708 }\r
8709 \r
8710 int\r
8711 StopLoadGameTimer()\r
8712 {\r
8713   if (loadGameTimerEvent == 0) return FALSE;\r
8714   KillTimer(hwndMain, loadGameTimerEvent);\r
8715   loadGameTimerEvent = 0;\r
8716   return TRUE;\r
8717 }\r
8718 \r
8719 void\r
8720 StartLoadGameTimer(long millisec)\r
8721 {\r
8722   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8723                                 (UINT) millisec, NULL);\r
8724 }\r
8725 \r
8726 void\r
8727 AutoSaveGame()\r
8728 {\r
8729   char *defName;\r
8730   FILE *f;\r
8731   char fileTitle[MSG_SIZ];\r
8732 \r
8733   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8734   f = OpenFileDialog(hwndMain, "a", defName,\r
8735                      appData.oldSaveStyle ? "gam" : "pgn",\r
8736                      GAME_FILT, \r
8737                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8738   if (f != NULL) {\r
8739     SaveGame(f, 0, "");\r
8740     fclose(f);\r
8741   }\r
8742 }\r
8743 \r
8744 \r
8745 void\r
8746 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8747 {\r
8748   if (delayedTimerEvent != 0) {\r
8749     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8750       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8751     }\r
8752     KillTimer(hwndMain, delayedTimerEvent);\r
8753     delayedTimerEvent = 0;\r
8754     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8755     delayedTimerCallback();\r
8756   }\r
8757   delayedTimerCallback = cb;\r
8758   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8759                                 (UINT) millisec, NULL);\r
8760 }\r
8761 \r
8762 DelayedEventCallback\r
8763 GetDelayedEvent()\r
8764 {\r
8765   if (delayedTimerEvent) {\r
8766     return delayedTimerCallback;\r
8767   } else {\r
8768     return NULL;\r
8769   }\r
8770 }\r
8771 \r
8772 void\r
8773 CancelDelayedEvent()\r
8774 {\r
8775   if (delayedTimerEvent) {\r
8776     KillTimer(hwndMain, delayedTimerEvent);\r
8777     delayedTimerEvent = 0;\r
8778   }\r
8779 }\r
8780 \r
8781 DWORD GetWin32Priority(int nice)\r
8782 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8783 /*\r
8784 REALTIME_PRIORITY_CLASS     0x00000100\r
8785 HIGH_PRIORITY_CLASS         0x00000080\r
8786 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8787 NORMAL_PRIORITY_CLASS       0x00000020\r
8788 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8789 IDLE_PRIORITY_CLASS         0x00000040\r
8790 */\r
8791         if (nice < -15) return 0x00000080;\r
8792         if (nice < 0)   return 0x00008000;\r
8793         if (nice == 0)  return 0x00000020;\r
8794         if (nice < 15)  return 0x00004000;\r
8795         return 0x00000040;\r
8796 }\r
8797 \r
8798 /* Start a child process running the given program.\r
8799    The process's standard output can be read from "from", and its\r
8800    standard input can be written to "to".\r
8801    Exit with fatal error if anything goes wrong.\r
8802    Returns an opaque pointer that can be used to destroy the process\r
8803    later.\r
8804 */\r
8805 int\r
8806 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8807 {\r
8808 #define BUFSIZE 4096\r
8809 \r
8810   HANDLE hChildStdinRd, hChildStdinWr,\r
8811     hChildStdoutRd, hChildStdoutWr;\r
8812   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8813   SECURITY_ATTRIBUTES saAttr;\r
8814   BOOL fSuccess;\r
8815   PROCESS_INFORMATION piProcInfo;\r
8816   STARTUPINFO siStartInfo;\r
8817   ChildProc *cp;\r
8818   char buf[MSG_SIZ];\r
8819   DWORD err;\r
8820 \r
8821   if (appData.debugMode) {\r
8822     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8823   }\r
8824 \r
8825   *pr = NoProc;\r
8826 \r
8827   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8828   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8829   saAttr.bInheritHandle = TRUE;\r
8830   saAttr.lpSecurityDescriptor = NULL;\r
8831 \r
8832   /*\r
8833    * The steps for redirecting child's STDOUT:\r
8834    *     1. Create anonymous pipe to be STDOUT for child.\r
8835    *     2. Create a noninheritable duplicate of read handle,\r
8836    *         and close the inheritable read handle.\r
8837    */\r
8838 \r
8839   /* Create a pipe for the child's STDOUT. */\r
8840   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8841     return GetLastError();\r
8842   }\r
8843 \r
8844   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8845   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8846                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8847                              FALSE,     /* not inherited */\r
8848                              DUPLICATE_SAME_ACCESS);\r
8849   if (! fSuccess) {\r
8850     return GetLastError();\r
8851   }\r
8852   CloseHandle(hChildStdoutRd);\r
8853 \r
8854   /*\r
8855    * The steps for redirecting child's STDIN:\r
8856    *     1. Create anonymous pipe to be STDIN for child.\r
8857    *     2. Create a noninheritable duplicate of write handle,\r
8858    *         and close the inheritable write handle.\r
8859    */\r
8860 \r
8861   /* Create a pipe for the child's STDIN. */\r
8862   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8863     return GetLastError();\r
8864   }\r
8865 \r
8866   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8867   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8868                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8869                              FALSE,     /* not inherited */\r
8870                              DUPLICATE_SAME_ACCESS);\r
8871   if (! fSuccess) {\r
8872     return GetLastError();\r
8873   }\r
8874   CloseHandle(hChildStdinWr);\r
8875 \r
8876   /* Arrange to (1) look in dir for the child .exe file, and\r
8877    * (2) have dir be the child's working directory.  Interpret\r
8878    * dir relative to the directory WinBoard loaded from. */\r
8879   GetCurrentDirectory(MSG_SIZ, buf);\r
8880   SetCurrentDirectory(installDir);\r
8881   SetCurrentDirectory(dir);\r
8882 \r
8883   /* Now create the child process. */\r
8884 \r
8885   siStartInfo.cb = sizeof(STARTUPINFO);\r
8886   siStartInfo.lpReserved = NULL;\r
8887   siStartInfo.lpDesktop = NULL;\r
8888   siStartInfo.lpTitle = NULL;\r
8889   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8890   siStartInfo.cbReserved2 = 0;\r
8891   siStartInfo.lpReserved2 = NULL;\r
8892   siStartInfo.hStdInput = hChildStdinRd;\r
8893   siStartInfo.hStdOutput = hChildStdoutWr;\r
8894   siStartInfo.hStdError = hChildStdoutWr;\r
8895 \r
8896   fSuccess = CreateProcess(NULL,\r
8897                            cmdLine,        /* command line */\r
8898                            NULL,           /* process security attributes */\r
8899                            NULL,           /* primary thread security attrs */\r
8900                            TRUE,           /* handles are inherited */\r
8901                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8902                            NULL,           /* use parent's environment */\r
8903                            NULL,\r
8904                            &siStartInfo, /* STARTUPINFO pointer */\r
8905                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8906 \r
8907   err = GetLastError();\r
8908   SetCurrentDirectory(buf); /* return to prev directory */\r
8909   if (! fSuccess) {\r
8910     return err;\r
8911   }\r
8912 \r
8913   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8914     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8915     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8916   }\r
8917 \r
8918   /* Close the handles we don't need in the parent */\r
8919   CloseHandle(piProcInfo.hThread);\r
8920   CloseHandle(hChildStdinRd);\r
8921   CloseHandle(hChildStdoutWr);\r
8922 \r
8923   /* Prepare return value */\r
8924   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8925   cp->kind = CPReal;\r
8926   cp->hProcess = piProcInfo.hProcess;\r
8927   cp->pid = piProcInfo.dwProcessId;\r
8928   cp->hFrom = hChildStdoutRdDup;\r
8929   cp->hTo = hChildStdinWrDup;\r
8930 \r
8931   *pr = (void *) cp;\r
8932 \r
8933   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8934      2000 where engines sometimes don't see the initial command(s)\r
8935      from WinBoard and hang.  I don't understand how that can happen,\r
8936      but the Sleep is harmless, so I've put it in.  Others have also\r
8937      reported what may be the same problem, so hopefully this will fix\r
8938      it for them too.  */\r
8939   Sleep(500);\r
8940 \r
8941   return NO_ERROR;\r
8942 }\r
8943 \r
8944 \r
8945 void\r
8946 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8947 {\r
8948   ChildProc *cp; int result;\r
8949 \r
8950   cp = (ChildProc *) pr;\r
8951   if (cp == NULL) return;\r
8952 \r
8953   switch (cp->kind) {\r
8954   case CPReal:\r
8955     /* TerminateProcess is considered harmful, so... */\r
8956     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8957     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8958     /* The following doesn't work because the chess program\r
8959        doesn't "have the same console" as WinBoard.  Maybe\r
8960        we could arrange for this even though neither WinBoard\r
8961        nor the chess program uses a console for stdio? */\r
8962     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8963 \r
8964     /* [AS] Special termination modes for misbehaving programs... */\r
8965     if( signal == 9 ) { \r
8966         result = TerminateProcess( cp->hProcess, 0 );\r
8967 \r
8968         if ( appData.debugMode) {\r
8969             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
8970         }\r
8971     }\r
8972     else if( signal == 10 ) {\r
8973         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
8974 \r
8975         if( dw != WAIT_OBJECT_0 ) {\r
8976             result = TerminateProcess( cp->hProcess, 0 );\r
8977 \r
8978             if ( appData.debugMode) {\r
8979                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
8980             }\r
8981 \r
8982         }\r
8983     }\r
8984 \r
8985     CloseHandle(cp->hProcess);\r
8986     break;\r
8987 \r
8988   case CPComm:\r
8989     if (cp->hFrom) CloseHandle(cp->hFrom);\r
8990     break;\r
8991 \r
8992   case CPSock:\r
8993     closesocket(cp->sock);\r
8994     WSACleanup();\r
8995     break;\r
8996 \r
8997   case CPRcmd:\r
8998     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
8999     closesocket(cp->sock);\r
9000     closesocket(cp->sock2);\r
9001     WSACleanup();\r
9002     break;\r
9003   }\r
9004   free(cp);\r
9005 }\r
9006 \r
9007 void\r
9008 InterruptChildProcess(ProcRef pr)\r
9009 {\r
9010   ChildProc *cp;\r
9011 \r
9012   cp = (ChildProc *) pr;\r
9013   if (cp == NULL) return;\r
9014   switch (cp->kind) {\r
9015   case CPReal:\r
9016     /* The following doesn't work because the chess program\r
9017        doesn't "have the same console" as WinBoard.  Maybe\r
9018        we could arrange for this even though neither WinBoard\r
9019        nor the chess program uses a console for stdio */\r
9020     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9021     break;\r
9022 \r
9023   case CPComm:\r
9024   case CPSock:\r
9025     /* Can't interrupt */\r
9026     break;\r
9027 \r
9028   case CPRcmd:\r
9029     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9030     break;\r
9031   }\r
9032 }\r
9033 \r
9034 \r
9035 int\r
9036 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9037 {\r
9038   char cmdLine[MSG_SIZ];\r
9039 \r
9040   if (port[0] == NULLCHAR) {\r
9041     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9042   } else {\r
9043     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9044   }\r
9045   return StartChildProcess(cmdLine, "", pr);\r
9046 }\r
9047 \r
9048 \r
9049 /* Code to open TCP sockets */\r
9050 \r
9051 int\r
9052 OpenTCP(char *host, char *port, ProcRef *pr)\r
9053 {\r
9054   ChildProc *cp;\r
9055   int err;\r
9056   SOCKET s;\r
9057   struct sockaddr_in sa, mysa;\r
9058   struct hostent FAR *hp;\r
9059   unsigned short uport;\r
9060   WORD wVersionRequested;\r
9061   WSADATA wsaData;\r
9062 \r
9063   /* Initialize socket DLL */\r
9064   wVersionRequested = MAKEWORD(1, 1);\r
9065   err = WSAStartup(wVersionRequested, &wsaData);\r
9066   if (err != 0) return err;\r
9067 \r
9068   /* Make socket */\r
9069   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9070     err = WSAGetLastError();\r
9071     WSACleanup();\r
9072     return err;\r
9073   }\r
9074 \r
9075   /* Bind local address using (mostly) don't-care values.\r
9076    */\r
9077   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9078   mysa.sin_family = AF_INET;\r
9079   mysa.sin_addr.s_addr = INADDR_ANY;\r
9080   uport = (unsigned short) 0;\r
9081   mysa.sin_port = htons(uport);\r
9082   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9083       == SOCKET_ERROR) {\r
9084     err = WSAGetLastError();\r
9085     WSACleanup();\r
9086     return err;\r
9087   }\r
9088 \r
9089   /* Resolve remote host name */\r
9090   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9091   if (!(hp = gethostbyname(host))) {\r
9092     unsigned int b0, b1, b2, b3;\r
9093 \r
9094     err = WSAGetLastError();\r
9095 \r
9096     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9097       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9098       hp->h_addrtype = AF_INET;\r
9099       hp->h_length = 4;\r
9100       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9101       hp->h_addr_list[0] = (char *) malloc(4);\r
9102       hp->h_addr_list[0][0] = (char) b0;\r
9103       hp->h_addr_list[0][1] = (char) b1;\r
9104       hp->h_addr_list[0][2] = (char) b2;\r
9105       hp->h_addr_list[0][3] = (char) b3;\r
9106     } else {\r
9107       WSACleanup();\r
9108       return err;\r
9109     }\r
9110   }\r
9111   sa.sin_family = hp->h_addrtype;\r
9112   uport = (unsigned short) atoi(port);\r
9113   sa.sin_port = htons(uport);\r
9114   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9115 \r
9116   /* Make connection */\r
9117   if (connect(s, (struct sockaddr *) &sa,\r
9118               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9119     err = WSAGetLastError();\r
9120     WSACleanup();\r
9121     return err;\r
9122   }\r
9123 \r
9124   /* Prepare return value */\r
9125   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9126   cp->kind = CPSock;\r
9127   cp->sock = s;\r
9128   *pr = (ProcRef *) cp;\r
9129 \r
9130   return NO_ERROR;\r
9131 }\r
9132 \r
9133 int\r
9134 OpenCommPort(char *name, ProcRef *pr)\r
9135 {\r
9136   HANDLE h;\r
9137   COMMTIMEOUTS ct;\r
9138   ChildProc *cp;\r
9139   char fullname[MSG_SIZ];\r
9140 \r
9141   if (*name != '\\')\r
9142     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9143   else\r
9144     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9145 \r
9146   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9147                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9148   if (h == (HANDLE) -1) {\r
9149     return GetLastError();\r
9150   }\r
9151   hCommPort = h;\r
9152 \r
9153   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9154 \r
9155   /* Accumulate characters until a 100ms pause, then parse */\r
9156   ct.ReadIntervalTimeout = 100;\r
9157   ct.ReadTotalTimeoutMultiplier = 0;\r
9158   ct.ReadTotalTimeoutConstant = 0;\r
9159   ct.WriteTotalTimeoutMultiplier = 0;\r
9160   ct.WriteTotalTimeoutConstant = 0;\r
9161   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9162 \r
9163   /* Prepare return value */\r
9164   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9165   cp->kind = CPComm;\r
9166   cp->hFrom = h;\r
9167   cp->hTo = h;\r
9168   *pr = (ProcRef *) cp;\r
9169 \r
9170   return NO_ERROR;\r
9171 }\r
9172 \r
9173 int\r
9174 OpenLoopback(ProcRef *pr)\r
9175 {\r
9176   DisplayFatalError(_("Not implemented"), 0, 1);\r
9177   return NO_ERROR;\r
9178 }\r
9179 \r
9180 \r
9181 int\r
9182 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9183 {\r
9184   ChildProc *cp;\r
9185   int err;\r
9186   SOCKET s, s2, s3;\r
9187   struct sockaddr_in sa, mysa;\r
9188   struct hostent FAR *hp;\r
9189   unsigned short uport;\r
9190   WORD wVersionRequested;\r
9191   WSADATA wsaData;\r
9192   int fromPort;\r
9193   char stderrPortStr[MSG_SIZ];\r
9194 \r
9195   /* Initialize socket DLL */\r
9196   wVersionRequested = MAKEWORD(1, 1);\r
9197   err = WSAStartup(wVersionRequested, &wsaData);\r
9198   if (err != 0) return err;\r
9199 \r
9200   /* Resolve remote host name */\r
9201   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9202   if (!(hp = gethostbyname(host))) {\r
9203     unsigned int b0, b1, b2, b3;\r
9204 \r
9205     err = WSAGetLastError();\r
9206 \r
9207     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9208       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9209       hp->h_addrtype = AF_INET;\r
9210       hp->h_length = 4;\r
9211       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9212       hp->h_addr_list[0] = (char *) malloc(4);\r
9213       hp->h_addr_list[0][0] = (char) b0;\r
9214       hp->h_addr_list[0][1] = (char) b1;\r
9215       hp->h_addr_list[0][2] = (char) b2;\r
9216       hp->h_addr_list[0][3] = (char) b3;\r
9217     } else {\r
9218       WSACleanup();\r
9219       return err;\r
9220     }\r
9221   }\r
9222   sa.sin_family = hp->h_addrtype;\r
9223   uport = (unsigned short) 514;\r
9224   sa.sin_port = htons(uport);\r
9225   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9226 \r
9227   /* Bind local socket to unused "privileged" port address\r
9228    */\r
9229   s = INVALID_SOCKET;\r
9230   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9231   mysa.sin_family = AF_INET;\r
9232   mysa.sin_addr.s_addr = INADDR_ANY;\r
9233   for (fromPort = 1023;; fromPort--) {\r
9234     if (fromPort < 0) {\r
9235       WSACleanup();\r
9236       return WSAEADDRINUSE;\r
9237     }\r
9238     if (s == INVALID_SOCKET) {\r
9239       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9240         err = WSAGetLastError();\r
9241         WSACleanup();\r
9242         return err;\r
9243       }\r
9244     }\r
9245     uport = (unsigned short) fromPort;\r
9246     mysa.sin_port = htons(uport);\r
9247     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9248         == SOCKET_ERROR) {\r
9249       err = WSAGetLastError();\r
9250       if (err == WSAEADDRINUSE) continue;\r
9251       WSACleanup();\r
9252       return err;\r
9253     }\r
9254     if (connect(s, (struct sockaddr *) &sa,\r
9255       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9256       err = WSAGetLastError();\r
9257       if (err == WSAEADDRINUSE) {\r
9258         closesocket(s);\r
9259         s = -1;\r
9260         continue;\r
9261       }\r
9262       WSACleanup();\r
9263       return err;\r
9264     }\r
9265     break;\r
9266   }\r
9267 \r
9268   /* Bind stderr local socket to unused "privileged" port address\r
9269    */\r
9270   s2 = INVALID_SOCKET;\r
9271   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9272   mysa.sin_family = AF_INET;\r
9273   mysa.sin_addr.s_addr = INADDR_ANY;\r
9274   for (fromPort = 1023;; fromPort--) {\r
9275     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9276     if (fromPort < 0) {\r
9277       (void) closesocket(s);\r
9278       WSACleanup();\r
9279       return WSAEADDRINUSE;\r
9280     }\r
9281     if (s2 == INVALID_SOCKET) {\r
9282       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9283         err = WSAGetLastError();\r
9284         closesocket(s);\r
9285         WSACleanup();\r
9286         return err;\r
9287       }\r
9288     }\r
9289     uport = (unsigned short) fromPort;\r
9290     mysa.sin_port = htons(uport);\r
9291     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9292         == SOCKET_ERROR) {\r
9293       err = WSAGetLastError();\r
9294       if (err == WSAEADDRINUSE) continue;\r
9295       (void) closesocket(s);\r
9296       WSACleanup();\r
9297       return err;\r
9298     }\r
9299     if (listen(s2, 1) == SOCKET_ERROR) {\r
9300       err = WSAGetLastError();\r
9301       if (err == WSAEADDRINUSE) {\r
9302         closesocket(s2);\r
9303         s2 = INVALID_SOCKET;\r
9304         continue;\r
9305       }\r
9306       (void) closesocket(s);\r
9307       (void) closesocket(s2);\r
9308       WSACleanup();\r
9309       return err;\r
9310     }\r
9311     break;\r
9312   }\r
9313   prevStderrPort = fromPort; // remember port used\r
9314   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9315 \r
9316   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9317     err = WSAGetLastError();\r
9318     (void) closesocket(s);\r
9319     (void) closesocket(s2);\r
9320     WSACleanup();\r
9321     return err;\r
9322   }\r
9323 \r
9324   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9325     err = WSAGetLastError();\r
9326     (void) closesocket(s);\r
9327     (void) closesocket(s2);\r
9328     WSACleanup();\r
9329     return err;\r
9330   }\r
9331   if (*user == NULLCHAR) user = UserName();\r
9332   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9333     err = WSAGetLastError();\r
9334     (void) closesocket(s);\r
9335     (void) closesocket(s2);\r
9336     WSACleanup();\r
9337     return err;\r
9338   }\r
9339   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9340     err = WSAGetLastError();\r
9341     (void) closesocket(s);\r
9342     (void) closesocket(s2);\r
9343     WSACleanup();\r
9344     return err;\r
9345   }\r
9346 \r
9347   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9348     err = WSAGetLastError();\r
9349     (void) closesocket(s);\r
9350     (void) closesocket(s2);\r
9351     WSACleanup();\r
9352     return err;\r
9353   }\r
9354   (void) closesocket(s2);  /* Stop listening */\r
9355 \r
9356   /* Prepare return value */\r
9357   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9358   cp->kind = CPRcmd;\r
9359   cp->sock = s;\r
9360   cp->sock2 = s3;\r
9361   *pr = (ProcRef *) cp;\r
9362 \r
9363   return NO_ERROR;\r
9364 }\r
9365 \r
9366 \r
9367 InputSourceRef\r
9368 AddInputSource(ProcRef pr, int lineByLine,\r
9369                InputCallback func, VOIDSTAR closure)\r
9370 {\r
9371   InputSource *is, *is2 = NULL;\r
9372   ChildProc *cp = (ChildProc *) pr;\r
9373 \r
9374   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9375   is->lineByLine = lineByLine;\r
9376   is->func = func;\r
9377   is->closure = closure;\r
9378   is->second = NULL;\r
9379   is->next = is->buf;\r
9380   if (pr == NoProc) {\r
9381     is->kind = CPReal;\r
9382     consoleInputSource = is;\r
9383   } else {\r
9384     is->kind = cp->kind;\r
9385     /* \r
9386         [AS] Try to avoid a race condition if the thread is given control too early:\r
9387         we create all threads suspended so that the is->hThread variable can be\r
9388         safely assigned, then let the threads start with ResumeThread.\r
9389     */\r
9390     switch (cp->kind) {\r
9391     case CPReal:\r
9392       is->hFile = cp->hFrom;\r
9393       cp->hFrom = NULL; /* now owned by InputThread */\r
9394       is->hThread =\r
9395         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9396                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9397       break;\r
9398 \r
9399     case CPComm:\r
9400       is->hFile = cp->hFrom;\r
9401       cp->hFrom = NULL; /* now owned by InputThread */\r
9402       is->hThread =\r
9403         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9404                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9405       break;\r
9406 \r
9407     case CPSock:\r
9408       is->sock = cp->sock;\r
9409       is->hThread =\r
9410         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9411                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9412       break;\r
9413 \r
9414     case CPRcmd:\r
9415       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9416       *is2 = *is;\r
9417       is->sock = cp->sock;\r
9418       is->second = is2;\r
9419       is2->sock = cp->sock2;\r
9420       is2->second = is2;\r
9421       is->hThread =\r
9422         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9423                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9424       is2->hThread =\r
9425         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9426                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9427       break;\r
9428     }\r
9429 \r
9430     if( is->hThread != NULL ) {\r
9431         ResumeThread( is->hThread );\r
9432     }\r
9433 \r
9434     if( is2 != NULL && is2->hThread != NULL ) {\r
9435         ResumeThread( is2->hThread );\r
9436     }\r
9437   }\r
9438 \r
9439   return (InputSourceRef) is;\r
9440 }\r
9441 \r
9442 void\r
9443 RemoveInputSource(InputSourceRef isr)\r
9444 {\r
9445   InputSource *is;\r
9446 \r
9447   is = (InputSource *) isr;\r
9448   is->hThread = NULL;  /* tell thread to stop */\r
9449   CloseHandle(is->hThread);\r
9450   if (is->second != NULL) {\r
9451     is->second->hThread = NULL;\r
9452     CloseHandle(is->second->hThread);\r
9453   }\r
9454 }\r
9455 \r
9456 int no_wrap(char *message, int count)\r
9457 {\r
9458     ConsoleOutput(message, count, FALSE);\r
9459     return count;\r
9460 }\r
9461 \r
9462 int\r
9463 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9464 {\r
9465   DWORD dOutCount;\r
9466   int outCount = SOCKET_ERROR;\r
9467   ChildProc *cp = (ChildProc *) pr;\r
9468   static OVERLAPPED ovl;\r
9469   static int line = 0;\r
9470 \r
9471   if (pr == NoProc)\r
9472   {\r
9473     if (appData.noJoin || !appData.useInternalWrap)\r
9474       return no_wrap(message, count);\r
9475     else\r
9476     {\r
9477       int width = get_term_width();\r
9478       int len = wrap(NULL, message, count, width, &line);\r
9479       char *msg = malloc(len);\r
9480       int dbgchk;\r
9481 \r
9482       if (!msg)\r
9483         return no_wrap(message, count);\r
9484       else\r
9485       {\r
9486         dbgchk = wrap(msg, message, count, width, &line);\r
9487         if (dbgchk != len && appData.debugMode)\r
9488             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9489         ConsoleOutput(msg, len, FALSE);\r
9490         free(msg);\r
9491         return len;\r
9492       }\r
9493     }\r
9494   }\r
9495 \r
9496   if (ovl.hEvent == NULL) {\r
9497     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9498   }\r
9499   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9500 \r
9501   switch (cp->kind) {\r
9502   case CPSock:\r
9503   case CPRcmd:\r
9504     outCount = send(cp->sock, message, count, 0);\r
9505     if (outCount == SOCKET_ERROR) {\r
9506       *outError = WSAGetLastError();\r
9507     } else {\r
9508       *outError = NO_ERROR;\r
9509     }\r
9510     break;\r
9511 \r
9512   case CPReal:\r
9513     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9514                   &dOutCount, NULL)) {\r
9515       *outError = NO_ERROR;\r
9516       outCount = (int) dOutCount;\r
9517     } else {\r
9518       *outError = GetLastError();\r
9519     }\r
9520     break;\r
9521 \r
9522   case CPComm:\r
9523     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9524                             &dOutCount, &ovl);\r
9525     if (*outError == NO_ERROR) {\r
9526       outCount = (int) dOutCount;\r
9527     }\r
9528     break;\r
9529   }\r
9530   return outCount;\r
9531 }\r
9532 \r
9533 int\r
9534 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9535                        long msdelay)\r
9536 {\r
9537   /* Ignore delay, not implemented for WinBoard */\r
9538   return OutputToProcess(pr, message, count, outError);\r
9539 }\r
9540 \r
9541 \r
9542 void\r
9543 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9544                         char *buf, int count, int error)\r
9545 {\r
9546   DisplayFatalError(_("Not implemented"), 0, 1);\r
9547 }\r
9548 \r
9549 /* see wgamelist.c for Game List functions */\r
9550 /* see wedittags.c for Edit Tags functions */\r
9551 \r
9552 \r
9553 VOID\r
9554 ICSInitScript()\r
9555 {\r
9556   FILE *f;\r
9557   char buf[MSG_SIZ];\r
9558   char *dummy;\r
9559 \r
9560   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9561     f = fopen(buf, "r");\r
9562     if (f != NULL) {\r
9563       ProcessICSInitScript(f);\r
9564       fclose(f);\r
9565     }\r
9566   }\r
9567 }\r
9568 \r
9569 \r
9570 VOID\r
9571 StartAnalysisClock()\r
9572 {\r
9573   if (analysisTimerEvent) return;\r
9574   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9575                                         (UINT) 2000, NULL);\r
9576 }\r
9577 \r
9578 VOID\r
9579 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9580 {\r
9581   highlightInfo.sq[0].x = fromX;\r
9582   highlightInfo.sq[0].y = fromY;\r
9583   highlightInfo.sq[1].x = toX;\r
9584   highlightInfo.sq[1].y = toY;\r
9585 }\r
9586 \r
9587 VOID\r
9588 ClearHighlights()\r
9589 {\r
9590   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9591     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9592 }\r
9593 \r
9594 VOID\r
9595 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9596 {\r
9597   premoveHighlightInfo.sq[0].x = fromX;\r
9598   premoveHighlightInfo.sq[0].y = fromY;\r
9599   premoveHighlightInfo.sq[1].x = toX;\r
9600   premoveHighlightInfo.sq[1].y = toY;\r
9601 }\r
9602 \r
9603 VOID\r
9604 ClearPremoveHighlights()\r
9605 {\r
9606   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9607     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9608 }\r
9609 \r
9610 VOID\r
9611 ShutDownFrontEnd()\r
9612 {\r
9613   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9614   DeleteClipboardTempFiles();\r
9615 }\r
9616 \r
9617 void\r
9618 BoardToTop()\r
9619 {\r
9620     if (IsIconic(hwndMain))\r
9621       ShowWindow(hwndMain, SW_RESTORE);\r
9622 \r
9623     SetActiveWindow(hwndMain);\r
9624 }\r
9625 \r
9626 /*\r
9627  * Prototypes for animation support routines\r
9628  */\r
9629 static void ScreenSquare(int column, int row, POINT * pt);\r
9630 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9631      POINT frames[], int * nFrames);\r
9632 \r
9633 \r
9634 #define kFactor 4\r
9635 \r
9636 void\r
9637 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9638 {       // [HGM] atomic: animate blast wave\r
9639         int i;\r
9640 \r
9641         explodeInfo.fromX = fromX;\r
9642         explodeInfo.fromY = fromY;\r
9643         explodeInfo.toX = toX;\r
9644         explodeInfo.toY = toY;\r
9645         for(i=1; i<4*kFactor; i++) {\r
9646             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9647             DrawPosition(FALSE, board);\r
9648             Sleep(appData.animSpeed);\r
9649         }\r
9650         explodeInfo.radius = 0;\r
9651         DrawPosition(TRUE, board);\r
9652 }\r
9653 \r
9654 void\r
9655 AnimateMove(board, fromX, fromY, toX, toY)\r
9656      Board board;\r
9657      int fromX;\r
9658      int fromY;\r
9659      int toX;\r
9660      int toY;\r
9661 {\r
9662   ChessSquare piece;\r
9663   POINT start, finish, mid;\r
9664   POINT frames[kFactor * 2 + 1];\r
9665   int nFrames, n;\r
9666 \r
9667   if (!appData.animate) return;\r
9668   if (doingSizing) return;\r
9669   if (fromY < 0 || fromX < 0) return;\r
9670   piece = board[fromY][fromX];\r
9671   if (piece >= EmptySquare) return;\r
9672 \r
9673   ScreenSquare(fromX, fromY, &start);\r
9674   ScreenSquare(toX, toY, &finish);\r
9675 \r
9676   /* All moves except knight jumps move in straight line */\r
9677   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9678     mid.x = start.x + (finish.x - start.x) / 2;\r
9679     mid.y = start.y + (finish.y - start.y) / 2;\r
9680   } else {\r
9681     /* Knight: make straight movement then diagonal */\r
9682     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9683        mid.x = start.x + (finish.x - start.x) / 2;\r
9684        mid.y = start.y;\r
9685      } else {\r
9686        mid.x = start.x;\r
9687        mid.y = start.y + (finish.y - start.y) / 2;\r
9688      }\r
9689   }\r
9690   \r
9691   /* Don't use as many frames for very short moves */\r
9692   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9693     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9694   else\r
9695     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9696 \r
9697   animInfo.from.x = fromX;\r
9698   animInfo.from.y = fromY;\r
9699   animInfo.to.x = toX;\r
9700   animInfo.to.y = toY;\r
9701   animInfo.lastpos = start;\r
9702   animInfo.piece = piece;\r
9703   for (n = 0; n < nFrames; n++) {\r
9704     animInfo.pos = frames[n];\r
9705     DrawPosition(FALSE, NULL);\r
9706     animInfo.lastpos = animInfo.pos;\r
9707     Sleep(appData.animSpeed);\r
9708   }\r
9709   animInfo.pos = finish;\r
9710   DrawPosition(FALSE, NULL);\r
9711   animInfo.piece = EmptySquare;\r
9712   Explode(board, fromX, fromY, toX, toY);\r
9713 }\r
9714 \r
9715 /*      Convert board position to corner of screen rect and color       */\r
9716 \r
9717 static void\r
9718 ScreenSquare(column, row, pt)\r
9719      int column; int row; POINT * pt;\r
9720 {\r
9721   if (flipView) {\r
9722     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9723     pt->y = lineGap + row * (squareSize + lineGap);\r
9724   } else {\r
9725     pt->x = lineGap + column * (squareSize + lineGap);\r
9726     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9727   }\r
9728 }\r
9729 \r
9730 /*      Generate a series of frame coords from start->mid->finish.\r
9731         The movement rate doubles until the half way point is\r
9732         reached, then halves back down to the final destination,\r
9733         which gives a nice slow in/out effect. The algorithmn\r
9734         may seem to generate too many intermediates for short\r
9735         moves, but remember that the purpose is to attract the\r
9736         viewers attention to the piece about to be moved and\r
9737         then to where it ends up. Too few frames would be less\r
9738         noticeable.                                             */\r
9739 \r
9740 static void\r
9741 Tween(start, mid, finish, factor, frames, nFrames)\r
9742      POINT * start; POINT * mid;\r
9743      POINT * finish; int factor;\r
9744      POINT frames[]; int * nFrames;\r
9745 {\r
9746   int n, fraction = 1, count = 0;\r
9747 \r
9748   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9749   for (n = 0; n < factor; n++)\r
9750     fraction *= 2;\r
9751   for (n = 0; n < factor; n++) {\r
9752     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9753     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9754     count ++;\r
9755     fraction = fraction / 2;\r
9756   }\r
9757   \r
9758   /* Midpoint */\r
9759   frames[count] = *mid;\r
9760   count ++;\r
9761   \r
9762   /* Slow out, stepping 1/2, then 1/4, ... */\r
9763   fraction = 2;\r
9764   for (n = 0; n < factor; n++) {\r
9765     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9766     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9767     count ++;\r
9768     fraction = fraction * 2;\r
9769   }\r
9770   *nFrames = count;\r
9771 }\r
9772 \r
9773 void\r
9774 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9775 {\r
9776     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9777 \r
9778     EvalGraphSet( first, last, current, pvInfoList );\r
9779 }\r
9780 \r
9781 void\r
9782 SettingsPopUp(ChessProgramState *cps)\r
9783 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9784       EngineOptionsPopup(savedHwnd, cps);\r
9785 }\r