Refactor move type-in code
[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[1000000]; \r
3846     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3847 \r
3848     GetObject(bufferBitmap, sizeof(b), &b);\r
3849     if(b.bmWidthBytes*b.bmHeight <= 990000) {\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      }\r
3914   }\r
3915 \r
3916   SelectObject(tmphdc, oldBitmap);\r
3917 \r
3918   /* Massive cleanup */\r
3919   for (x = 0; x < num_clips; x++)\r
3920     DeleteObject(clips[x]);\r
3921 \r
3922   DeleteDC(tmphdc);\r
3923   DeleteObject(bufferBitmap);\r
3924 \r
3925   if (releaseDC) \r
3926     ReleaseDC(hwndMain, hdc);\r
3927   \r
3928   if (lastDrawnFlipView != flipView && nr == 0) {\r
3929     if (flipView)\r
3930       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3931     else\r
3932       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3933   }\r
3934 \r
3935 /*  CopyBoard(lastDrawn, board);*/\r
3936   lastDrawnHighlight = highlightInfo;\r
3937   lastDrawnPremove   = premoveHighlightInfo;\r
3938   lastDrawnFlipView = flipView;\r
3939   lastDrawnValid[nr] = 1;\r
3940 }\r
3941 \r
3942 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3943 int\r
3944 SaveDiagram(f)\r
3945      FILE *f;\r
3946 {\r
3947     saveDiagFlag = 1; diagFile = f;\r
3948     HDCDrawPosition(NULL, TRUE, NULL);\r
3949 \r
3950     saveDiagFlag = 0;\r
3951 \r
3952 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
3953     \r
3954     fclose(f);\r
3955     return TRUE;\r
3956 }\r
3957 \r
3958 \r
3959 /*---------------------------------------------------------------------------*\\r
3960 | CLIENT PAINT PROCEDURE\r
3961 |   This is the main event-handler for the WM_PAINT message.\r
3962 |\r
3963 \*---------------------------------------------------------------------------*/\r
3964 VOID\r
3965 PaintProc(HWND hwnd)\r
3966 {\r
3967   HDC         hdc;\r
3968   PAINTSTRUCT ps;\r
3969   HFONT       oldFont;\r
3970 \r
3971   if((hdc = BeginPaint(hwnd, &ps))) {\r
3972     if (IsIconic(hwnd)) {\r
3973       DrawIcon(hdc, 2, 2, iconCurrent);\r
3974     } else {\r
3975       if (!appData.monoMode) {\r
3976         SelectPalette(hdc, hPal, FALSE);\r
3977         RealizePalette(hdc);\r
3978       }\r
3979       HDCDrawPosition(hdc, 1, NULL);\r
3980       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3981         flipView = !flipView; partnerUp = !partnerUp;\r
3982         HDCDrawPosition(hdc, 1, NULL);\r
3983         flipView = !flipView; partnerUp = !partnerUp;\r
3984       }\r
3985       oldFont =\r
3986         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3987       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3988                  ETO_CLIPPED|ETO_OPAQUE,\r
3989                  &messageRect, messageText, strlen(messageText), NULL);\r
3990       SelectObject(hdc, oldFont);\r
3991       DisplayBothClocks();\r
3992       DisplayLogos();\r
3993     }\r
3994     EndPaint(hwnd,&ps);\r
3995   }\r
3996 \r
3997   return;\r
3998 }\r
3999 \r
4000 \r
4001 /*\r
4002  * If the user selects on a border boundary, return -1; if off the board,\r
4003  *   return -2.  Otherwise map the event coordinate to the square.\r
4004  * The offset boardRect.left or boardRect.top must already have been\r
4005  *   subtracted from x.\r
4006  */\r
4007 int EventToSquare(x, limit)\r
4008      int x, limit;\r
4009 {\r
4010   if (x <= 0)\r
4011     return -2;\r
4012   if (x < lineGap)\r
4013     return -1;\r
4014   x -= lineGap;\r
4015   if ((x % (squareSize + lineGap)) >= squareSize)\r
4016     return -1;\r
4017   x /= (squareSize + lineGap);\r
4018     if (x >= limit)\r
4019     return -2;\r
4020   return x;\r
4021 }\r
4022 \r
4023 typedef struct {\r
4024   char piece;\r
4025   int command;\r
4026   char* name;\r
4027 } DropEnable;\r
4028 \r
4029 DropEnable dropEnables[] = {\r
4030   { 'P', DP_Pawn, N_("Pawn") },\r
4031   { 'N', DP_Knight, N_("Knight") },\r
4032   { 'B', DP_Bishop, N_("Bishop") },\r
4033   { 'R', DP_Rook, N_("Rook") },\r
4034   { 'Q', DP_Queen, N_("Queen") },\r
4035 };\r
4036 \r
4037 VOID\r
4038 SetupDropMenu(HMENU hmenu)\r
4039 {\r
4040   int i, count, enable;\r
4041   char *p;\r
4042   extern char white_holding[], black_holding[];\r
4043   char item[MSG_SIZ];\r
4044 \r
4045   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4046     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4047                dropEnables[i].piece);\r
4048     count = 0;\r
4049     while (p && *p++ == dropEnables[i].piece) count++;\r
4050       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4051     enable = count > 0 || !appData.testLegality\r
4052       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4053                       && !appData.icsActive);\r
4054     ModifyMenu(hmenu, dropEnables[i].command,\r
4055                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4056                dropEnables[i].command, item);\r
4057   }\r
4058 }\r
4059 \r
4060 void DragPieceBegin(int x, int y)\r
4061 {\r
4062       dragInfo.lastpos.x = boardRect.left + x;\r
4063       dragInfo.lastpos.y = boardRect.top + y;\r
4064       dragInfo.from.x = fromX;\r
4065       dragInfo.from.y = fromY;\r
4066       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4067       dragInfo.start = dragInfo.from;\r
4068       SetCapture(hwndMain);\r
4069 }\r
4070 \r
4071 void DragPieceEnd(int x, int y)\r
4072 {\r
4073     ReleaseCapture();\r
4074     dragInfo.start.x = dragInfo.start.y = -1;\r
4075     dragInfo.from = dragInfo.start;\r
4076     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4077 }\r
4078 \r
4079 void ChangeDragPiece(ChessSquare piece)\r
4080 {\r
4081     dragInfo.piece = piece;\r
4082 }\r
4083 \r
4084 /* Event handler for mouse messages */\r
4085 VOID\r
4086 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4087 {\r
4088   int x, y, menuNr;\r
4089   POINT pt;\r
4090   static int recursive = 0;\r
4091   HMENU hmenu;\r
4092   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4093 \r
4094   if (recursive) {\r
4095     if (message == WM_MBUTTONUP) {\r
4096       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4097          to the middle button: we simulate pressing the left button too!\r
4098          */\r
4099       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4100       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4101     }\r
4102     return;\r
4103   }\r
4104   recursive++;\r
4105   \r
4106   pt.x = LOWORD(lParam);\r
4107   pt.y = HIWORD(lParam);\r
4108   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4109   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4110   if (!flipView && y >= 0) {\r
4111     y = BOARD_HEIGHT - 1 - y;\r
4112   }\r
4113   if (flipView && x >= 0) {\r
4114     x = BOARD_WIDTH - 1 - x;\r
4115   }\r
4116 \r
4117   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4118 \r
4119   switch (message) {\r
4120   case WM_LBUTTONDOWN:\r
4121       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4122         ClockClick(flipClock);\r
4123       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4124         ClockClick(!flipClock);\r
4125       }\r
4126       dragInfo.start.x = dragInfo.start.y = -1;\r
4127       dragInfo.from = dragInfo.start;\r
4128     if(fromX == -1 && frozen) { // not sure where this is for\r
4129                 fromX = fromY = -1; \r
4130       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4131       break;\r
4132     }\r
4133       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4134       DrawPosition(TRUE, NULL);\r
4135     break;\r
4136 \r
4137   case WM_LBUTTONUP:\r
4138       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4139       DrawPosition(TRUE, NULL);\r
4140     break;\r
4141 \r
4142   case WM_MOUSEMOVE:\r
4143     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4144     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4145     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4146     if ((appData.animateDragging || appData.highlightDragging)\r
4147         && (wParam & MK_LBUTTON)\r
4148         && dragInfo.from.x >= 0) \r
4149     {\r
4150       BOOL full_repaint = FALSE;\r
4151 \r
4152       if (appData.animateDragging) {\r
4153         dragInfo.pos = pt;\r
4154       }\r
4155       if (appData.highlightDragging) {\r
4156         SetHighlights(fromX, fromY, x, y);\r
4157         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4158             full_repaint = TRUE;\r
4159         }\r
4160       }\r
4161       \r
4162       DrawPosition( full_repaint, NULL);\r
4163       \r
4164       dragInfo.lastpos = dragInfo.pos;\r
4165     }\r
4166     break;\r
4167 \r
4168   case WM_MOUSEWHEEL: // [DM]\r
4169     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4170        /* Mouse Wheel is being rolled forward\r
4171         * Play moves forward\r
4172         */\r
4173        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4174                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4175        /* Mouse Wheel is being rolled backward\r
4176         * Play moves backward\r
4177         */\r
4178        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4179                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4180     }\r
4181     break;\r
4182 \r
4183   case WM_MBUTTONUP:\r
4184   case WM_RBUTTONUP:\r
4185     ReleaseCapture();\r
4186     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4187     break;\r
4188  \r
4189   case WM_MBUTTONDOWN:\r
4190   case WM_RBUTTONDOWN:\r
4191     ErrorPopDown();\r
4192     ReleaseCapture();\r
4193     fromX = fromY = -1;\r
4194     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4195     dragInfo.start.x = dragInfo.start.y = -1;\r
4196     dragInfo.from = dragInfo.start;\r
4197     dragInfo.lastpos = dragInfo.pos;\r
4198     if (appData.highlightDragging) {\r
4199       ClearHighlights();\r
4200     }\r
4201     if(y == -2) {\r
4202       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4203       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4204           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4205       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4206           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4207       }\r
4208       break;\r
4209     }\r
4210     DrawPosition(TRUE, NULL);\r
4211 \r
4212     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4213     switch (menuNr) {\r
4214     case 0:\r
4215       if (message == WM_MBUTTONDOWN) {\r
4216         buttonCount = 3;  /* even if system didn't think so */\r
4217         if (wParam & MK_SHIFT) \r
4218           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4219         else\r
4220           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4221       } else { /* message == WM_RBUTTONDOWN */\r
4222         /* Just have one menu, on the right button.  Windows users don't\r
4223            think to try the middle one, and sometimes other software steals\r
4224            it, or it doesn't really exist. */\r
4225         if(gameInfo.variant != VariantShogi)\r
4226             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4227         else\r
4228             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4229       }\r
4230       break;\r
4231     case 2:\r
4232       SetCapture(hwndMain);
4233       break;\r
4234     case 1:\r
4235       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4236       SetupDropMenu(hmenu);\r
4237       MenuPopup(hwnd, pt, hmenu, -1);\r
4238     default:\r
4239       break;\r
4240     }\r
4241     break;\r
4242   }\r
4243 \r
4244   recursive--;\r
4245 }\r
4246 \r
4247 /* Preprocess messages for buttons in main window */\r
4248 LRESULT CALLBACK\r
4249 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4250 {\r
4251   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4252   int i, dir;\r
4253 \r
4254   for (i=0; i<N_BUTTONS; i++) {\r
4255     if (buttonDesc[i].id == id) break;\r
4256   }\r
4257   if (i == N_BUTTONS) return 0;\r
4258   switch (message) {\r
4259   case WM_KEYDOWN:\r
4260     switch (wParam) {\r
4261     case VK_LEFT:\r
4262     case VK_RIGHT:\r
4263       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4264       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4265       return TRUE;\r
4266     }\r
4267     break;\r
4268   case WM_CHAR:\r
4269     switch (wParam) {\r
4270     case '\r':\r
4271       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4272       return TRUE;\r
4273     default:\r
4274       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4275         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4276         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4277         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4278         SetFocus(h);\r
4279         SendMessage(h, WM_CHAR, wParam, lParam);\r
4280         return TRUE;\r
4281       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4282         TypeInEvent((char)wParam);\r
4283       }\r
4284       break;\r
4285     }\r
4286     break;\r
4287   }\r
4288   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4289 }\r
4290 \r
4291 /* Process messages for Promotion dialog box */\r
4292 LRESULT CALLBACK\r
4293 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4294 {\r
4295   char promoChar;\r
4296 \r
4297   switch (message) {\r
4298   case WM_INITDIALOG: /* message: initialize dialog box */\r
4299     /* Center the dialog over the application window */\r
4300     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4301     Translate(hDlg, DLG_PromotionKing);\r
4302     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4303       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4304        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4305        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4306                SW_SHOW : SW_HIDE);\r
4307     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4308     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4309        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4310          PieceToChar(WhiteAngel) != '~') ||\r
4311         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4312          PieceToChar(BlackAngel) != '~')   ) ?\r
4313                SW_SHOW : SW_HIDE);\r
4314     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4315        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4316          PieceToChar(WhiteMarshall) != '~') ||\r
4317         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4318          PieceToChar(BlackMarshall) != '~')   ) ?\r
4319                SW_SHOW : SW_HIDE);\r
4320     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4321     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4322        gameInfo.variant != VariantShogi ?\r
4323                SW_SHOW : SW_HIDE);\r
4324     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4325        gameInfo.variant != VariantShogi ?\r
4326                SW_SHOW : SW_HIDE);\r
4327     if(gameInfo.variant == VariantShogi) {\r
4328         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4329         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4330         SetWindowText(hDlg, "Promote?");\r
4331     }\r
4332     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4333        gameInfo.variant == VariantSuper ?\r
4334                SW_SHOW : SW_HIDE);\r
4335     return TRUE;\r
4336 \r
4337   case WM_COMMAND: /* message: received a command */\r
4338     switch (LOWORD(wParam)) {\r
4339     case IDCANCEL:\r
4340       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4341       ClearHighlights();\r
4342       DrawPosition(FALSE, NULL);\r
4343       return TRUE;\r
4344     case PB_King:\r
4345       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4346       break;\r
4347     case PB_Queen:\r
4348       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4349       break;\r
4350     case PB_Rook:\r
4351       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4352       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4353       break;\r
4354     case PB_Bishop:\r
4355       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4356       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4357       break;\r
4358     case PB_Chancellor:\r
4359       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4360       break;\r
4361     case PB_Archbishop:\r
4362       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4363       break;\r
4364     case PB_Knight:\r
4365       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4366       break;\r
4367     default:\r
4368       return FALSE;\r
4369     }\r
4370     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4371     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4372     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4373     fromX = fromY = -1;\r
4374     if (!appData.highlightLastMove) {\r
4375       ClearHighlights();\r
4376       DrawPosition(FALSE, NULL);\r
4377     }\r
4378     return TRUE;\r
4379   }\r
4380   return FALSE;\r
4381 }\r
4382 \r
4383 /* Pop up promotion dialog */\r
4384 VOID\r
4385 PromotionPopup(HWND hwnd)\r
4386 {\r
4387   FARPROC lpProc;\r
4388 \r
4389   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4390   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4391     hwnd, (DLGPROC)lpProc);\r
4392   FreeProcInstance(lpProc);\r
4393 }\r
4394 \r
4395 void\r
4396 PromotionPopUp()\r
4397 {\r
4398   DrawPosition(TRUE, NULL);\r
4399   PromotionPopup(hwndMain);\r
4400 }\r
4401 \r
4402 /* Toggle ShowThinking */\r
4403 VOID\r
4404 ToggleShowThinking()\r
4405 {\r
4406   appData.showThinking = !appData.showThinking;\r
4407   ShowThinkingEvent();\r
4408 }\r
4409 \r
4410 VOID\r
4411 LoadGameDialog(HWND hwnd, char* title)\r
4412 {\r
4413   UINT number = 0;\r
4414   FILE *f;\r
4415   char fileTitle[MSG_SIZ];\r
4416   f = OpenFileDialog(hwnd, "rb", "",\r
4417                      appData.oldSaveStyle ? "gam" : "pgn",\r
4418                      GAME_FILT,\r
4419                      title, &number, fileTitle, NULL);\r
4420   if (f != NULL) {\r
4421     cmailMsgLoaded = FALSE;\r
4422     if (number == 0) {\r
4423       int error = GameListBuild(f);\r
4424       if (error) {\r
4425         DisplayError(_("Cannot build game list"), error);\r
4426       } else if (!ListEmpty(&gameList) &&\r
4427                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4428         GameListPopUp(f, fileTitle);\r
4429         return;\r
4430       }\r
4431       GameListDestroy();\r
4432       number = 1;\r
4433     }\r
4434     LoadGame(f, number, fileTitle, FALSE);\r
4435   }\r
4436 }\r
4437 \r
4438 int get_term_width()\r
4439 {\r
4440     HDC hdc;\r
4441     TEXTMETRIC tm;\r
4442     RECT rc;\r
4443     HFONT hfont, hold_font;\r
4444     LOGFONT lf;\r
4445     HWND hText;\r
4446 \r
4447     if (hwndConsole)\r
4448         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4449     else\r
4450         return 79;\r
4451 \r
4452     // get the text metrics\r
4453     hdc = GetDC(hText);\r
4454     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4455     if (consoleCF.dwEffects & CFE_BOLD)\r
4456         lf.lfWeight = FW_BOLD;\r
4457     if (consoleCF.dwEffects & CFE_ITALIC)\r
4458         lf.lfItalic = TRUE;\r
4459     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4460         lf.lfStrikeOut = TRUE;\r
4461     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4462         lf.lfUnderline = TRUE;\r
4463     hfont = CreateFontIndirect(&lf);\r
4464     hold_font = SelectObject(hdc, hfont);\r
4465     GetTextMetrics(hdc, &tm);\r
4466     SelectObject(hdc, hold_font);\r
4467     DeleteObject(hfont);\r
4468     ReleaseDC(hText, hdc);\r
4469 \r
4470     // get the rectangle\r
4471     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4472 \r
4473     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4474 }\r
4475 \r
4476 void UpdateICSWidth(HWND hText)\r
4477 {\r
4478     LONG old_width, new_width;\r
4479 \r
4480     new_width = get_term_width(hText, FALSE);\r
4481     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4482     if (new_width != old_width)\r
4483     {\r
4484         ics_update_width(new_width);\r
4485         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4486     }\r
4487 }\r
4488 \r
4489 VOID\r
4490 ChangedConsoleFont()\r
4491 {\r
4492   CHARFORMAT cfmt;\r
4493   CHARRANGE tmpsel, sel;\r
4494   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4495   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4496   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4497   PARAFORMAT paraf;\r
4498 \r
4499   cfmt.cbSize = sizeof(CHARFORMAT);\r
4500   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4501     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4502                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4503   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4504    * size.  This was undocumented in the version of MSVC++ that I had\r
4505    * when I wrote the code, but is apparently documented now.\r
4506    */\r
4507   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4508   cfmt.bCharSet = f->lf.lfCharSet;\r
4509   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4510   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4511   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4512   /* Why are the following seemingly needed too? */\r
4513   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4514   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4515   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4516   tmpsel.cpMin = 0;\r
4517   tmpsel.cpMax = -1; /*999999?*/\r
4518   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4519   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4520   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4521    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4522    */\r
4523   paraf.cbSize = sizeof(paraf);\r
4524   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4525   paraf.dxStartIndent = 0;\r
4526   paraf.dxOffset = WRAP_INDENT;\r
4527   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4528   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4529   UpdateICSWidth(hText);\r
4530 }\r
4531 \r
4532 /*---------------------------------------------------------------------------*\\r
4533  *\r
4534  * Window Proc for main window\r
4535  *\r
4536 \*---------------------------------------------------------------------------*/\r
4537 \r
4538 /* Process messages for main window, etc. */\r
4539 LRESULT CALLBACK\r
4540 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4541 {\r
4542   FARPROC lpProc;\r
4543   int wmId, wmEvent;\r
4544   char *defName;\r
4545   FILE *f;\r
4546   UINT number;\r
4547   char fileTitle[MSG_SIZ];\r
4548   char buf[MSG_SIZ];\r
4549   static SnapData sd;\r
4550 \r
4551   switch (message) {\r
4552 \r
4553   case WM_PAINT: /* message: repaint portion of window */\r
4554     PaintProc(hwnd);\r
4555     break;\r
4556 \r
4557   case WM_ERASEBKGND:\r
4558     if (IsIconic(hwnd)) {\r
4559       /* Cheat; change the message */\r
4560       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4561     } else {\r
4562       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4563     }\r
4564     break;\r
4565 \r
4566   case WM_LBUTTONDOWN:\r
4567   case WM_MBUTTONDOWN:\r
4568   case WM_RBUTTONDOWN:\r
4569   case WM_LBUTTONUP:\r
4570   case WM_MBUTTONUP:\r
4571   case WM_RBUTTONUP:\r
4572   case WM_MOUSEMOVE:\r
4573   case WM_MOUSEWHEEL:\r
4574     MouseEvent(hwnd, message, wParam, lParam);\r
4575     break;\r
4576 \r
4577   JAWS_KB_NAVIGATION\r
4578 \r
4579   case WM_CHAR:\r
4580     \r
4581     JAWS_ALT_INTERCEPT\r
4582 \r
4583     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4584         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4585         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4586         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4587         SetFocus(h);\r
4588         SendMessage(h, message, wParam, lParam);\r
4589     } else if(lParam != KF_REPEAT) {\r
4590         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4591                 TypeInEvent((char)wParam);\r
4592         } else if((char)wParam == 003) CopyGameToClipboard();\r
4593          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4594     }\r
4595 \r
4596     break;\r
4597 \r
4598   case WM_PALETTECHANGED:\r
4599     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4600       int nnew;\r
4601       HDC hdc = GetDC(hwndMain);\r
4602       SelectPalette(hdc, hPal, TRUE);\r
4603       nnew = RealizePalette(hdc);\r
4604       if (nnew > 0) {\r
4605         paletteChanged = TRUE;\r
4606         InvalidateRect(hwnd, &boardRect, FALSE);\r
4607       }\r
4608       ReleaseDC(hwnd, hdc);\r
4609     }\r
4610     break;\r
4611 \r
4612   case WM_QUERYNEWPALETTE:\r
4613     if (!appData.monoMode /*&& paletteChanged*/) {\r
4614       int nnew;\r
4615       HDC hdc = GetDC(hwndMain);\r
4616       paletteChanged = FALSE;\r
4617       SelectPalette(hdc, hPal, FALSE);\r
4618       nnew = RealizePalette(hdc);\r
4619       if (nnew > 0) {\r
4620         InvalidateRect(hwnd, &boardRect, FALSE);\r
4621       }\r
4622       ReleaseDC(hwnd, hdc);\r
4623       return TRUE;\r
4624     }\r
4625     return FALSE;\r
4626 \r
4627   case WM_COMMAND: /* message: command from application menu */\r
4628     wmId    = LOWORD(wParam);\r
4629     wmEvent = HIWORD(wParam);\r
4630 \r
4631     switch (wmId) {\r
4632     case IDM_NewGame:\r
4633       ResetGameEvent();\r
4634       SAY("new game enter a move to play against the computer with white");\r
4635       break;\r
4636 \r
4637     case IDM_NewGameFRC:\r
4638       if( NewGameFRC() == 0 ) {\r
4639         ResetGameEvent();\r
4640       }\r
4641       break;\r
4642 \r
4643     case IDM_NewVariant:\r
4644       NewVariantPopup(hwnd);\r
4645       break;\r
4646 \r
4647     case IDM_LoadGame:\r
4648       LoadGameDialog(hwnd, _("Load Game from File"));\r
4649       break;\r
4650 \r
4651     case IDM_LoadNextGame:\r
4652       ReloadGame(1);\r
4653       break;\r
4654 \r
4655     case IDM_LoadPrevGame:\r
4656       ReloadGame(-1);\r
4657       break;\r
4658 \r
4659     case IDM_ReloadGame:\r
4660       ReloadGame(0);\r
4661       break;\r
4662 \r
4663     case IDM_LoadPosition:\r
4664       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4665         Reset(FALSE, TRUE);\r
4666       }\r
4667       number = 1;\r
4668       f = OpenFileDialog(hwnd, "rb", "",\r
4669                          appData.oldSaveStyle ? "pos" : "fen",\r
4670                          POSITION_FILT,\r
4671                          _("Load Position from File"), &number, fileTitle, NULL);\r
4672       if (f != NULL) {\r
4673         LoadPosition(f, number, fileTitle);\r
4674       }\r
4675       break;\r
4676 \r
4677     case IDM_LoadNextPosition:\r
4678       ReloadPosition(1);\r
4679       break;\r
4680 \r
4681     case IDM_LoadPrevPosition:\r
4682       ReloadPosition(-1);\r
4683       break;\r
4684 \r
4685     case IDM_ReloadPosition:\r
4686       ReloadPosition(0);\r
4687       break;\r
4688 \r
4689     case IDM_SaveGame:\r
4690       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4691       f = OpenFileDialog(hwnd, "a", defName,\r
4692                          appData.oldSaveStyle ? "gam" : "pgn",\r
4693                          GAME_FILT,\r
4694                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4695       if (f != NULL) {\r
4696         SaveGame(f, 0, "");\r
4697       }\r
4698       break;\r
4699 \r
4700     case IDM_SavePosition:\r
4701       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4702       f = OpenFileDialog(hwnd, "a", defName,\r
4703                          appData.oldSaveStyle ? "pos" : "fen",\r
4704                          POSITION_FILT,\r
4705                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4706       if (f != NULL) {\r
4707         SavePosition(f, 0, "");\r
4708       }\r
4709       break;\r
4710 \r
4711     case IDM_SaveDiagram:\r
4712       defName = "diagram";\r
4713       f = OpenFileDialog(hwnd, "wb", defName,\r
4714                          "bmp",\r
4715                          DIAGRAM_FILT,\r
4716                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4717       if (f != NULL) {\r
4718         SaveDiagram(f);\r
4719       }\r
4720       break;\r
4721 \r
4722     case IDM_CopyGame:\r
4723       CopyGameToClipboard();\r
4724       break;\r
4725 \r
4726     case IDM_PasteGame:\r
4727       PasteGameFromClipboard();\r
4728       break;\r
4729 \r
4730     case IDM_CopyGameListToClipboard:\r
4731       CopyGameListToClipboard();\r
4732       break;\r
4733 \r
4734     /* [AS] Autodetect FEN or PGN data */\r
4735     case IDM_PasteAny:\r
4736       PasteGameOrFENFromClipboard();\r
4737       break;\r
4738 \r
4739     /* [AS] Move history */\r
4740     case IDM_ShowMoveHistory:\r
4741         if( MoveHistoryIsUp() ) {\r
4742             MoveHistoryPopDown();\r
4743         }\r
4744         else {\r
4745             MoveHistoryPopUp();\r
4746         }\r
4747         break;\r
4748 \r
4749     /* [AS] Eval graph */\r
4750     case IDM_ShowEvalGraph:\r
4751         if( EvalGraphIsUp() ) {\r
4752             EvalGraphPopDown();\r
4753         }\r
4754         else {\r
4755             EvalGraphPopUp();\r
4756             SetFocus(hwndMain);\r
4757         }\r
4758         break;\r
4759 \r
4760     /* [AS] Engine output */\r
4761     case IDM_ShowEngineOutput:\r
4762         if( EngineOutputIsUp() ) {\r
4763             EngineOutputPopDown();\r
4764         }\r
4765         else {\r
4766             EngineOutputPopUp();\r
4767         }\r
4768         break;\r
4769 \r
4770     /* [AS] User adjudication */\r
4771     case IDM_UserAdjudication_White:\r
4772         UserAdjudicationEvent( +1 );\r
4773         break;\r
4774 \r
4775     case IDM_UserAdjudication_Black:\r
4776         UserAdjudicationEvent( -1 );\r
4777         break;\r
4778 \r
4779     case IDM_UserAdjudication_Draw:\r
4780         UserAdjudicationEvent( 0 );\r
4781         break;\r
4782 \r
4783     /* [AS] Game list options dialog */\r
4784     case IDM_GameListOptions:\r
4785       GameListOptions();\r
4786       break;\r
4787 \r
4788     case IDM_NewChat:\r
4789       ChatPopUp(NULL);\r
4790       break;\r
4791 \r
4792     case IDM_CopyPosition:\r
4793       CopyFENToClipboard();\r
4794       break;\r
4795 \r
4796     case IDM_PastePosition:\r
4797       PasteFENFromClipboard();\r
4798       break;\r
4799 \r
4800     case IDM_MailMove:\r
4801       MailMoveEvent();\r
4802       break;\r
4803 \r
4804     case IDM_ReloadCMailMsg:\r
4805       Reset(TRUE, TRUE);\r
4806       ReloadCmailMsgEvent(FALSE);\r
4807       break;\r
4808 \r
4809     case IDM_Minimize:\r
4810       ShowWindow(hwnd, SW_MINIMIZE);\r
4811       break;\r
4812 \r
4813     case IDM_Exit:\r
4814       ExitEvent(0);\r
4815       break;\r
4816 \r
4817     case IDM_MachineWhite:\r
4818       MachineWhiteEvent();\r
4819       /*\r
4820        * refresh the tags dialog only if it's visible\r
4821        */\r
4822       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4823           char *tags;\r
4824           tags = PGNTags(&gameInfo);\r
4825           TagsPopUp(tags, CmailMsg());\r
4826           free(tags);\r
4827       }\r
4828       SAY("computer starts playing white");\r
4829       break;\r
4830 \r
4831     case IDM_MachineBlack:\r
4832       MachineBlackEvent();\r
4833       /*\r
4834        * refresh the tags dialog only if it's visible\r
4835        */\r
4836       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4837           char *tags;\r
4838           tags = PGNTags(&gameInfo);\r
4839           TagsPopUp(tags, CmailMsg());\r
4840           free(tags);\r
4841       }\r
4842       SAY("computer starts playing black");\r
4843       break;\r
4844 \r
4845     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4846       if(gameMode != BeginningOfGame) { // allow menu item to remain enabled for better mode highligting\r
4847         DisplayError(_("You can only start a match from the initial position."), 0); break;\r
4848       }\r
4849       appData.matchGames = appData.defaultMatchGames;
4850       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
4851       break;\r
4852 \r
4853     case IDM_TwoMachines:\r
4854       TwoMachinesEvent();\r
4855       /*\r
4856        * refresh the tags dialog only if it's visible\r
4857        */\r
4858       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4859           char *tags;\r
4860           tags = PGNTags(&gameInfo);\r
4861           TagsPopUp(tags, CmailMsg());\r
4862           free(tags);\r
4863       }\r
4864       SAY("computer starts playing both sides");\r
4865       break;\r
4866 \r
4867     case IDM_AnalysisMode:\r
4868       if (!first.analysisSupport) {\r
4869         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4870         DisplayError(buf, 0);\r
4871       } else {\r
4872         SAY("analyzing current position");\r
4873         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4874         if (appData.icsActive) {\r
4875                if (gameMode != IcsObserving) {\r
4876                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4877                        DisplayError(buf, 0);\r
4878                        /* secure check */\r
4879                        if (appData.icsEngineAnalyze) {\r
4880                                if (appData.debugMode) \r
4881                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4882                                ExitAnalyzeMode();\r
4883                                ModeHighlight();\r
4884                                break;\r
4885                        }\r
4886                        break;\r
4887                } else {\r
4888                        /* if enable, user want disable icsEngineAnalyze */\r
4889                        if (appData.icsEngineAnalyze) {\r
4890                                ExitAnalyzeMode();\r
4891                                ModeHighlight();\r
4892                                break;\r
4893                        }\r
4894                        appData.icsEngineAnalyze = TRUE;\r
4895                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4896                }\r
4897         } \r
4898         if (!appData.showThinking) ToggleShowThinking();\r
4899         AnalyzeModeEvent();\r
4900       }\r
4901       break;\r
4902 \r
4903     case IDM_AnalyzeFile:\r
4904       if (!first.analysisSupport) {\r
4905         char buf[MSG_SIZ];\r
4906           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4907         DisplayError(buf, 0);\r
4908       } else {\r
4909         if (!appData.showThinking) ToggleShowThinking();\r
4910         AnalyzeFileEvent();\r
4911         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4912         AnalysisPeriodicEvent(1);\r
4913       }\r
4914       break;\r
4915 \r
4916     case IDM_IcsClient:\r
4917       IcsClientEvent();\r
4918       break;\r
4919 \r
4920     case IDM_EditGame:\r
4921     case IDM_EditGame2:\r
4922       EditGameEvent();\r
4923       SAY("edit game");\r
4924       break;\r
4925 \r
4926     case IDM_EditPosition:\r
4927     case IDM_EditPosition2:\r
4928       EditPositionEvent();\r
4929       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4930       break;\r
4931 \r
4932     case IDM_Training:\r
4933       TrainingEvent();\r
4934       break;\r
4935 \r
4936     case IDM_ShowGameList:\r
4937       ShowGameListProc();\r
4938       break;\r
4939 \r
4940     case IDM_EditProgs1:\r
4941       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4942       break;\r
4943 \r
4944     case IDM_EditProgs2:\r
4945       EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);\r
4946       break;\r
4947 \r
4948     case IDM_EditServers:\r
4949       EditTagsPopUp(icsNames, &icsNames);\r
4950       break;\r
4951 \r
4952     case IDM_EditTags:\r
4953     case IDM_Tags:\r
4954       EditTagsProc();\r
4955       break;\r
4956 \r
4957     case IDM_EditComment:\r
4958     case IDM_Comment:\r
4959       if (commentUp && editComment) {\r
4960         CommentPopDown();\r
4961       } else {\r
4962         EditCommentEvent();\r
4963       }\r
4964       break;\r
4965 \r
4966     case IDM_Pause:\r
4967       PauseEvent();\r
4968       break;\r
4969 \r
4970     case IDM_Accept:\r
4971       AcceptEvent();\r
4972       break;\r
4973 \r
4974     case IDM_Decline:\r
4975       DeclineEvent();\r
4976       break;\r
4977 \r
4978     case IDM_Rematch:\r
4979       RematchEvent();\r
4980       break;\r
4981 \r
4982     case IDM_CallFlag:\r
4983       CallFlagEvent();\r
4984       break;\r
4985 \r
4986     case IDM_Draw:\r
4987       DrawEvent();\r
4988       break;\r
4989 \r
4990     case IDM_Adjourn:\r
4991       AdjournEvent();\r
4992       break;\r
4993 \r
4994     case IDM_Abort:\r
4995       AbortEvent();\r
4996       break;\r
4997 \r
4998     case IDM_Resign:\r
4999       ResignEvent();\r
5000       break;\r
5001 \r
5002     case IDM_StopObserving:\r
5003       StopObservingEvent();\r
5004       break;\r
5005 \r
5006     case IDM_StopExamining:\r
5007       StopExaminingEvent();\r
5008       break;\r
5009 \r
5010     case IDM_Upload:\r
5011       UploadGameEvent();\r
5012       break;\r
5013 \r
5014     case IDM_TypeInMove:\r
5015       TypeInEvent('\000');\r
5016       break;\r
5017 \r
5018     case IDM_TypeInName:\r
5019       PopUpNameDialog('\000');\r
5020       break;\r
5021 \r
5022     case IDM_Backward:\r
5023       BackwardEvent();\r
5024       SetFocus(hwndMain);\r
5025       break;\r
5026 \r
5027     JAWS_MENU_ITEMS\r
5028 \r
5029     case IDM_Forward:\r
5030       ForwardEvent();\r
5031       SetFocus(hwndMain);\r
5032       break;\r
5033 \r
5034     case IDM_ToStart:\r
5035       ToStartEvent();\r
5036       SetFocus(hwndMain);\r
5037       break;\r
5038 \r
5039     case IDM_ToEnd:\r
5040       ToEndEvent();\r
5041       SetFocus(hwndMain);\r
5042       break;\r
5043 \r
5044     case IDM_Revert:\r
5045       RevertEvent(FALSE);\r
5046       break;\r
5047 \r
5048     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5049       RevertEvent(TRUE);\r
5050       break;\r
5051 \r
5052     case IDM_TruncateGame:\r
5053       TruncateGameEvent();\r
5054       break;\r
5055 \r
5056     case IDM_MoveNow:\r
5057       MoveNowEvent();\r
5058       break;\r
5059 \r
5060     case IDM_RetractMove:\r
5061       RetractMoveEvent();\r
5062       break;\r
5063 \r
5064     case IDM_FlipView:\r
5065       flipView = !flipView;\r
5066       DrawPosition(FALSE, NULL);\r
5067       break;\r
5068 \r
5069     case IDM_FlipClock:\r
5070       flipClock = !flipClock;\r
5071       DisplayBothClocks();\r
5072       DisplayLogos();\r
5073       break;\r
5074 \r
5075     case IDM_MuteSounds:\r
5076       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5077       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5078                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5079       break;\r
5080 \r
5081     case IDM_GeneralOptions:\r
5082       GeneralOptionsPopup(hwnd);\r
5083       DrawPosition(TRUE, NULL);\r
5084       break;\r
5085 \r
5086     case IDM_BoardOptions:\r
5087       BoardOptionsPopup(hwnd);\r
5088       break;\r
5089 \r
5090     case IDM_EnginePlayOptions:\r
5091       EnginePlayOptionsPopup(hwnd);\r
5092       break;\r
5093 \r
5094     case IDM_Engine1Options:\r
5095       EngineOptionsPopup(hwnd, &first);\r
5096       break;\r
5097 \r
5098     case IDM_Engine2Options:\r
5099       savedHwnd = hwnd;\r
5100       if(WaitForSecond(SettingsMenuIfReady)) break;\r
5101       EngineOptionsPopup(hwnd, &second);\r
5102       break;\r
5103 \r
5104     case IDM_OptionsUCI:\r
5105       UciOptionsPopup(hwnd);\r
5106       break;\r
5107 \r
5108     case IDM_IcsOptions:\r
5109       IcsOptionsPopup(hwnd);\r
5110       break;\r
5111 \r
5112     case IDM_Fonts:\r
5113       FontsOptionsPopup(hwnd);\r
5114       break;\r
5115 \r
5116     case IDM_Sounds:\r
5117       SoundOptionsPopup(hwnd);\r
5118       break;\r
5119 \r
5120     case IDM_CommPort:\r
5121       CommPortOptionsPopup(hwnd);\r
5122       break;\r
5123 \r
5124     case IDM_LoadOptions:\r
5125       LoadOptionsPopup(hwnd);\r
5126       break;\r
5127 \r
5128     case IDM_SaveOptions:\r
5129       SaveOptionsPopup(hwnd);\r
5130       break;\r
5131 \r
5132     case IDM_TimeControl:\r
5133       TimeControlOptionsPopup(hwnd);\r
5134       break;\r
5135 \r
5136     case IDM_SaveSettings:\r
5137       SaveSettings(settingsFileName);\r
5138       break;\r
5139 \r
5140     case IDM_SaveSettingsOnExit:\r
5141       saveSettingsOnExit = !saveSettingsOnExit;\r
5142       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5143                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5144                                          MF_CHECKED : MF_UNCHECKED));\r
5145       break;\r
5146 \r
5147     case IDM_Hint:\r
5148       HintEvent();\r
5149       break;\r
5150 \r
5151     case IDM_Book:\r
5152       BookEvent();\r
5153       break;\r
5154 \r
5155     case IDM_AboutGame:\r
5156       AboutGameEvent();\r
5157       break;\r
5158 \r
5159     case IDM_Debug:\r
5160       appData.debugMode = !appData.debugMode;\r
5161       if (appData.debugMode) {\r
5162         char dir[MSG_SIZ];\r
5163         GetCurrentDirectory(MSG_SIZ, dir);\r
5164         SetCurrentDirectory(installDir);\r
5165         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5166         SetCurrentDirectory(dir);\r
5167         setbuf(debugFP, NULL);\r
5168       } else {\r
5169         fclose(debugFP);\r
5170         debugFP = NULL;\r
5171       }\r
5172       break;\r
5173 \r
5174     case IDM_HELPCONTENTS:\r
5175       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5176           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5177           MessageBox (GetFocus(),\r
5178                     _("Unable to activate help"),\r
5179                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5180       }\r
5181       break;\r
5182 \r
5183     case IDM_HELPSEARCH:\r
5184         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5185             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5186         MessageBox (GetFocus(),\r
5187                     _("Unable to activate help"),\r
5188                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5189       }\r
5190       break;\r
5191 \r
5192     case IDM_HELPHELP:\r
5193       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5194         MessageBox (GetFocus(),\r
5195                     _("Unable to activate help"),\r
5196                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5197       }\r
5198       break;\r
5199 \r
5200     case IDM_ABOUT:\r
5201       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5202       DialogBox(hInst, \r
5203         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5204         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5205       FreeProcInstance(lpProc);\r
5206       break;\r
5207 \r
5208     case IDM_DirectCommand1:\r
5209       AskQuestionEvent(_("Direct Command"),\r
5210                        _("Send to chess program:"), "", "1");\r
5211       break;\r
5212     case IDM_DirectCommand2:\r
5213       AskQuestionEvent(_("Direct Command"),\r
5214                        _("Send to second chess program:"), "", "2");\r
5215       break;\r
5216 \r
5217     case EP_WhitePawn:\r
5218       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5219       fromX = fromY = -1;\r
5220       break;\r
5221 \r
5222     case EP_WhiteKnight:\r
5223       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5224       fromX = fromY = -1;\r
5225       break;\r
5226 \r
5227     case EP_WhiteBishop:\r
5228       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5229       fromX = fromY = -1;\r
5230       break;\r
5231 \r
5232     case EP_WhiteRook:\r
5233       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5234       fromX = fromY = -1;\r
5235       break;\r
5236 \r
5237     case EP_WhiteQueen:\r
5238       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5239       fromX = fromY = -1;\r
5240       break;\r
5241 \r
5242     case EP_WhiteFerz:\r
5243       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5244       fromX = fromY = -1;\r
5245       break;\r
5246 \r
5247     case EP_WhiteWazir:\r
5248       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5249       fromX = fromY = -1;\r
5250       break;\r
5251 \r
5252     case EP_WhiteAlfil:\r
5253       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5254       fromX = fromY = -1;\r
5255       break;\r
5256 \r
5257     case EP_WhiteCannon:\r
5258       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5259       fromX = fromY = -1;\r
5260       break;\r
5261 \r
5262     case EP_WhiteCardinal:\r
5263       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5264       fromX = fromY = -1;\r
5265       break;\r
5266 \r
5267     case EP_WhiteMarshall:\r
5268       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5269       fromX = fromY = -1;\r
5270       break;\r
5271 \r
5272     case EP_WhiteKing:\r
5273       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5274       fromX = fromY = -1;\r
5275       break;\r
5276 \r
5277     case EP_BlackPawn:\r
5278       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5279       fromX = fromY = -1;\r
5280       break;\r
5281 \r
5282     case EP_BlackKnight:\r
5283       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5284       fromX = fromY = -1;\r
5285       break;\r
5286 \r
5287     case EP_BlackBishop:\r
5288       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5289       fromX = fromY = -1;\r
5290       break;\r
5291 \r
5292     case EP_BlackRook:\r
5293       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5294       fromX = fromY = -1;\r
5295       break;\r
5296 \r
5297     case EP_BlackQueen:\r
5298       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5299       fromX = fromY = -1;\r
5300       break;\r
5301 \r
5302     case EP_BlackFerz:\r
5303       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5304       fromX = fromY = -1;\r
5305       break;\r
5306 \r
5307     case EP_BlackWazir:\r
5308       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5309       fromX = fromY = -1;\r
5310       break;\r
5311 \r
5312     case EP_BlackAlfil:\r
5313       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5314       fromX = fromY = -1;\r
5315       break;\r
5316 \r
5317     case EP_BlackCannon:\r
5318       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5319       fromX = fromY = -1;\r
5320       break;\r
5321 \r
5322     case EP_BlackCardinal:\r
5323       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5324       fromX = fromY = -1;\r
5325       break;\r
5326 \r
5327     case EP_BlackMarshall:\r
5328       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5329       fromX = fromY = -1;\r
5330       break;\r
5331 \r
5332     case EP_BlackKing:\r
5333       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5334       fromX = fromY = -1;\r
5335       break;\r
5336 \r
5337     case EP_EmptySquare:\r
5338       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5339       fromX = fromY = -1;\r
5340       break;\r
5341 \r
5342     case EP_ClearBoard:\r
5343       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5344       fromX = fromY = -1;\r
5345       break;\r
5346 \r
5347     case EP_White:\r
5348       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5349       fromX = fromY = -1;\r
5350       break;\r
5351 \r
5352     case EP_Black:\r
5353       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5354       fromX = fromY = -1;\r
5355       break;\r
5356 \r
5357     case EP_Promote:\r
5358       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5359       fromX = fromY = -1;\r
5360       break;\r
5361 \r
5362     case EP_Demote:\r
5363       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5364       fromX = fromY = -1;\r
5365       break;\r
5366 \r
5367     case DP_Pawn:\r
5368       DropMenuEvent(WhitePawn, fromX, fromY);\r
5369       fromX = fromY = -1;\r
5370       break;\r
5371 \r
5372     case DP_Knight:\r
5373       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5374       fromX = fromY = -1;\r
5375       break;\r
5376 \r
5377     case DP_Bishop:\r
5378       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5379       fromX = fromY = -1;\r
5380       break;\r
5381 \r
5382     case DP_Rook:\r
5383       DropMenuEvent(WhiteRook, fromX, fromY);\r
5384       fromX = fromY = -1;\r
5385       break;\r
5386 \r
5387     case DP_Queen:\r
5388       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5389       fromX = fromY = -1;\r
5390       break;\r
5391 \r
5392     case IDM_English:\r
5393       barbaric = 0; appData.language = "";\r
5394       TranslateMenus(0);\r
5395       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5396       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5397       lastChecked = wmId;\r
5398       break;\r
5399 \r
5400     default:\r
5401       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5402           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5403           TranslateMenus(0);\r
5404           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5405           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5406           lastChecked = wmId;\r
5407           break;\r
5408       }\r
5409       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5410     }\r
5411     break;\r
5412 \r
5413   case WM_TIMER:\r
5414     switch (wParam) {\r
5415     case CLOCK_TIMER_ID:\r
5416       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5417       clockTimerEvent = 0;\r
5418       DecrementClocks(); /* call into back end */\r
5419       break;\r
5420     case LOAD_GAME_TIMER_ID:\r
5421       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5422       loadGameTimerEvent = 0;\r
5423       AutoPlayGameLoop(); /* call into back end */\r
5424       break;\r
5425     case ANALYSIS_TIMER_ID:\r
5426       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5427                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5428         AnalysisPeriodicEvent(0);\r
5429       } else {\r
5430         KillTimer(hwnd, analysisTimerEvent);\r
5431         analysisTimerEvent = 0;\r
5432       }\r
5433       break;\r
5434     case DELAYED_TIMER_ID:\r
5435       KillTimer(hwnd, delayedTimerEvent);\r
5436       delayedTimerEvent = 0;\r
5437       delayedTimerCallback();\r
5438       break;\r
5439     }\r
5440     break;\r
5441 \r
5442   case WM_USER_Input:\r
5443     InputEvent(hwnd, message, wParam, lParam);\r
5444     break;\r
5445 \r
5446   /* [AS] Also move "attached" child windows */\r
5447   case WM_WINDOWPOSCHANGING:\r
5448 \r
5449     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5450         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5451 \r
5452         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5453             /* Window is moving */\r
5454             RECT rcMain;\r
5455 \r
5456 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5457             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5458             rcMain.right  = wpMain.x + wpMain.width;\r
5459             rcMain.top    = wpMain.y;\r
5460             rcMain.bottom = wpMain.y + wpMain.height;\r
5461             \r
5462             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5463             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5464             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5465             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5466             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5467             wpMain.x = lpwp->x;\r
5468             wpMain.y = lpwp->y;\r
5469         }\r
5470     }\r
5471     break;\r
5472 \r
5473   /* [AS] Snapping */\r
5474   case WM_ENTERSIZEMOVE:\r
5475     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5476     if (hwnd == hwndMain) {\r
5477       doingSizing = TRUE;\r
5478       lastSizing = 0;\r
5479     }\r
5480     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5481     break;\r
5482 \r
5483   case WM_SIZING:\r
5484     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5485     if (hwnd == hwndMain) {\r
5486       lastSizing = wParam;\r
5487     }\r
5488     break;\r
5489 \r
5490   case WM_MOVING:\r
5491     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5492       return OnMoving( &sd, hwnd, wParam, lParam );\r
5493 \r
5494   case WM_EXITSIZEMOVE:\r
5495     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5496     if (hwnd == hwndMain) {\r
5497       RECT client;\r
5498       doingSizing = FALSE;\r
5499       InvalidateRect(hwnd, &boardRect, FALSE);\r
5500       GetClientRect(hwnd, &client);\r
5501       ResizeBoard(client.right, client.bottom, lastSizing);\r
5502       lastSizing = 0;\r
5503       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5504     }\r
5505     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5506     break;\r
5507 \r
5508   case WM_DESTROY: /* message: window being destroyed */\r
5509     PostQuitMessage(0);\r
5510     break;\r
5511 \r
5512   case WM_CLOSE:\r
5513     if (hwnd == hwndMain) {\r
5514       ExitEvent(0);\r
5515     }\r
5516     break;\r
5517 \r
5518   default:      /* Passes it on if unprocessed */\r
5519     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5520   }\r
5521   return 0;\r
5522 }\r
5523 \r
5524 /*---------------------------------------------------------------------------*\\r
5525  *\r
5526  * Misc utility routines\r
5527  *\r
5528 \*---------------------------------------------------------------------------*/\r
5529 \r
5530 /*\r
5531  * Decent random number generator, at least not as bad as Windows\r
5532  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5533  */\r
5534 unsigned int randstate;\r
5535 \r
5536 int\r
5537 myrandom(void)\r
5538 {\r
5539   randstate = randstate * 1664525 + 1013904223;\r
5540   return (int) randstate & 0x7fffffff;\r
5541 }\r
5542 \r
5543 void\r
5544 mysrandom(unsigned int seed)\r
5545 {\r
5546   randstate = seed;\r
5547 }\r
5548 \r
5549 \r
5550 /* \r
5551  * returns TRUE if user selects a different color, FALSE otherwise \r
5552  */\r
5553 \r
5554 BOOL\r
5555 ChangeColor(HWND hwnd, COLORREF *which)\r
5556 {\r
5557   static BOOL firstTime = TRUE;\r
5558   static DWORD customColors[16];\r
5559   CHOOSECOLOR cc;\r
5560   COLORREF newcolor;\r
5561   int i;\r
5562   ColorClass ccl;\r
5563 \r
5564   if (firstTime) {\r
5565     /* Make initial colors in use available as custom colors */\r
5566     /* Should we put the compiled-in defaults here instead? */\r
5567     i = 0;\r
5568     customColors[i++] = lightSquareColor & 0xffffff;\r
5569     customColors[i++] = darkSquareColor & 0xffffff;\r
5570     customColors[i++] = whitePieceColor & 0xffffff;\r
5571     customColors[i++] = blackPieceColor & 0xffffff;\r
5572     customColors[i++] = highlightSquareColor & 0xffffff;\r
5573     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5574 \r
5575     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5576       customColors[i++] = textAttribs[ccl].color;\r
5577     }\r
5578     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5579     firstTime = FALSE;\r
5580   }\r
5581 \r
5582   cc.lStructSize = sizeof(cc);\r
5583   cc.hwndOwner = hwnd;\r
5584   cc.hInstance = NULL;\r
5585   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5586   cc.lpCustColors = (LPDWORD) customColors;\r
5587   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5588 \r
5589   if (!ChooseColor(&cc)) return FALSE;\r
5590 \r
5591   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5592   if (newcolor == *which) return FALSE;\r
5593   *which = newcolor;\r
5594   return TRUE;\r
5595 \r
5596   /*\r
5597   InitDrawingColors();\r
5598   InvalidateRect(hwnd, &boardRect, FALSE);\r
5599   */\r
5600 }\r
5601 \r
5602 BOOLEAN\r
5603 MyLoadSound(MySound *ms)\r
5604 {\r
5605   BOOL ok = FALSE;\r
5606   struct stat st;\r
5607   FILE *f;\r
5608 \r
5609   if (ms->data) free(ms->data);\r
5610   ms->data = NULL;\r
5611 \r
5612   switch (ms->name[0]) {\r
5613   case NULLCHAR:\r
5614     /* Silence */\r
5615     ok = TRUE;\r
5616     break;\r
5617   case '$':\r
5618     /* System sound from Control Panel.  Don't preload here. */\r
5619     ok = TRUE;\r
5620     break;\r
5621   case '!':\r
5622     if (ms->name[1] == NULLCHAR) {\r
5623       /* "!" alone = silence */\r
5624       ok = TRUE;\r
5625     } else {\r
5626       /* Builtin wave resource.  Error if not found. */\r
5627       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5628       if (h == NULL) break;\r
5629       ms->data = (void *)LoadResource(hInst, h);\r
5630       if (h == NULL) break;\r
5631       ok = TRUE;\r
5632     }\r
5633     break;\r
5634   default:\r
5635     /* .wav file.  Error if not found. */\r
5636     f = fopen(ms->name, "rb");\r
5637     if (f == NULL) break;\r
5638     if (fstat(fileno(f), &st) < 0) break;\r
5639     ms->data = malloc(st.st_size);\r
5640     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5641     fclose(f);\r
5642     ok = TRUE;\r
5643     break;\r
5644   }\r
5645   if (!ok) {\r
5646     char buf[MSG_SIZ];\r
5647       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5648     DisplayError(buf, GetLastError());\r
5649   }\r
5650   return ok;\r
5651 }\r
5652 \r
5653 BOOLEAN\r
5654 MyPlaySound(MySound *ms)\r
5655 {\r
5656   BOOLEAN ok = FALSE;\r
5657 \r
5658   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5659   switch (ms->name[0]) {\r
5660   case NULLCHAR:\r
5661         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5662     /* Silence */\r
5663     ok = TRUE;\r
5664     break;\r
5665   case '$':\r
5666     /* System sound from Control Panel (deprecated feature).\r
5667        "$" alone or an unset sound name gets default beep (still in use). */\r
5668     if (ms->name[1]) {\r
5669       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5670     }\r
5671     if (!ok) ok = MessageBeep(MB_OK);\r
5672     break; \r
5673   case '!':\r
5674     /* Builtin wave resource, or "!" alone for silence */\r
5675     if (ms->name[1]) {\r
5676       if (ms->data == NULL) return FALSE;\r
5677       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5678     } else {\r
5679       ok = TRUE;\r
5680     }\r
5681     break;\r
5682   default:\r
5683     /* .wav file.  Error if not found. */\r
5684     if (ms->data == NULL) return FALSE;\r
5685     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5686     break;\r
5687   }\r
5688   /* Don't print an error: this can happen innocently if the sound driver\r
5689      is busy; for instance, if another instance of WinBoard is playing\r
5690      a sound at about the same time. */\r
5691   return ok;\r
5692 }\r
5693 \r
5694 \r
5695 LRESULT CALLBACK\r
5696 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5697 {\r
5698   BOOL ok;\r
5699   OPENFILENAME *ofn;\r
5700   static UINT *number; /* gross that this is static */\r
5701 \r
5702   switch (message) {\r
5703   case WM_INITDIALOG: /* message: initialize dialog box */\r
5704     /* Center the dialog over the application window */\r
5705     ofn = (OPENFILENAME *) lParam;\r
5706     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5707       number = (UINT *) ofn->lCustData;\r
5708       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5709     } else {\r
5710       number = NULL;\r
5711     }\r
5712     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5713     Translate(hDlg, 1536);\r
5714     return FALSE;  /* Allow for further processing */\r
5715 \r
5716   case WM_COMMAND:\r
5717     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5718       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5719     }\r
5720     return FALSE;  /* Allow for further processing */\r
5721   }\r
5722   return FALSE;\r
5723 }\r
5724 \r
5725 UINT APIENTRY\r
5726 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5727 {\r
5728   static UINT *number;\r
5729   OPENFILENAME *ofname;\r
5730   OFNOTIFY *ofnot;\r
5731   switch (uiMsg) {\r
5732   case WM_INITDIALOG:\r
5733     Translate(hdlg, DLG_IndexNumber);\r
5734     ofname = (OPENFILENAME *)lParam;\r
5735     number = (UINT *)(ofname->lCustData);\r
5736     break;\r
5737   case WM_NOTIFY:\r
5738     ofnot = (OFNOTIFY *)lParam;\r
5739     if (ofnot->hdr.code == CDN_FILEOK) {\r
5740       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5741     }\r
5742     break;\r
5743   }\r
5744   return 0;\r
5745 }\r
5746 \r
5747 \r
5748 FILE *\r
5749 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5750                char *nameFilt, char *dlgTitle, UINT *number,\r
5751                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5752 {\r
5753   OPENFILENAME openFileName;\r
5754   char buf1[MSG_SIZ];\r
5755   FILE *f;\r
5756 \r
5757   if (fileName == NULL) fileName = buf1;\r
5758   if (defName == NULL) {\r
5759     safeStrCpy(fileName, "*.", 3 );\r
5760     strcat(fileName, defExt);\r
5761   } else {\r
5762     safeStrCpy(fileName, defName, MSG_SIZ );\r
5763   }\r
5764     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5765   if (number) *number = 0;\r
5766 \r
5767   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5768   openFileName.hwndOwner         = hwnd;\r
5769   openFileName.hInstance         = (HANDLE) hInst;\r
5770   openFileName.lpstrFilter       = nameFilt;\r
5771   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5772   openFileName.nMaxCustFilter    = 0L;\r
5773   openFileName.nFilterIndex      = 1L;\r
5774   openFileName.lpstrFile         = fileName;\r
5775   openFileName.nMaxFile          = MSG_SIZ;\r
5776   openFileName.lpstrFileTitle    = fileTitle;\r
5777   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5778   openFileName.lpstrInitialDir   = NULL;\r
5779   openFileName.lpstrTitle        = dlgTitle;\r
5780   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5781     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5782     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5783     | (oldDialog ? 0 : OFN_EXPLORER);\r
5784   openFileName.nFileOffset       = 0;\r
5785   openFileName.nFileExtension    = 0;\r
5786   openFileName.lpstrDefExt       = defExt;\r
5787   openFileName.lCustData         = (LONG) number;\r
5788   openFileName.lpfnHook          = oldDialog ?\r
5789     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5790   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5791 \r
5792   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5793                         GetOpenFileName(&openFileName)) {\r
5794     /* open the file */\r
5795     f = fopen(openFileName.lpstrFile, write);\r
5796     if (f == NULL) {\r
5797       MessageBox(hwnd, _("File open failed"), NULL,\r
5798                  MB_OK|MB_ICONEXCLAMATION);\r
5799       return NULL;\r
5800     }\r
5801   } else {\r
5802     int err = CommDlgExtendedError();\r
5803     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5804     return FALSE;\r
5805   }\r
5806   return f;\r
5807 }\r
5808 \r
5809 \r
5810 \r
5811 VOID APIENTRY\r
5812 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5813 {\r
5814   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5815 \r
5816   /*\r
5817    * Get the first pop-up menu in the menu template. This is the\r
5818    * menu that TrackPopupMenu displays.\r
5819    */\r
5820   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5821   TranslateOneMenu(10, hmenuTrackPopup);\r
5822 \r
5823   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5824 \r
5825   /*\r
5826    * TrackPopup uses screen coordinates, so convert the\r
5827    * coordinates of the mouse click to screen coordinates.\r
5828    */\r
5829   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5830 \r
5831   /* Draw and track the floating pop-up menu. */\r
5832   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5833                  pt.x, pt.y, 0, hwnd, NULL);\r
5834 \r
5835   /* Destroy the menu.*/\r
5836   DestroyMenu(hmenu);\r
5837 }\r
5838    \r
5839 typedef struct {\r
5840   HWND hDlg, hText;\r
5841   int sizeX, sizeY, newSizeX, newSizeY;\r
5842   HDWP hdwp;\r
5843 } ResizeEditPlusButtonsClosure;\r
5844 \r
5845 BOOL CALLBACK\r
5846 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5847 {\r
5848   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5849   RECT rect;\r
5850   POINT pt;\r
5851 \r
5852   if (hChild == cl->hText) return TRUE;\r
5853   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5854   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5855   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5856   ScreenToClient(cl->hDlg, &pt);\r
5857   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5858     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5859   return TRUE;\r
5860 }\r
5861 \r
5862 /* Resize a dialog that has a (rich) edit field filling most of\r
5863    the top, with a row of buttons below */\r
5864 VOID\r
5865 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5866 {\r
5867   RECT rectText;\r
5868   int newTextHeight, newTextWidth;\r
5869   ResizeEditPlusButtonsClosure cl;\r
5870   \r
5871   /*if (IsIconic(hDlg)) return;*/\r
5872   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5873   \r
5874   cl.hdwp = BeginDeferWindowPos(8);\r
5875 \r
5876   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5877   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5878   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5879   if (newTextHeight < 0) {\r
5880     newSizeY += -newTextHeight;\r
5881     newTextHeight = 0;\r
5882   }\r
5883   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5884     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5885 \r
5886   cl.hDlg = hDlg;\r
5887   cl.hText = hText;\r
5888   cl.sizeX = sizeX;\r
5889   cl.sizeY = sizeY;\r
5890   cl.newSizeX = newSizeX;\r
5891   cl.newSizeY = newSizeY;\r
5892   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5893 \r
5894   EndDeferWindowPos(cl.hdwp);\r
5895 }\r
5896 \r
5897 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5898 {\r
5899     RECT    rChild, rParent;\r
5900     int     wChild, hChild, wParent, hParent;\r
5901     int     wScreen, hScreen, xNew, yNew;\r
5902     HDC     hdc;\r
5903 \r
5904     /* Get the Height and Width of the child window */\r
5905     GetWindowRect (hwndChild, &rChild);\r
5906     wChild = rChild.right - rChild.left;\r
5907     hChild = rChild.bottom - rChild.top;\r
5908 \r
5909     /* Get the Height and Width of the parent window */\r
5910     GetWindowRect (hwndParent, &rParent);\r
5911     wParent = rParent.right - rParent.left;\r
5912     hParent = rParent.bottom - rParent.top;\r
5913 \r
5914     /* Get the display limits */\r
5915     hdc = GetDC (hwndChild);\r
5916     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5917     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5918     ReleaseDC(hwndChild, hdc);\r
5919 \r
5920     /* Calculate new X position, then adjust for screen */\r
5921     xNew = rParent.left + ((wParent - wChild) /2);\r
5922     if (xNew < 0) {\r
5923         xNew = 0;\r
5924     } else if ((xNew+wChild) > wScreen) {\r
5925         xNew = wScreen - wChild;\r
5926     }\r
5927 \r
5928     /* Calculate new Y position, then adjust for screen */\r
5929     if( mode == 0 ) {\r
5930         yNew = rParent.top  + ((hParent - hChild) /2);\r
5931     }\r
5932     else {\r
5933         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5934     }\r
5935 \r
5936     if (yNew < 0) {\r
5937         yNew = 0;\r
5938     } else if ((yNew+hChild) > hScreen) {\r
5939         yNew = hScreen - hChild;\r
5940     }\r
5941 \r
5942     /* Set it, and return */\r
5943     return SetWindowPos (hwndChild, NULL,\r
5944                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5945 }\r
5946 \r
5947 /* Center one window over another */\r
5948 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5949 {\r
5950     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5951 }\r
5952 \r
5953 /*---------------------------------------------------------------------------*\\r
5954  *\r
5955  * Startup Dialog functions\r
5956  *\r
5957 \*---------------------------------------------------------------------------*/\r
5958 void\r
5959 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5960 {\r
5961   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5962 \r
5963   while (*cd != NULL) {\r
5964     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5965     cd++;\r
5966   }\r
5967 }\r
5968 \r
5969 void\r
5970 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5971 {\r
5972   char buf1[MAX_ARG_LEN];\r
5973   int len;\r
5974 \r
5975   if (str[0] == '@') {\r
5976     FILE* f = fopen(str + 1, "r");\r
5977     if (f == NULL) {\r
5978       DisplayFatalError(str + 1, errno, 2);\r
5979       return;\r
5980     }\r
5981     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5982     fclose(f);\r
5983     buf1[len] = NULLCHAR;\r
5984     str = buf1;\r
5985   }\r
5986 \r
5987   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5988 \r
5989   for (;;) {\r
5990     char buf[MSG_SIZ];\r
5991     char *end = strchr(str, '\n');\r
5992     if (end == NULL) return;\r
5993     memcpy(buf, str, end - str);\r
5994     buf[end - str] = NULLCHAR;\r
5995     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5996     str = end + 1;\r
5997   }\r
5998 }\r
5999 \r
6000 void\r
6001 SetStartupDialogEnables(HWND hDlg)\r
6002 {\r
6003   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6004     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6005     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6006   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6007     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6008   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6009     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6010   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6011     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6012   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6013     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6014     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6015     IsDlgButtonChecked(hDlg, OPT_View));\r
6016 }\r
6017 \r
6018 char *\r
6019 QuoteForFilename(char *filename)\r
6020 {\r
6021   int dquote, space;\r
6022   dquote = strchr(filename, '"') != NULL;\r
6023   space = strchr(filename, ' ') != NULL;\r
6024   if (dquote || space) {\r
6025     if (dquote) {\r
6026       return "'";\r
6027     } else {\r
6028       return "\"";\r
6029     }\r
6030   } else {\r
6031     return "";\r
6032   }\r
6033 }\r
6034 \r
6035 VOID\r
6036 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6037 {\r
6038   char buf[MSG_SIZ];\r
6039   char *q;\r
6040 \r
6041   InitComboStringsFromOption(hwndCombo, nthnames);\r
6042   q = QuoteForFilename(nthcp);\r
6043     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6044   if (*nthdir != NULLCHAR) {\r
6045     q = QuoteForFilename(nthdir);\r
6046       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6047   }\r
6048   if (*nthcp == NULLCHAR) {\r
6049     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6050   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6051     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6052     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6053   }\r
6054 }\r
6055 \r
6056 LRESULT CALLBACK\r
6057 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6058 {\r
6059   char buf[MSG_SIZ];\r
6060   HANDLE hwndCombo;\r
6061   char *p;\r
6062 \r
6063   switch (message) {\r
6064   case WM_INITDIALOG:\r
6065     /* Center the dialog */\r
6066     CenterWindow (hDlg, GetDesktopWindow());\r
6067     Translate(hDlg, DLG_Startup);\r
6068     /* Initialize the dialog items */\r
6069     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6070                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6071                   firstChessProgramNames);\r
6072     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6073                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6074                   secondChessProgramNames);\r
6075     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6076     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6077       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6078     if (*appData.icsHelper != NULLCHAR) {\r
6079       char *q = QuoteForFilename(appData.icsHelper);\r
6080       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6081     }\r
6082     if (*appData.icsHost == NULLCHAR) {\r
6083       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6084       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6085     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6086       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6087       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6088     }\r
6089 \r
6090     if (appData.icsActive) {\r
6091       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6092     }\r
6093     else if (appData.noChessProgram) {\r
6094       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6095     }\r
6096     else {\r
6097       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6098     }\r
6099 \r
6100     SetStartupDialogEnables(hDlg);\r
6101     return TRUE;\r
6102 \r
6103   case WM_COMMAND:\r
6104     switch (LOWORD(wParam)) {\r
6105     case IDOK:\r
6106       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6107         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6108         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6109         p = buf;\r
6110         ParseArgs(StringGet, &p);\r
6111         safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6112         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6113         p = buf;\r
6114         ParseArgs(StringGet, &p);\r
6115         appData.noChessProgram = FALSE;\r
6116         appData.icsActive = FALSE;\r
6117       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6118         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6119         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6120         p = buf;\r
6121         ParseArgs(StringGet, &p);\r
6122         if (appData.zippyPlay) {\r
6123           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6124           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6125           p = buf;\r
6126           ParseArgs(StringGet, &p);\r
6127         }\r
6128       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6129         appData.noChessProgram = TRUE;\r
6130         appData.icsActive = FALSE;\r
6131       } else {\r
6132         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6133                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6134         return TRUE;\r
6135       }\r
6136       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6137         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6138         p = buf;\r
6139         ParseArgs(StringGet, &p);\r
6140       }\r
6141       EndDialog(hDlg, TRUE);\r
6142       return TRUE;\r
6143 \r
6144     case IDCANCEL:\r
6145       ExitEvent(0);\r
6146       return TRUE;\r
6147 \r
6148     case IDM_HELPCONTENTS:\r
6149       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6150         MessageBox (GetFocus(),\r
6151                     _("Unable to activate help"),\r
6152                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6153       }\r
6154       break;\r
6155 \r
6156     default:\r
6157       SetStartupDialogEnables(hDlg);\r
6158       break;\r
6159     }\r
6160     break;\r
6161   }\r
6162   return FALSE;\r
6163 }\r
6164 \r
6165 /*---------------------------------------------------------------------------*\\r
6166  *\r
6167  * About box dialog functions\r
6168  *\r
6169 \*---------------------------------------------------------------------------*/\r
6170 \r
6171 /* Process messages for "About" dialog box */\r
6172 LRESULT CALLBACK\r
6173 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6174 {\r
6175   switch (message) {\r
6176   case WM_INITDIALOG: /* message: initialize dialog box */\r
6177     /* Center the dialog over the application window */\r
6178     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6179     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6180     Translate(hDlg, ABOUTBOX);\r
6181     JAWS_COPYRIGHT\r
6182     return (TRUE);\r
6183 \r
6184   case WM_COMMAND: /* message: received a command */\r
6185     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6186         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6187       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6188       return (TRUE);\r
6189     }\r
6190     break;\r
6191   }\r
6192   return (FALSE);\r
6193 }\r
6194 \r
6195 /*---------------------------------------------------------------------------*\\r
6196  *\r
6197  * Comment Dialog functions\r
6198  *\r
6199 \*---------------------------------------------------------------------------*/\r
6200 \r
6201 LRESULT CALLBACK\r
6202 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6203 {\r
6204   static HANDLE hwndText = NULL;\r
6205   int len, newSizeX, newSizeY, flags;\r
6206   static int sizeX, sizeY;\r
6207   char *str;\r
6208   RECT rect;\r
6209   MINMAXINFO *mmi;\r
6210 \r
6211   switch (message) {\r
6212   case WM_INITDIALOG: /* message: initialize dialog box */\r
6213     /* Initialize the dialog items */\r
6214     Translate(hDlg, DLG_EditComment);\r
6215     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6216     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6217     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6218     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6219     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6220     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6221     SetWindowText(hDlg, commentTitle);\r
6222     if (editComment) {\r
6223       SetFocus(hwndText);\r
6224     } else {\r
6225       SetFocus(GetDlgItem(hDlg, IDOK));\r
6226     }\r
6227     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6228                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6229                 MAKELPARAM(FALSE, 0));\r
6230     /* Size and position the dialog */\r
6231     if (!commentDialog) {\r
6232       commentDialog = hDlg;\r
6233       flags = SWP_NOZORDER;\r
6234       GetClientRect(hDlg, &rect);\r
6235       sizeX = rect.right;\r
6236       sizeY = rect.bottom;\r
6237       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6238           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6239         WINDOWPLACEMENT wp;\r
6240         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6241         wp.length = sizeof(WINDOWPLACEMENT);\r
6242         wp.flags = 0;\r
6243         wp.showCmd = SW_SHOW;\r
6244         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6245         wp.rcNormalPosition.left = wpComment.x;\r
6246         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6247         wp.rcNormalPosition.top = wpComment.y;\r
6248         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6249         SetWindowPlacement(hDlg, &wp);\r
6250 \r
6251         GetClientRect(hDlg, &rect);\r
6252         newSizeX = rect.right;\r
6253         newSizeY = rect.bottom;\r
6254         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6255                               newSizeX, newSizeY);\r
6256         sizeX = newSizeX;\r
6257         sizeY = newSizeY;\r
6258       }\r
6259     }\r
6260     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6261     return FALSE;\r
6262 \r
6263   case WM_COMMAND: /* message: received a command */\r
6264     switch (LOWORD(wParam)) {\r
6265     case IDOK:\r
6266       if (editComment) {\r
6267         char *p, *q;\r
6268         /* Read changed options from the dialog box */\r
6269         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6270         len = GetWindowTextLength(hwndText);\r
6271         str = (char *) malloc(len + 1);\r
6272         GetWindowText(hwndText, str, len + 1);\r
6273         p = q = str;\r
6274         while (*q) {\r
6275           if (*q == '\r')\r
6276             q++;\r
6277           else\r
6278             *p++ = *q++;\r
6279         }\r
6280         *p = NULLCHAR;\r
6281         ReplaceComment(commentIndex, str);\r
6282         free(str);\r
6283       }\r
6284       CommentPopDown();\r
6285       return TRUE;\r
6286 \r
6287     case IDCANCEL:\r
6288     case OPT_CancelComment:\r
6289       CommentPopDown();\r
6290       return TRUE;\r
6291 \r
6292     case OPT_ClearComment:\r
6293       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6294       break;\r
6295 \r
6296     case OPT_EditComment:\r
6297       EditCommentEvent();\r
6298       return TRUE;\r
6299 \r
6300     default:\r
6301       break;\r
6302     }\r
6303     break;\r
6304 \r
6305   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6306         if( wParam == OPT_CommentText ) {\r
6307             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6308 \r
6309             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6310                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6311                 POINTL pt;\r
6312                 LRESULT index;\r
6313 \r
6314                 pt.x = LOWORD( lpMF->lParam );\r
6315                 pt.y = HIWORD( lpMF->lParam );\r
6316 \r
6317                 if(lpMF->msg == WM_CHAR) {\r
6318                         CHARRANGE sel;\r
6319                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6320                         index = sel.cpMin;\r
6321                 } else\r
6322                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6323 \r
6324                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6325                 len = GetWindowTextLength(hwndText);\r
6326                 str = (char *) malloc(len + 1);\r
6327                 GetWindowText(hwndText, str, len + 1);\r
6328                 ReplaceComment(commentIndex, str);\r
6329                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6330                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6331                 free(str);\r
6332 \r
6333                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6334                 lpMF->msg = WM_USER;\r
6335 \r
6336                 return TRUE;\r
6337             }\r
6338         }\r
6339         break;\r
6340 \r
6341   case WM_SIZE:\r
6342     newSizeX = LOWORD(lParam);\r
6343     newSizeY = HIWORD(lParam);\r
6344     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6345     sizeX = newSizeX;\r
6346     sizeY = newSizeY;\r
6347     break;\r
6348 \r
6349   case WM_GETMINMAXINFO:\r
6350     /* Prevent resizing window too small */\r
6351     mmi = (MINMAXINFO *) lParam;\r
6352     mmi->ptMinTrackSize.x = 100;\r
6353     mmi->ptMinTrackSize.y = 100;\r
6354     break;\r
6355   }\r
6356   return FALSE;\r
6357 }\r
6358 \r
6359 VOID\r
6360 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6361 {\r
6362   FARPROC lpProc;\r
6363   char *p, *q;\r
6364 \r
6365   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6366 \r
6367   if (str == NULL) str = "";\r
6368   p = (char *) malloc(2 * strlen(str) + 2);\r
6369   q = p;\r
6370   while (*str) {\r
6371     if (*str == '\n') *q++ = '\r';\r
6372     *q++ = *str++;\r
6373   }\r
6374   *q = NULLCHAR;\r
6375   if (commentText != NULL) free(commentText);\r
6376 \r
6377   commentIndex = index;\r
6378   commentTitle = title;\r
6379   commentText = p;\r
6380   editComment = edit;\r
6381 \r
6382   if (commentDialog) {\r
6383     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6384     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6385   } else {\r
6386     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6387     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6388                  hwndMain, (DLGPROC)lpProc);\r
6389     FreeProcInstance(lpProc);\r
6390   }\r
6391   commentUp = TRUE;\r
6392 }\r
6393 \r
6394 \r
6395 /*---------------------------------------------------------------------------*\\r
6396  *\r
6397  * Type-in move dialog functions\r
6398  * \r
6399 \*---------------------------------------------------------------------------*/\r
6400 \r
6401 LRESULT CALLBACK\r
6402 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6403 {\r
6404   char move[MSG_SIZ];\r
6405   HWND hInput;\r
6406 \r
6407   switch (message) {\r
6408   case WM_INITDIALOG:\r
6409     move[0] = (char) lParam;\r
6410     move[1] = NULLCHAR;\r
6411     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6412     Translate(hDlg, DLG_TypeInMove);\r
6413     hInput = GetDlgItem(hDlg, OPT_Move);\r
6414     SetWindowText(hInput, move);\r
6415     SetFocus(hInput);\r
6416     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6417     return FALSE;\r
6418 \r
6419   case WM_COMMAND:\r
6420     switch (LOWORD(wParam)) {\r
6421     case IDOK:
6422 \r
6423       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6424       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6425       TypeInDoneEvent(move);\r
6426       EndDialog(hDlg, TRUE);\r
6427       return TRUE;\r
6428     case IDCANCEL:\r
6429       EndDialog(hDlg, FALSE);\r
6430       return TRUE;\r
6431     default:\r
6432       break;\r
6433     }\r
6434     break;\r
6435   }\r
6436   return FALSE;\r
6437 }\r
6438 \r
6439 VOID\r
6440 PopUpMoveDialog(char firstchar)\r
6441 {\r
6442     FARPROC lpProc;\r
6443 \r
6444       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6445       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6446         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6447       FreeProcInstance(lpProc);\r
6448 }\r
6449 \r
6450 /*---------------------------------------------------------------------------*\\r
6451  *\r
6452  * Type-in name dialog functions\r
6453  * \r
6454 \*---------------------------------------------------------------------------*/\r
6455 \r
6456 LRESULT CALLBACK\r
6457 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6458 {\r
6459   char move[MSG_SIZ];\r
6460   HWND hInput;\r
6461 \r
6462   switch (message) {\r
6463   case WM_INITDIALOG:\r
6464     move[0] = (char) lParam;\r
6465     move[1] = NULLCHAR;\r
6466     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6467     Translate(hDlg, DLG_TypeInName);\r
6468     hInput = GetDlgItem(hDlg, OPT_Name);\r
6469     SetWindowText(hInput, move);\r
6470     SetFocus(hInput);\r
6471     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6472     return FALSE;\r
6473 \r
6474   case WM_COMMAND:\r
6475     switch (LOWORD(wParam)) {\r
6476     case IDOK:\r
6477       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6478       appData.userName = strdup(move);\r
6479       SetUserLogo();\r
6480       SetGameInfo();\r
6481       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6482         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6483         DisplayTitle(move);\r
6484       }\r
6485 \r
6486 \r
6487       EndDialog(hDlg, TRUE);\r
6488       return TRUE;\r
6489     case IDCANCEL:\r
6490       EndDialog(hDlg, FALSE);\r
6491       return TRUE;\r
6492     default:\r
6493       break;\r
6494     }\r
6495     break;\r
6496   }\r
6497   return FALSE;\r
6498 }\r
6499 \r
6500 VOID\r
6501 PopUpNameDialog(char firstchar)\r
6502 {\r
6503     FARPROC lpProc;\r
6504     \r
6505       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6506       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6507         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6508       FreeProcInstance(lpProc);\r
6509 }\r
6510 \r
6511 /*---------------------------------------------------------------------------*\\r
6512  *\r
6513  *  Error dialogs\r
6514  * \r
6515 \*---------------------------------------------------------------------------*/\r
6516 \r
6517 /* Nonmodal error box */\r
6518 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6519                              WPARAM wParam, LPARAM lParam);\r
6520 \r
6521 VOID\r
6522 ErrorPopUp(char *title, char *content)\r
6523 {\r
6524   FARPROC lpProc;\r
6525   char *p, *q;\r
6526   BOOLEAN modal = hwndMain == NULL;\r
6527 \r
6528   p = content;\r
6529   q = errorMessage;\r
6530   while (*p) {\r
6531     if (*p == '\n') {\r
6532       if (modal) {\r
6533         *q++ = ' ';\r
6534         p++;\r
6535       } else {\r
6536         *q++ = '\r';\r
6537         *q++ = *p++;\r
6538       }\r
6539     } else {\r
6540       *q++ = *p++;\r
6541     }\r
6542   }\r
6543   *q = NULLCHAR;\r
6544   strncpy(errorTitle, title, sizeof(errorTitle));\r
6545   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6546   \r
6547   if (modal) {\r
6548     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6549   } else {\r
6550     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6551     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6552                  hwndMain, (DLGPROC)lpProc);\r
6553     FreeProcInstance(lpProc);\r
6554   }\r
6555 }\r
6556 \r
6557 VOID\r
6558 ErrorPopDown()\r
6559 {\r
6560   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6561   if (errorDialog == NULL) return;\r
6562   DestroyWindow(errorDialog);\r
6563   errorDialog = NULL;\r
6564   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6565 }\r
6566 \r
6567 LRESULT CALLBACK\r
6568 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6569 {\r
6570   HANDLE hwndText;\r
6571   RECT rChild;\r
6572 \r
6573   switch (message) {\r
6574   case WM_INITDIALOG:\r
6575     GetWindowRect(hDlg, &rChild);\r
6576 \r
6577     /*\r
6578     SetWindowPos(hDlg, NULL, rChild.left,\r
6579       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6580       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6581     */\r
6582 \r
6583     /* \r
6584         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6585         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6586         and it doesn't work when you resize the dialog.\r
6587         For now, just give it a default position.\r
6588     */\r
6589     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6590     Translate(hDlg, DLG_Error);\r
6591 \r
6592     errorDialog = hDlg;\r
6593     SetWindowText(hDlg, errorTitle);\r
6594     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6595     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6596     return FALSE;\r
6597 \r
6598   case WM_COMMAND:\r
6599     switch (LOWORD(wParam)) {\r
6600     case IDOK:\r
6601     case IDCANCEL:\r
6602       if (errorDialog == hDlg) errorDialog = NULL;\r
6603       DestroyWindow(hDlg);\r
6604       return TRUE;\r
6605 \r
6606     default:\r
6607       break;\r
6608     }\r
6609     break;\r
6610   }\r
6611   return FALSE;\r
6612 }\r
6613 \r
6614 #ifdef GOTHIC\r
6615 HWND gothicDialog = NULL;\r
6616 \r
6617 LRESULT CALLBACK\r
6618 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6619 {\r
6620   HANDLE hwndText;\r
6621   RECT rChild;\r
6622   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6623 \r
6624   switch (message) {\r
6625   case WM_INITDIALOG:\r
6626     GetWindowRect(hDlg, &rChild);\r
6627 \r
6628     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6629                                                              SWP_NOZORDER);\r
6630 \r
6631     /* \r
6632         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6633         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6634         and it doesn't work when you resize the dialog.\r
6635         For now, just give it a default position.\r
6636     */\r
6637     gothicDialog = hDlg;\r
6638     SetWindowText(hDlg, errorTitle);\r
6639     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6640     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6641     return FALSE;\r
6642 \r
6643   case WM_COMMAND:\r
6644     switch (LOWORD(wParam)) {\r
6645     case IDOK:\r
6646     case IDCANCEL:\r
6647       if (errorDialog == hDlg) errorDialog = NULL;\r
6648       DestroyWindow(hDlg);\r
6649       return TRUE;\r
6650 \r
6651     default:\r
6652       break;\r
6653     }\r
6654     break;\r
6655   }\r
6656   return FALSE;\r
6657 }\r
6658 \r
6659 VOID\r
6660 GothicPopUp(char *title, VariantClass variant)\r
6661 {\r
6662   FARPROC lpProc;\r
6663   static char *lastTitle;\r
6664 \r
6665   strncpy(errorTitle, title, sizeof(errorTitle));\r
6666   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6667 \r
6668   if(lastTitle != title && gothicDialog != NULL) {\r
6669     DestroyWindow(gothicDialog);\r
6670     gothicDialog = NULL;\r
6671   }\r
6672   if(variant != VariantNormal && gothicDialog == NULL) {\r
6673     title = lastTitle;\r
6674     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6675     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6676                  hwndMain, (DLGPROC)lpProc);\r
6677     FreeProcInstance(lpProc);\r
6678   }\r
6679 }\r
6680 #endif\r
6681 \r
6682 /*---------------------------------------------------------------------------*\\r
6683  *\r
6684  *  Ics Interaction console functions\r
6685  *\r
6686 \*---------------------------------------------------------------------------*/\r
6687 \r
6688 #define HISTORY_SIZE 64\r
6689 static char *history[HISTORY_SIZE];\r
6690 int histIn = 0, histP = 0;\r
6691 \r
6692 VOID\r
6693 SaveInHistory(char *cmd)\r
6694 {\r
6695   if (history[histIn] != NULL) {\r
6696     free(history[histIn]);\r
6697     history[histIn] = NULL;\r
6698   }\r
6699   if (*cmd == NULLCHAR) return;\r
6700   history[histIn] = StrSave(cmd);\r
6701   histIn = (histIn + 1) % HISTORY_SIZE;\r
6702   if (history[histIn] != NULL) {\r
6703     free(history[histIn]);\r
6704     history[histIn] = NULL;\r
6705   }\r
6706   histP = histIn;\r
6707 }\r
6708 \r
6709 char *\r
6710 PrevInHistory(char *cmd)\r
6711 {\r
6712   int newhp;\r
6713   if (histP == histIn) {\r
6714     if (history[histIn] != NULL) free(history[histIn]);\r
6715     history[histIn] = StrSave(cmd);\r
6716   }\r
6717   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6718   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6719   histP = newhp;\r
6720   return history[histP];\r
6721 }\r
6722 \r
6723 char *\r
6724 NextInHistory()\r
6725 {\r
6726   if (histP == histIn) return NULL;\r
6727   histP = (histP + 1) % HISTORY_SIZE;\r
6728   return history[histP];   \r
6729 }\r
6730 \r
6731 HMENU\r
6732 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6733 {\r
6734   HMENU hmenu, h;\r
6735   int i = 0;\r
6736   hmenu = LoadMenu(hInst, "TextMenu");\r
6737   h = GetSubMenu(hmenu, 0);\r
6738   while (e->item) {\r
6739     if (strcmp(e->item, "-") == 0) {\r
6740       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6741     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6742       int flags = MF_STRING, j = 0;\r
6743       if (e->item[0] == '|') {\r
6744         flags |= MF_MENUBARBREAK;\r
6745         j++;\r
6746       }\r
6747       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6748       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6749     }\r
6750     e++;\r
6751     i++;\r
6752   } \r
6753   return hmenu;\r
6754 }\r
6755 \r
6756 WNDPROC consoleTextWindowProc;\r
6757 \r
6758 void\r
6759 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6760 {\r
6761   char buf[MSG_SIZ], name[MSG_SIZ];\r
6762   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6763   CHARRANGE sel;\r
6764 \r
6765   if (!getname) {\r
6766     SetWindowText(hInput, command);\r
6767     if (immediate) {\r
6768       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6769     } else {\r
6770       sel.cpMin = 999999;\r
6771       sel.cpMax = 999999;\r
6772       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6773       SetFocus(hInput);\r
6774     }\r
6775     return;\r
6776   }    \r
6777   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6778   if (sel.cpMin == sel.cpMax) {\r
6779     /* Expand to surrounding word */\r
6780     TEXTRANGE tr;\r
6781     do {\r
6782       tr.chrg.cpMax = sel.cpMin;\r
6783       tr.chrg.cpMin = --sel.cpMin;\r
6784       if (sel.cpMin < 0) break;\r
6785       tr.lpstrText = name;\r
6786       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6787     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6788     sel.cpMin++;\r
6789 \r
6790     do {\r
6791       tr.chrg.cpMin = sel.cpMax;\r
6792       tr.chrg.cpMax = ++sel.cpMax;\r
6793       tr.lpstrText = name;\r
6794       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6795     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6796     sel.cpMax--;\r
6797 \r
6798     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6799       MessageBeep(MB_ICONEXCLAMATION);\r
6800       return;\r
6801     }\r
6802     tr.chrg = sel;\r
6803     tr.lpstrText = name;\r
6804     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6805   } else {\r
6806     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6807       MessageBeep(MB_ICONEXCLAMATION);\r
6808       return;\r
6809     }\r
6810     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6811   }\r
6812   if (immediate) {\r
6813     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6814     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6815     SetWindowText(hInput, buf);\r
6816     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6817   } else {\r
6818     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6819       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6820     SetWindowText(hInput, buf);\r
6821     sel.cpMin = 999999;\r
6822     sel.cpMax = 999999;\r
6823     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6824     SetFocus(hInput);\r
6825   }\r
6826 }\r
6827 \r
6828 LRESULT CALLBACK \r
6829 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6830 {\r
6831   HWND hInput;\r
6832   CHARRANGE sel;\r
6833 \r
6834   switch (message) {\r
6835   case WM_KEYDOWN:\r
6836     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6837     if(wParam=='R') return 0;\r
6838     switch (wParam) {\r
6839     case VK_PRIOR:\r
6840       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6841       return 0;\r
6842     case VK_NEXT:\r
6843       sel.cpMin = 999999;\r
6844       sel.cpMax = 999999;\r
6845       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6846       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6847       return 0;\r
6848     }\r
6849     break;\r
6850   case WM_CHAR:\r
6851    if(wParam != '\022') {\r
6852     if (wParam == '\t') {\r
6853       if (GetKeyState(VK_SHIFT) < 0) {\r
6854         /* shifted */\r
6855         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6856         if (buttonDesc[0].hwnd) {\r
6857           SetFocus(buttonDesc[0].hwnd);\r
6858         } else {\r
6859           SetFocus(hwndMain);\r
6860         }\r
6861       } else {\r
6862         /* unshifted */\r
6863         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6864       }\r
6865     } else {\r
6866       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6867       JAWS_DELETE( SetFocus(hInput); )\r
6868       SendMessage(hInput, message, wParam, lParam);\r
6869     }\r
6870     return 0;\r
6871    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6872    lParam = -1;\r
6873   case WM_RBUTTONDOWN:\r
6874     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6875       /* Move selection here if it was empty */\r
6876       POINT pt;\r
6877       pt.x = LOWORD(lParam);\r
6878       pt.y = HIWORD(lParam);\r
6879       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6880       if (sel.cpMin == sel.cpMax) {\r
6881         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6882         sel.cpMax = sel.cpMin;\r
6883         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6884       }\r
6885       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6886 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6887       POINT pt;\r
6888       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6889       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6890       if (sel.cpMin == sel.cpMax) {\r
6891         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6892         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6893       }\r
6894       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6895         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6896       }\r
6897       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6898       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6899       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6900       MenuPopup(hwnd, pt, hmenu, -1);\r
6901 }\r
6902     }\r
6903     return 0;\r
6904   case WM_RBUTTONUP:\r
6905     if (GetKeyState(VK_SHIFT) & ~1) {\r
6906       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6907         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6908     }\r
6909     return 0;\r
6910   case WM_PASTE:\r
6911     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6912     SetFocus(hInput);\r
6913     return SendMessage(hInput, message, wParam, lParam);\r
6914   case WM_MBUTTONDOWN:\r
6915     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6916   case WM_COMMAND:\r
6917     switch (LOWORD(wParam)) {\r
6918     case IDM_QuickPaste:\r
6919       {\r
6920         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6921         if (sel.cpMin == sel.cpMax) {\r
6922           MessageBeep(MB_ICONEXCLAMATION);\r
6923           return 0;\r
6924         }\r
6925         SendMessage(hwnd, WM_COPY, 0, 0);\r
6926         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6927         SendMessage(hInput, WM_PASTE, 0, 0);\r
6928         SetFocus(hInput);\r
6929         return 0;\r
6930       }\r
6931     case IDM_Cut:\r
6932       SendMessage(hwnd, WM_CUT, 0, 0);\r
6933       return 0;\r
6934     case IDM_Paste:\r
6935       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6936       return 0;\r
6937     case IDM_Copy:\r
6938       SendMessage(hwnd, WM_COPY, 0, 0);\r
6939       return 0;\r
6940     default:\r
6941       {\r
6942         int i = LOWORD(wParam) - IDM_CommandX;\r
6943         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6944             icsTextMenuEntry[i].command != NULL) {\r
6945           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6946                    icsTextMenuEntry[i].getname,\r
6947                    icsTextMenuEntry[i].immediate);\r
6948           return 0;\r
6949         }\r
6950       }\r
6951       break;\r
6952     }\r
6953     break;\r
6954   }\r
6955   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6956 }\r
6957 \r
6958 WNDPROC consoleInputWindowProc;\r
6959 \r
6960 LRESULT CALLBACK\r
6961 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6962 {\r
6963   char buf[MSG_SIZ];\r
6964   char *p;\r
6965   static BOOL sendNextChar = FALSE;\r
6966   static BOOL quoteNextChar = FALSE;\r
6967   InputSource *is = consoleInputSource;\r
6968   CHARFORMAT cf;\r
6969   CHARRANGE sel;\r
6970 \r
6971   switch (message) {\r
6972   case WM_CHAR:\r
6973     if (!appData.localLineEditing || sendNextChar) {\r
6974       is->buf[0] = (CHAR) wParam;\r
6975       is->count = 1;\r
6976       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6977       sendNextChar = FALSE;\r
6978       return 0;\r
6979     }\r
6980     if (quoteNextChar) {\r
6981       buf[0] = (char) wParam;\r
6982       buf[1] = NULLCHAR;\r
6983       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
6984       quoteNextChar = FALSE;\r
6985       return 0;\r
6986     }\r
6987     switch (wParam) {\r
6988     case '\r':   /* Enter key */\r
6989       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
6990       if (consoleEcho) SaveInHistory(is->buf);\r
6991       is->buf[is->count++] = '\n';\r
6992       is->buf[is->count] = NULLCHAR;\r
6993       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6994       if (consoleEcho) {\r
6995         ConsoleOutput(is->buf, is->count, TRUE);\r
6996       } else if (appData.localLineEditing) {\r
6997         ConsoleOutput("\n", 1, TRUE);\r
6998       }\r
6999       /* fall thru */\r
7000     case '\033': /* Escape key */\r
7001       SetWindowText(hwnd, "");\r
7002       cf.cbSize = sizeof(CHARFORMAT);\r
7003       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7004       if (consoleEcho) {\r
7005         cf.crTextColor = textAttribs[ColorNormal].color;\r
7006       } else {\r
7007         cf.crTextColor = COLOR_ECHOOFF;\r
7008       }\r
7009       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7010       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7011       return 0;\r
7012     case '\t':   /* Tab key */\r
7013       if (GetKeyState(VK_SHIFT) < 0) {\r
7014         /* shifted */\r
7015         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7016       } else {\r
7017         /* unshifted */\r
7018         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7019         if (buttonDesc[0].hwnd) {\r
7020           SetFocus(buttonDesc[0].hwnd);\r
7021         } else {\r
7022           SetFocus(hwndMain);\r
7023         }\r
7024       }\r
7025       return 0;\r
7026     case '\023': /* Ctrl+S */\r
7027       sendNextChar = TRUE;\r
7028       return 0;\r
7029     case '\021': /* Ctrl+Q */\r
7030       quoteNextChar = TRUE;\r
7031       return 0;\r
7032     JAWS_REPLAY\r
7033     default:\r
7034       break;\r
7035     }\r
7036     break;\r
7037   case WM_KEYDOWN:\r
7038     switch (wParam) {\r
7039     case VK_UP:\r
7040       GetWindowText(hwnd, buf, MSG_SIZ);\r
7041       p = PrevInHistory(buf);\r
7042       if (p != NULL) {\r
7043         SetWindowText(hwnd, p);\r
7044         sel.cpMin = 999999;\r
7045         sel.cpMax = 999999;\r
7046         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7047         return 0;\r
7048       }\r
7049       break;\r
7050     case VK_DOWN:\r
7051       p = NextInHistory();\r
7052       if (p != NULL) {\r
7053         SetWindowText(hwnd, p);\r
7054         sel.cpMin = 999999;\r
7055         sel.cpMax = 999999;\r
7056         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7057         return 0;\r
7058       }\r
7059       break;\r
7060     case VK_HOME:\r
7061     case VK_END:\r
7062       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7063       /* fall thru */\r
7064     case VK_PRIOR:\r
7065     case VK_NEXT:\r
7066       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7067       return 0;\r
7068     }\r
7069     break;\r
7070   case WM_MBUTTONDOWN:\r
7071     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7072       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7073     break;\r
7074   case WM_RBUTTONUP:\r
7075     if (GetKeyState(VK_SHIFT) & ~1) {\r
7076       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7077         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7078     } else {\r
7079       POINT pt;\r
7080       HMENU hmenu;\r
7081       hmenu = LoadMenu(hInst, "InputMenu");\r
7082       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7083       if (sel.cpMin == sel.cpMax) {\r
7084         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7085         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7086       }\r
7087       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7088         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7089       }\r
7090       pt.x = LOWORD(lParam);\r
7091       pt.y = HIWORD(lParam);\r
7092       MenuPopup(hwnd, pt, hmenu, -1);\r
7093     }\r
7094     return 0;\r
7095   case WM_COMMAND:\r
7096     switch (LOWORD(wParam)) { \r
7097     case IDM_Undo:\r
7098       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7099       return 0;\r
7100     case IDM_SelectAll:\r
7101       sel.cpMin = 0;\r
7102       sel.cpMax = -1; /*999999?*/\r
7103       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7104       return 0;\r
7105     case IDM_Cut:\r
7106       SendMessage(hwnd, WM_CUT, 0, 0);\r
7107       return 0;\r
7108     case IDM_Paste:\r
7109       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7110       return 0;\r
7111     case IDM_Copy:\r
7112       SendMessage(hwnd, WM_COPY, 0, 0);\r
7113       return 0;\r
7114     }\r
7115     break;\r
7116   }\r
7117   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7118 }\r
7119 \r
7120 #define CO_MAX  100000\r
7121 #define CO_TRIM   1000\r
7122 \r
7123 LRESULT CALLBACK\r
7124 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7125 {\r
7126   static SnapData sd;\r
7127   HWND hText, hInput;\r
7128   RECT rect;\r
7129   static int sizeX, sizeY;\r
7130   int newSizeX, newSizeY;\r
7131   MINMAXINFO *mmi;\r
7132   WORD wMask;\r
7133 \r
7134   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7135   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7136 \r
7137   switch (message) {\r
7138   case WM_NOTIFY:\r
7139     if (((NMHDR*)lParam)->code == EN_LINK)\r
7140     {\r
7141       ENLINK *pLink = (ENLINK*)lParam;\r
7142       if (pLink->msg == WM_LBUTTONUP)\r
7143       {\r
7144         TEXTRANGE tr;\r
7145 \r
7146         tr.chrg = pLink->chrg;\r
7147         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7148         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7149         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7150         free(tr.lpstrText);\r
7151       }\r
7152     }\r
7153     break;\r
7154   case WM_INITDIALOG: /* message: initialize dialog box */\r
7155     hwndConsole = hDlg;\r
7156     SetFocus(hInput);\r
7157     consoleTextWindowProc = (WNDPROC)\r
7158       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7159     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7160     consoleInputWindowProc = (WNDPROC)\r
7161       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7162     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7163     Colorize(ColorNormal, TRUE);\r
7164     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7165     ChangedConsoleFont();\r
7166     GetClientRect(hDlg, &rect);\r
7167     sizeX = rect.right;\r
7168     sizeY = rect.bottom;\r
7169     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7170         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7171       WINDOWPLACEMENT wp;\r
7172       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7173       wp.length = sizeof(WINDOWPLACEMENT);\r
7174       wp.flags = 0;\r
7175       wp.showCmd = SW_SHOW;\r
7176       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7177       wp.rcNormalPosition.left = wpConsole.x;\r
7178       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7179       wp.rcNormalPosition.top = wpConsole.y;\r
7180       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7181       SetWindowPlacement(hDlg, &wp);\r
7182     }\r
7183 \r
7184    // [HGM] Chessknight's change 2004-07-13\r
7185    else { /* Determine Defaults */\r
7186        WINDOWPLACEMENT wp;\r
7187        wpConsole.x = wpMain.width + 1;\r
7188        wpConsole.y = wpMain.y;\r
7189        wpConsole.width = screenWidth -  wpMain.width;\r
7190        wpConsole.height = wpMain.height;\r
7191        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7192        wp.length = sizeof(WINDOWPLACEMENT);\r
7193        wp.flags = 0;\r
7194        wp.showCmd = SW_SHOW;\r
7195        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7196        wp.rcNormalPosition.left = wpConsole.x;\r
7197        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7198        wp.rcNormalPosition.top = wpConsole.y;\r
7199        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7200        SetWindowPlacement(hDlg, &wp);\r
7201     }\r
7202 \r
7203    // Allow hText to highlight URLs and send notifications on them\r
7204    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7205    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7206    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7207    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7208 \r
7209     return FALSE;\r
7210 \r
7211   case WM_SETFOCUS:\r
7212     SetFocus(hInput);\r
7213     return 0;\r
7214 \r
7215   case WM_CLOSE:\r
7216     ExitEvent(0);\r
7217     /* not reached */\r
7218     break;\r
7219 \r
7220   case WM_SIZE:\r
7221     if (IsIconic(hDlg)) break;\r
7222     newSizeX = LOWORD(lParam);\r
7223     newSizeY = HIWORD(lParam);\r
7224     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7225       RECT rectText, rectInput;\r
7226       POINT pt;\r
7227       int newTextHeight, newTextWidth;\r
7228       GetWindowRect(hText, &rectText);\r
7229       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7230       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7231       if (newTextHeight < 0) {\r
7232         newSizeY += -newTextHeight;\r
7233         newTextHeight = 0;\r
7234       }\r
7235       SetWindowPos(hText, NULL, 0, 0,\r
7236         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7237       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7238       pt.x = rectInput.left;\r
7239       pt.y = rectInput.top + newSizeY - sizeY;\r
7240       ScreenToClient(hDlg, &pt);\r
7241       SetWindowPos(hInput, NULL, \r
7242         pt.x, pt.y, /* needs client coords */   \r
7243         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7244         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7245     }\r
7246     sizeX = newSizeX;\r
7247     sizeY = newSizeY;\r
7248     break;\r
7249 \r
7250   case WM_GETMINMAXINFO:\r
7251     /* Prevent resizing window too small */\r
7252     mmi = (MINMAXINFO *) lParam;\r
7253     mmi->ptMinTrackSize.x = 100;\r
7254     mmi->ptMinTrackSize.y = 100;\r
7255     break;\r
7256 \r
7257   /* [AS] Snapping */\r
7258   case WM_ENTERSIZEMOVE:\r
7259     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7260 \r
7261   case WM_SIZING:\r
7262     return OnSizing( &sd, hDlg, wParam, lParam );\r
7263 \r
7264   case WM_MOVING:\r
7265     return OnMoving( &sd, hDlg, wParam, lParam );\r
7266 \r
7267   case WM_EXITSIZEMOVE:\r
7268         UpdateICSWidth(hText);\r
7269     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7270   }\r
7271 \r
7272   return DefWindowProc(hDlg, message, wParam, lParam);\r
7273 }\r
7274 \r
7275 \r
7276 VOID\r
7277 ConsoleCreate()\r
7278 {\r
7279   HWND hCons;\r
7280   if (hwndConsole) return;\r
7281   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7282   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7283 }\r
7284 \r
7285 \r
7286 VOID\r
7287 ConsoleOutput(char* data, int length, int forceVisible)\r
7288 {\r
7289   HWND hText;\r
7290   int trim, exlen;\r
7291   char *p, *q;\r
7292   char buf[CO_MAX+1];\r
7293   POINT pEnd;\r
7294   RECT rect;\r
7295   static int delayLF = 0;\r
7296   CHARRANGE savesel, sel;\r
7297 \r
7298   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7299   p = data;\r
7300   q = buf;\r
7301   if (delayLF) {\r
7302     *q++ = '\r';\r
7303     *q++ = '\n';\r
7304     delayLF = 0;\r
7305   }\r
7306   while (length--) {\r
7307     if (*p == '\n') {\r
7308       if (*++p) {\r
7309         *q++ = '\r';\r
7310         *q++ = '\n';\r
7311       } else {\r
7312         delayLF = 1;\r
7313       }\r
7314     } else if (*p == '\007') {\r
7315        MyPlaySound(&sounds[(int)SoundBell]);\r
7316        p++;\r
7317     } else {\r
7318       *q++ = *p++;\r
7319     }\r
7320   }\r
7321   *q = NULLCHAR;\r
7322   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7323   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7324   /* Save current selection */\r
7325   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7326   exlen = GetWindowTextLength(hText);\r
7327   /* Find out whether current end of text is visible */\r
7328   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7329   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7330   /* Trim existing text if it's too long */\r
7331   if (exlen + (q - buf) > CO_MAX) {\r
7332     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7333     sel.cpMin = 0;\r
7334     sel.cpMax = trim;\r
7335     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7336     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7337     exlen -= trim;\r
7338     savesel.cpMin -= trim;\r
7339     savesel.cpMax -= trim;\r
7340     if (exlen < 0) exlen = 0;\r
7341     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7342     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7343   }\r
7344   /* Append the new text */\r
7345   sel.cpMin = exlen;\r
7346   sel.cpMax = exlen;\r
7347   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7348   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7349   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7350   if (forceVisible || exlen == 0 ||\r
7351       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7352        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7353     /* Scroll to make new end of text visible if old end of text\r
7354        was visible or new text is an echo of user typein */\r
7355     sel.cpMin = 9999999;\r
7356     sel.cpMax = 9999999;\r
7357     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7358     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7359     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7360     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7361   }\r
7362   if (savesel.cpMax == exlen || forceVisible) {\r
7363     /* Move insert point to new end of text if it was at the old\r
7364        end of text or if the new text is an echo of user typein */\r
7365     sel.cpMin = 9999999;\r
7366     sel.cpMax = 9999999;\r
7367     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7368   } else {\r
7369     /* Restore previous selection */\r
7370     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7371   }\r
7372   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7373 }\r
7374 \r
7375 /*---------*/\r
7376 \r
7377 \r
7378 void\r
7379 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7380 {\r
7381   char buf[100];\r
7382   char *str;\r
7383   COLORREF oldFg, oldBg;\r
7384   HFONT oldFont;\r
7385   RECT rect;\r
7386 \r
7387   if(copyNumber > 1)
7388     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7389 \r
7390   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7391   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7392   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7393 \r
7394   rect.left = x;\r
7395   rect.right = x + squareSize;\r
7396   rect.top  = y;\r
7397   rect.bottom = y + squareSize;\r
7398   str = buf;\r
7399 \r
7400   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7401                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7402              y, ETO_CLIPPED|ETO_OPAQUE,\r
7403              &rect, str, strlen(str), NULL);\r
7404 \r
7405   (void) SetTextColor(hdc, oldFg);\r
7406   (void) SetBkColor(hdc, oldBg);\r
7407   (void) SelectObject(hdc, oldFont);\r
7408 }\r
7409 \r
7410 void\r
7411 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7412               RECT *rect, char *color, char *flagFell)\r
7413 {\r
7414   char buf[100];\r
7415   char *str;\r
7416   COLORREF oldFg, oldBg;\r
7417   HFONT oldFont;\r
7418 \r
7419   if (appData.clockMode) {\r
7420     if (tinyLayout)\r
7421       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7422     else\r
7423       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7424     str = buf;\r
7425   } else {\r
7426     str = color;\r
7427   }\r
7428 \r
7429   if (highlight) {\r
7430     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7431     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7432   } else {\r
7433     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7434     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7435   }\r
7436   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7437 \r
7438   JAWS_SILENCE\r
7439 \r
7440   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7441              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7442              rect, str, strlen(str), NULL);\r
7443   if(logoHeight > 0 && appData.clockMode) {\r
7444       RECT r;\r
7445       str += strlen(color)+2;\r
7446       r.top = rect->top + logoHeight/2;\r
7447       r.left = rect->left;\r
7448       r.right = rect->right;\r
7449       r.bottom = rect->bottom;\r
7450       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7451                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7452                  &r, str, strlen(str), NULL);\r
7453   }\r
7454   (void) SetTextColor(hdc, oldFg);\r
7455   (void) SetBkColor(hdc, oldBg);\r
7456   (void) SelectObject(hdc, oldFont);\r
7457 }\r
7458 \r
7459 \r
7460 int\r
7461 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7462            OVERLAPPED *ovl)\r
7463 {\r
7464   int ok, err;\r
7465 \r
7466   /* [AS]  */\r
7467   if( count <= 0 ) {\r
7468     if (appData.debugMode) {\r
7469       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7470     }\r
7471 \r
7472     return ERROR_INVALID_USER_BUFFER;\r
7473   }\r
7474 \r
7475   ResetEvent(ovl->hEvent);\r
7476   ovl->Offset = ovl->OffsetHigh = 0;\r
7477   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7478   if (ok) {\r
7479     err = NO_ERROR;\r
7480   } else {\r
7481     err = GetLastError();\r
7482     if (err == ERROR_IO_PENDING) {\r
7483       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7484       if (ok)\r
7485         err = NO_ERROR;\r
7486       else\r
7487         err = GetLastError();\r
7488     }\r
7489   }\r
7490   return err;\r
7491 }\r
7492 \r
7493 int\r
7494 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7495             OVERLAPPED *ovl)\r
7496 {\r
7497   int ok, err;\r
7498 \r
7499   ResetEvent(ovl->hEvent);\r
7500   ovl->Offset = ovl->OffsetHigh = 0;\r
7501   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7502   if (ok) {\r
7503     err = NO_ERROR;\r
7504   } else {\r
7505     err = GetLastError();\r
7506     if (err == ERROR_IO_PENDING) {\r
7507       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7508       if (ok)\r
7509         err = NO_ERROR;\r
7510       else\r
7511         err = GetLastError();\r
7512     }\r
7513   }\r
7514   return err;\r
7515 }\r
7516 \r
7517 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7518 void CheckForInputBufferFull( InputSource * is )\r
7519 {\r
7520     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7521         /* Look for end of line */\r
7522         char * p = is->buf;\r
7523         \r
7524         while( p < is->next && *p != '\n' ) {\r
7525             p++;\r
7526         }\r
7527 \r
7528         if( p >= is->next ) {\r
7529             if (appData.debugMode) {\r
7530                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7531             }\r
7532 \r
7533             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7534             is->count = (DWORD) -1;\r
7535             is->next = is->buf;\r
7536         }\r
7537     }\r
7538 }\r
7539 \r
7540 DWORD\r
7541 InputThread(LPVOID arg)\r
7542 {\r
7543   InputSource *is;\r
7544   OVERLAPPED ovl;\r
7545 \r
7546   is = (InputSource *) arg;\r
7547   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7548   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7549   while (is->hThread != NULL) {\r
7550     is->error = DoReadFile(is->hFile, is->next,\r
7551                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7552                            &is->count, &ovl);\r
7553     if (is->error == NO_ERROR) {\r
7554       is->next += is->count;\r
7555     } else {\r
7556       if (is->error == ERROR_BROKEN_PIPE) {\r
7557         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7558         is->count = 0;\r
7559       } else {\r
7560         is->count = (DWORD) -1;\r
7561         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7562         break; \r
7563       }\r
7564     }\r
7565 \r
7566     CheckForInputBufferFull( is );\r
7567 \r
7568     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7569 \r
7570     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7571 \r
7572     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7573   }\r
7574 \r
7575   CloseHandle(ovl.hEvent);\r
7576   CloseHandle(is->hFile);\r
7577 \r
7578   if (appData.debugMode) {\r
7579     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7580   }\r
7581 \r
7582   return 0;\r
7583 }\r
7584 \r
7585 \r
7586 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7587 DWORD\r
7588 NonOvlInputThread(LPVOID arg)\r
7589 {\r
7590   InputSource *is;\r
7591   char *p, *q;\r
7592   int i;\r
7593   char prev;\r
7594 \r
7595   is = (InputSource *) arg;\r
7596   while (is->hThread != NULL) {\r
7597     is->error = ReadFile(is->hFile, is->next,\r
7598                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7599                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7600     if (is->error == NO_ERROR) {\r
7601       /* Change CRLF to LF */\r
7602       if (is->next > is->buf) {\r
7603         p = is->next - 1;\r
7604         i = is->count + 1;\r
7605       } else {\r
7606         p = is->next;\r
7607         i = is->count;\r
7608       }\r
7609       q = p;\r
7610       prev = NULLCHAR;\r
7611       while (i > 0) {\r
7612         if (prev == '\r' && *p == '\n') {\r
7613           *(q-1) = '\n';\r
7614           is->count--;\r
7615         } else { \r
7616           *q++ = *p;\r
7617         }\r
7618         prev = *p++;\r
7619         i--;\r
7620       }\r
7621       *q = NULLCHAR;\r
7622       is->next = q;\r
7623     } else {\r
7624       if (is->error == ERROR_BROKEN_PIPE) {\r
7625         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7626         is->count = 0; \r
7627       } else {\r
7628         is->count = (DWORD) -1;\r
7629       }\r
7630     }\r
7631 \r
7632     CheckForInputBufferFull( is );\r
7633 \r
7634     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7635 \r
7636     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7637 \r
7638     if (is->count < 0) break;  /* Quit on error */\r
7639   }\r
7640   CloseHandle(is->hFile);\r
7641   return 0;\r
7642 }\r
7643 \r
7644 DWORD\r
7645 SocketInputThread(LPVOID arg)\r
7646 {\r
7647   InputSource *is;\r
7648 \r
7649   is = (InputSource *) arg;\r
7650   while (is->hThread != NULL) {\r
7651     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7652     if ((int)is->count == SOCKET_ERROR) {\r
7653       is->count = (DWORD) -1;\r
7654       is->error = WSAGetLastError();\r
7655     } else {\r
7656       is->error = NO_ERROR;\r
7657       is->next += is->count;\r
7658       if (is->count == 0 && is->second == is) {\r
7659         /* End of file on stderr; quit with no message */\r
7660         break;\r
7661       }\r
7662     }\r
7663     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7664 \r
7665     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7666 \r
7667     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7668   }\r
7669   return 0;\r
7670 }\r
7671 \r
7672 VOID\r
7673 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7674 {\r
7675   InputSource *is;\r
7676 \r
7677   is = (InputSource *) lParam;\r
7678   if (is->lineByLine) {\r
7679     /* Feed in lines one by one */\r
7680     char *p = is->buf;\r
7681     char *q = p;\r
7682     while (q < is->next) {\r
7683       if (*q++ == '\n') {\r
7684         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7685         p = q;\r
7686       }\r
7687     }\r
7688     \r
7689     /* Move any partial line to the start of the buffer */\r
7690     q = is->buf;\r
7691     while (p < is->next) {\r
7692       *q++ = *p++;\r
7693     }\r
7694     is->next = q;\r
7695 \r
7696     if (is->error != NO_ERROR || is->count == 0) {\r
7697       /* Notify backend of the error.  Note: If there was a partial\r
7698          line at the end, it is not flushed through. */\r
7699       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7700     }\r
7701   } else {\r
7702     /* Feed in the whole chunk of input at once */\r
7703     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7704     is->next = is->buf;\r
7705   }\r
7706 }\r
7707 \r
7708 /*---------------------------------------------------------------------------*\\r
7709  *\r
7710  *  Menu enables. Used when setting various modes.\r
7711  *\r
7712 \*---------------------------------------------------------------------------*/\r
7713 \r
7714 typedef struct {\r
7715   int item;\r
7716   int flags;\r
7717 } Enables;\r
7718 \r
7719 VOID\r
7720 GreyRevert(Boolean grey)\r
7721 { // [HGM] vari: for retracting variations in local mode\r
7722   HMENU hmenu = GetMenu(hwndMain);\r
7723   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7724   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7725 }\r
7726 \r
7727 VOID\r
7728 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7729 {\r
7730   while (enab->item > 0) {\r
7731     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7732     enab++;\r
7733   }\r
7734 }\r
7735 \r
7736 Enables gnuEnables[] = {\r
7737   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7738   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7739   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7740   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7741   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7742   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7743   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7744   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7745   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7746   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7747   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7748   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7749   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7750   { -1, -1 }\r
7751 };\r
7752 \r
7753 Enables icsEnables[] = {\r
7754   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7755   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7756   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7757   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7758   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7759   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7760   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7761   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7762   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7763   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7764   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7765   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7766   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7767   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7768   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7769   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7770   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7771   { -1, -1 }\r
7772 };\r
7773 \r
7774 #if ZIPPY\r
7775 Enables zippyEnables[] = {\r
7776   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7777   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7778   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7779   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7780   { -1, -1 }\r
7781 };\r
7782 #endif\r
7783 \r
7784 Enables ncpEnables[] = {\r
7785   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7786   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7787   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7788   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7789   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7790   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7791   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7792   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7793   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7794   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7795   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7796   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7797   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7798   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7799   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7800   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7801   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7802   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7803   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7804   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7805   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7806   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7807   { -1, -1 }\r
7808 };\r
7809 \r
7810 Enables trainingOnEnables[] = {\r
7811   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7812   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7813   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7814   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7815   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7816   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7817   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7818   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7819   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7820   { -1, -1 }\r
7821 };\r
7822 \r
7823 Enables trainingOffEnables[] = {\r
7824   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7825   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7826   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7827   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7828   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7829   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7830   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7831   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7832   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7833   { -1, -1 }\r
7834 };\r
7835 \r
7836 /* These modify either ncpEnables or gnuEnables */\r
7837 Enables cmailEnables[] = {\r
7838   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7839   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7840   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7841   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7842   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7843   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7844   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7845   { -1, -1 }\r
7846 };\r
7847 \r
7848 Enables machineThinkingEnables[] = {\r
7849   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7850   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7851   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7852   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7853   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7854   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7855   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7856   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7857   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7858   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7859   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7860   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7861   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7862   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7863   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7864   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7865   { -1, -1 }\r
7866 };\r
7867 \r
7868 Enables userThinkingEnables[] = {\r
7869   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7870   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7871   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7872   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7873   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7874   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7875   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7876   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7877   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7878   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7879   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7880   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7881   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7882   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7883   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7884   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7885   { -1, -1 }\r
7886 };\r
7887 \r
7888 /*---------------------------------------------------------------------------*\\r
7889  *\r
7890  *  Front-end interface functions exported by XBoard.\r
7891  *  Functions appear in same order as prototypes in frontend.h.\r
7892  * \r
7893 \*---------------------------------------------------------------------------*/\r
7894 VOID\r
7895 ModeHighlight()\r
7896 {\r
7897   static UINT prevChecked = 0;\r
7898   static int prevPausing = 0;\r
7899   UINT nowChecked;\r
7900 \r
7901   if (pausing != prevPausing) {\r
7902     prevPausing = pausing;\r
7903     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7904                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7905     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7906   }\r
7907 \r
7908   switch (gameMode) {\r
7909   case BeginningOfGame:\r
7910     if (appData.icsActive)\r
7911       nowChecked = IDM_IcsClient;\r
7912     else if (appData.noChessProgram)\r
7913       nowChecked = IDM_EditGame;\r
7914     else\r
7915       nowChecked = IDM_MachineBlack;\r
7916     break;\r
7917   case MachinePlaysBlack:\r
7918     nowChecked = IDM_MachineBlack;\r
7919     break;\r
7920   case MachinePlaysWhite:\r
7921     nowChecked = IDM_MachineWhite;\r
7922     break;\r
7923   case TwoMachinesPlay:\r
7924     nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match\r
7925     break;\r
7926   case AnalyzeMode:\r
7927     nowChecked = IDM_AnalysisMode;\r
7928     break;\r
7929   case AnalyzeFile:\r
7930     nowChecked = IDM_AnalyzeFile;\r
7931     break;\r
7932   case EditGame:\r
7933     nowChecked = IDM_EditGame;\r
7934     break;\r
7935   case PlayFromGameFile:\r
7936     nowChecked = IDM_LoadGame;\r
7937     break;\r
7938   case EditPosition:\r
7939     nowChecked = IDM_EditPosition;\r
7940     break;\r
7941   case Training:\r
7942     nowChecked = IDM_Training;\r
7943     break;\r
7944   case IcsPlayingWhite:\r
7945   case IcsPlayingBlack:\r
7946   case IcsObserving:\r
7947   case IcsIdle:\r
7948     nowChecked = IDM_IcsClient;\r
7949     break;\r
7950   default:\r
7951   case EndOfGame:\r
7952     nowChecked = 0;\r
7953     break;\r
7954   }\r
7955   if (prevChecked != 0)\r
7956     (void) CheckMenuItem(GetMenu(hwndMain),\r
7957                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7958   if (nowChecked != 0)\r
7959     (void) CheckMenuItem(GetMenu(hwndMain),\r
7960                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
7961 \r
7962   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7963     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7964                           MF_BYCOMMAND|MF_ENABLED);\r
7965   } else {\r
7966     (void) EnableMenuItem(GetMenu(hwndMain), \r
7967                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7968   }\r
7969 \r
7970   prevChecked = nowChecked;\r
7971 \r
7972   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
7973   if (appData.icsActive) {\r
7974        if (appData.icsEngineAnalyze) {\r
7975                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7976                        MF_BYCOMMAND|MF_CHECKED);\r
7977        } else {\r
7978                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7979                        MF_BYCOMMAND|MF_UNCHECKED);\r
7980        }\r
7981   }\r
7982   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
7983 }\r
7984 \r
7985 VOID\r
7986 SetICSMode()\r
7987 {\r
7988   HMENU hmenu = GetMenu(hwndMain);\r
7989   SetMenuEnables(hmenu, icsEnables);\r
7990   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
7991     MF_BYCOMMAND|MF_ENABLED);\r
7992 #if ZIPPY\r
7993   if (appData.zippyPlay) {\r
7994     SetMenuEnables(hmenu, zippyEnables);\r
7995     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
7996          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7997           MF_BYCOMMAND|MF_ENABLED);\r
7998   }\r
7999 #endif\r
8000 }\r
8001 \r
8002 VOID\r
8003 SetGNUMode()\r
8004 {\r
8005   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8006 }\r
8007 \r
8008 VOID\r
8009 SetNCPMode()\r
8010 {\r
8011   HMENU hmenu = GetMenu(hwndMain);\r
8012   SetMenuEnables(hmenu, ncpEnables);\r
8013     DrawMenuBar(hwndMain);\r
8014 }\r
8015 \r
8016 VOID\r
8017 SetCmailMode()\r
8018 {\r
8019   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8020 }\r
8021 \r
8022 VOID \r
8023 SetTrainingModeOn()\r
8024 {\r
8025   int i;\r
8026   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8027   for (i = 0; i < N_BUTTONS; i++) {\r
8028     if (buttonDesc[i].hwnd != NULL)\r
8029       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8030   }\r
8031   CommentPopDown();\r
8032 }\r
8033 \r
8034 VOID SetTrainingModeOff()\r
8035 {\r
8036   int i;\r
8037   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8038   for (i = 0; i < N_BUTTONS; i++) {\r
8039     if (buttonDesc[i].hwnd != NULL)\r
8040       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8041   }\r
8042 }\r
8043 \r
8044 \r
8045 VOID\r
8046 SetUserThinkingEnables()\r
8047 {\r
8048   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8049 }\r
8050 \r
8051 VOID\r
8052 SetMachineThinkingEnables()\r
8053 {\r
8054   HMENU hMenu = GetMenu(hwndMain);\r
8055   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8056 \r
8057   SetMenuEnables(hMenu, machineThinkingEnables);\r
8058 \r
8059   if (gameMode == MachinePlaysBlack) {\r
8060     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8061   } else if (gameMode == MachinePlaysWhite) {\r
8062     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8063   } else if (gameMode == TwoMachinesPlay) {\r
8064     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8065   }\r
8066 }\r
8067 \r
8068 \r
8069 VOID\r
8070 DisplayTitle(char *str)\r
8071 {\r
8072   char title[MSG_SIZ], *host;\r
8073   if (str[0] != NULLCHAR) {\r
8074     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8075   } else if (appData.icsActive) {\r
8076     if (appData.icsCommPort[0] != NULLCHAR)\r
8077       host = "ICS";\r
8078     else \r
8079       host = appData.icsHost;\r
8080       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8081   } else if (appData.noChessProgram) {\r
8082     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8083   } else {\r
8084     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8085     strcat(title, ": ");\r
8086     strcat(title, first.tidy);\r
8087   }\r
8088   SetWindowText(hwndMain, title);\r
8089 }\r
8090 \r
8091 \r
8092 VOID\r
8093 DisplayMessage(char *str1, char *str2)\r
8094 {\r
8095   HDC hdc;\r
8096   HFONT oldFont;\r
8097   int remain = MESSAGE_TEXT_MAX - 1;\r
8098   int len;\r
8099 \r
8100   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8101   messageText[0] = NULLCHAR;\r
8102   if (*str1) {\r
8103     len = strlen(str1);\r
8104     if (len > remain) len = remain;\r
8105     strncpy(messageText, str1, len);\r
8106     messageText[len] = NULLCHAR;\r
8107     remain -= len;\r
8108   }\r
8109   if (*str2 && remain >= 2) {\r
8110     if (*str1) {\r
8111       strcat(messageText, "  ");\r
8112       remain -= 2;\r
8113     }\r
8114     len = strlen(str2);\r
8115     if (len > remain) len = remain;\r
8116     strncat(messageText, str2, len);\r
8117   }\r
8118   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8119 \r
8120   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8121 \r
8122   SAYMACHINEMOVE();\r
8123 \r
8124   hdc = GetDC(hwndMain);\r
8125   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8126   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8127              &messageRect, messageText, strlen(messageText), NULL);\r
8128   (void) SelectObject(hdc, oldFont);\r
8129   (void) ReleaseDC(hwndMain, hdc);\r
8130 }\r
8131 \r
8132 VOID\r
8133 DisplayError(char *str, int error)\r
8134 {\r
8135   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8136   int len;\r
8137 \r
8138   if (error == 0) {\r
8139     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8140   } else {\r
8141     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8142                         NULL, error, LANG_NEUTRAL,\r
8143                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8144     if (len > 0) {\r
8145       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8146     } else {\r
8147       ErrorMap *em = errmap;\r
8148       while (em->err != 0 && em->err != error) em++;\r
8149       if (em->err != 0) {\r
8150         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8151       } else {\r
8152         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8153       }\r
8154     }\r
8155   }\r
8156   \r
8157   ErrorPopUp(_("Error"), buf);\r
8158 }\r
8159 \r
8160 \r
8161 VOID\r
8162 DisplayMoveError(char *str)\r
8163 {\r
8164   fromX = fromY = -1;\r
8165   ClearHighlights();\r
8166   DrawPosition(FALSE, NULL);\r
8167   if (appData.popupMoveErrors) {\r
8168     ErrorPopUp(_("Error"), str);\r
8169   } else {\r
8170     DisplayMessage(str, "");\r
8171     moveErrorMessageUp = TRUE;\r
8172   }\r
8173 }\r
8174 \r
8175 VOID\r
8176 DisplayFatalError(char *str, int error, int exitStatus)\r
8177 {\r
8178   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8179   int len;\r
8180   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8181 \r
8182   if (error != 0) {\r
8183     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8184                         NULL, error, LANG_NEUTRAL,\r
8185                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8186     if (len > 0) {\r
8187       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8188     } else {\r
8189       ErrorMap *em = errmap;\r
8190       while (em->err != 0 && em->err != error) em++;\r
8191       if (em->err != 0) {\r
8192         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8193       } else {\r
8194         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8195       }\r
8196     }\r
8197     str = buf;\r
8198   }\r
8199   if (appData.debugMode) {\r
8200     fprintf(debugFP, "%s: %s\n", label, str);\r
8201   }\r
8202   if (appData.popupExitMessage) {\r
8203     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8204                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8205   }\r
8206   ExitEvent(exitStatus);\r
8207 }\r
8208 \r
8209 \r
8210 VOID\r
8211 DisplayInformation(char *str)\r
8212 {\r
8213   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8214 }\r
8215 \r
8216 \r
8217 VOID\r
8218 DisplayNote(char *str)\r
8219 {\r
8220   ErrorPopUp(_("Note"), str);\r
8221 }\r
8222 \r
8223 \r
8224 typedef struct {\r
8225   char *title, *question, *replyPrefix;\r
8226   ProcRef pr;\r
8227 } QuestionParams;\r
8228 \r
8229 LRESULT CALLBACK\r
8230 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8231 {\r
8232   static QuestionParams *qp;\r
8233   char reply[MSG_SIZ];\r
8234   int len, err;\r
8235 \r
8236   switch (message) {\r
8237   case WM_INITDIALOG:\r
8238     qp = (QuestionParams *) lParam;\r
8239     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8240     Translate(hDlg, DLG_Question);\r
8241     SetWindowText(hDlg, qp->title);\r
8242     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8243     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8244     return FALSE;\r
8245 \r
8246   case WM_COMMAND:\r
8247     switch (LOWORD(wParam)) {\r
8248     case IDOK:\r
8249       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8250       if (*reply) strcat(reply, " ");\r
8251       len = strlen(reply);\r
8252       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8253       strcat(reply, "\n");\r
8254       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8255       EndDialog(hDlg, TRUE);\r
8256       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8257       return TRUE;\r
8258     case IDCANCEL:\r
8259       EndDialog(hDlg, FALSE);\r
8260       return TRUE;\r
8261     default:\r
8262       break;\r
8263     }\r
8264     break;\r
8265   }\r
8266   return FALSE;\r
8267 }\r
8268 \r
8269 VOID\r
8270 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8271 {\r
8272     QuestionParams qp;\r
8273     FARPROC lpProc;\r
8274     \r
8275     qp.title = title;\r
8276     qp.question = question;\r
8277     qp.replyPrefix = replyPrefix;\r
8278     qp.pr = pr;\r
8279     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8280     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8281       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8282     FreeProcInstance(lpProc);\r
8283 }\r
8284 \r
8285 /* [AS] Pick FRC position */\r
8286 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8287 {\r
8288     static int * lpIndexFRC;\r
8289     BOOL index_is_ok;\r
8290     char buf[16];\r
8291 \r
8292     switch( message )\r
8293     {\r
8294     case WM_INITDIALOG:\r
8295         lpIndexFRC = (int *) lParam;\r
8296 \r
8297         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8298         Translate(hDlg, DLG_NewGameFRC);\r
8299 \r
8300         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8301         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8302         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8303         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8304 \r
8305         break;\r
8306 \r
8307     case WM_COMMAND:\r
8308         switch( LOWORD(wParam) ) {\r
8309         case IDOK:\r
8310             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8311             EndDialog( hDlg, 0 );\r
8312             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8313             return TRUE;\r
8314         case IDCANCEL:\r
8315             EndDialog( hDlg, 1 );   \r
8316             return TRUE;\r
8317         case IDC_NFG_Edit:\r
8318             if( HIWORD(wParam) == EN_CHANGE ) {\r
8319                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8320 \r
8321                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8322             }\r
8323             return TRUE;\r
8324         case IDC_NFG_Random:\r
8325           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8326             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8327             return TRUE;\r
8328         }\r
8329 \r
8330         break;\r
8331     }\r
8332 \r
8333     return FALSE;\r
8334 }\r
8335 \r
8336 int NewGameFRC()\r
8337 {\r
8338     int result;\r
8339     int index = appData.defaultFrcPosition;\r
8340     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8341 \r
8342     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8343 \r
8344     if( result == 0 ) {\r
8345         appData.defaultFrcPosition = index;\r
8346     }\r
8347 \r
8348     return result;\r
8349 }\r
8350 \r
8351 /* [AS] Game list options. Refactored by HGM */\r
8352 \r
8353 HWND gameListOptionsDialog;\r
8354 \r
8355 // low-level front-end: clear text edit / list widget\r
8356 void\r
8357 GLT_ClearList()\r
8358 {\r
8359     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8360 }\r
8361 \r
8362 // low-level front-end: clear text edit / list widget\r
8363 void\r
8364 GLT_DeSelectList()\r
8365 {\r
8366     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8367 }\r
8368 \r
8369 // low-level front-end: append line to text edit / list widget\r
8370 void\r
8371 GLT_AddToList( char *name )\r
8372 {\r
8373     if( name != 0 ) {\r
8374             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8375     }\r
8376 }\r
8377 \r
8378 // low-level front-end: get line from text edit / list widget\r
8379 Boolean\r
8380 GLT_GetFromList( int index, char *name )\r
8381 {\r
8382     if( name != 0 ) {\r
8383             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8384                 return TRUE;\r
8385     }\r
8386     return FALSE;\r
8387 }\r
8388 \r
8389 void GLT_MoveSelection( HWND hDlg, int delta )\r
8390 {\r
8391     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8392     int idx2 = idx1 + delta;\r
8393     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8394 \r
8395     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8396         char buf[128];\r
8397 \r
8398         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8399         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8400         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8401         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8402     }\r
8403 }\r
8404 \r
8405 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8406 {\r
8407     switch( message )\r
8408     {\r
8409     case WM_INITDIALOG:\r
8410         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8411         \r
8412         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8413         Translate(hDlg, DLG_GameListOptions);\r
8414 \r
8415         /* Initialize list */\r
8416         GLT_TagsToList( lpUserGLT );\r
8417 \r
8418         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8419 \r
8420         break;\r
8421 \r
8422     case WM_COMMAND:\r
8423         switch( LOWORD(wParam) ) {\r
8424         case IDOK:\r
8425             GLT_ParseList();\r
8426             EndDialog( hDlg, 0 );\r
8427             return TRUE;\r
8428         case IDCANCEL:\r
8429             EndDialog( hDlg, 1 );\r
8430             return TRUE;\r
8431 \r
8432         case IDC_GLT_Default:\r
8433             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8434             return TRUE;\r
8435 \r
8436         case IDC_GLT_Restore:\r
8437             GLT_TagsToList( appData.gameListTags );\r
8438             return TRUE;\r
8439 \r
8440         case IDC_GLT_Up:\r
8441             GLT_MoveSelection( hDlg, -1 );\r
8442             return TRUE;\r
8443 \r
8444         case IDC_GLT_Down:\r
8445             GLT_MoveSelection( hDlg, +1 );\r
8446             return TRUE;\r
8447         }\r
8448 \r
8449         break;\r
8450     }\r
8451 \r
8452     return FALSE;\r
8453 }\r
8454 \r
8455 int GameListOptions()\r
8456 {\r
8457     int result;\r
8458     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8459 \r
8460       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8461 \r
8462     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8463 \r
8464     if( result == 0 ) {\r
8465         /* [AS] Memory leak here! */\r
8466         appData.gameListTags = strdup( lpUserGLT ); \r
8467     }\r
8468 \r
8469     return result;\r
8470 }\r
8471 \r
8472 VOID\r
8473 DisplayIcsInteractionTitle(char *str)\r
8474 {\r
8475   char consoleTitle[MSG_SIZ];\r
8476 \r
8477     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8478   SetWindowText(hwndConsole, consoleTitle);\r
8479 }\r
8480 \r
8481 void\r
8482 DrawPosition(int fullRedraw, Board board)\r
8483 {\r
8484   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8485 }\r
8486 \r
8487 void NotifyFrontendLogin()\r
8488 {\r
8489         if (hwndConsole)\r
8490                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8491 }\r
8492 \r
8493 VOID\r
8494 ResetFrontEnd()\r
8495 {\r
8496   fromX = fromY = -1;\r
8497   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8498     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8499     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8500     dragInfo.lastpos = dragInfo.pos;\r
8501     dragInfo.start.x = dragInfo.start.y = -1;\r
8502     dragInfo.from = dragInfo.start;\r
8503     ReleaseCapture();\r
8504     DrawPosition(TRUE, NULL);\r
8505   }\r
8506   TagsPopDown();\r
8507 }\r
8508 \r
8509 \r
8510 VOID\r
8511 CommentPopUp(char *title, char *str)\r
8512 {\r
8513   HWND hwnd = GetActiveWindow();\r
8514   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8515   SAY(str);\r
8516   SetActiveWindow(hwnd);\r
8517 }\r
8518 \r
8519 VOID\r
8520 CommentPopDown(void)\r
8521 {\r
8522   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8523   if (commentDialog) {\r
8524     ShowWindow(commentDialog, SW_HIDE);\r
8525   }\r
8526   commentUp = FALSE;\r
8527 }\r
8528 \r
8529 VOID\r
8530 EditCommentPopUp(int index, char *title, char *str)\r
8531 {\r
8532   EitherCommentPopUp(index, title, str, TRUE);\r
8533 }\r
8534 \r
8535 \r
8536 VOID\r
8537 RingBell()\r
8538 {\r
8539   MyPlaySound(&sounds[(int)SoundMove]);\r
8540 }\r
8541 \r
8542 VOID PlayIcsWinSound()\r
8543 {\r
8544   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8545 }\r
8546 \r
8547 VOID PlayIcsLossSound()\r
8548 {\r
8549   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8550 }\r
8551 \r
8552 VOID PlayIcsDrawSound()\r
8553 {\r
8554   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8555 }\r
8556 \r
8557 VOID PlayIcsUnfinishedSound()\r
8558 {\r
8559   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8560 }\r
8561 \r
8562 VOID\r
8563 PlayAlarmSound()\r
8564 {\r
8565   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8566 }\r
8567 \r
8568 \r
8569 VOID\r
8570 EchoOn()\r
8571 {\r
8572   HWND hInput;\r
8573   consoleEcho = TRUE;\r
8574   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8575   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8576   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8577 }\r
8578 \r
8579 \r
8580 VOID\r
8581 EchoOff()\r
8582 {\r
8583   CHARFORMAT cf;\r
8584   HWND hInput;\r
8585   consoleEcho = FALSE;\r
8586   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8587   /* This works OK: set text and background both to the same color */\r
8588   cf = consoleCF;\r
8589   cf.crTextColor = COLOR_ECHOOFF;\r
8590   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8591   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8592 }\r
8593 \r
8594 /* No Raw()...? */\r
8595 \r
8596 void Colorize(ColorClass cc, int continuation)\r
8597 {\r
8598   currentColorClass = cc;\r
8599   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8600   consoleCF.crTextColor = textAttribs[cc].color;\r
8601   consoleCF.dwEffects = textAttribs[cc].effects;\r
8602   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8603 }\r
8604 \r
8605 char *\r
8606 UserName()\r
8607 {\r
8608   static char buf[MSG_SIZ];\r
8609   DWORD bufsiz = MSG_SIZ;\r
8610 \r
8611   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8612         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8613   }\r
8614   if (!GetUserName(buf, &bufsiz)) {\r
8615     /*DisplayError("Error getting user name", GetLastError());*/\r
8616     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8617   }\r
8618   return buf;\r
8619 }\r
8620 \r
8621 char *\r
8622 HostName()\r
8623 {\r
8624   static char buf[MSG_SIZ];\r
8625   DWORD bufsiz = MSG_SIZ;\r
8626 \r
8627   if (!GetComputerName(buf, &bufsiz)) {\r
8628     /*DisplayError("Error getting host name", GetLastError());*/\r
8629     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8630   }\r
8631   return buf;\r
8632 }\r
8633 \r
8634 \r
8635 int\r
8636 ClockTimerRunning()\r
8637 {\r
8638   return clockTimerEvent != 0;\r
8639 }\r
8640 \r
8641 int\r
8642 StopClockTimer()\r
8643 {\r
8644   if (clockTimerEvent == 0) return FALSE;\r
8645   KillTimer(hwndMain, clockTimerEvent);\r
8646   clockTimerEvent = 0;\r
8647   return TRUE;\r
8648 }\r
8649 \r
8650 void\r
8651 StartClockTimer(long millisec)\r
8652 {\r
8653   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8654                              (UINT) millisec, NULL);\r
8655 }\r
8656 \r
8657 void\r
8658 DisplayWhiteClock(long timeRemaining, int highlight)\r
8659 {\r
8660   HDC hdc;\r
8661   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8662 \r
8663   if(appData.noGUI) return;\r
8664   hdc = GetDC(hwndMain);\r
8665   if (!IsIconic(hwndMain)) {\r
8666     DisplayAClock(hdc, timeRemaining, highlight, \r
8667                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8668   }\r
8669   if (highlight && iconCurrent == iconBlack) {\r
8670     iconCurrent = iconWhite;\r
8671     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8672     if (IsIconic(hwndMain)) {\r
8673       DrawIcon(hdc, 2, 2, iconCurrent);\r
8674     }\r
8675   }\r
8676   (void) ReleaseDC(hwndMain, hdc);\r
8677   if (hwndConsole)\r
8678     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8679 }\r
8680 \r
8681 void\r
8682 DisplayBlackClock(long timeRemaining, int highlight)\r
8683 {\r
8684   HDC hdc;\r
8685   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8686 \r
8687   if(appData.noGUI) return;\r
8688   hdc = GetDC(hwndMain);\r
8689   if (!IsIconic(hwndMain)) {\r
8690     DisplayAClock(hdc, timeRemaining, highlight, \r
8691                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8692   }\r
8693   if (highlight && iconCurrent == iconWhite) {\r
8694     iconCurrent = iconBlack;\r
8695     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8696     if (IsIconic(hwndMain)) {\r
8697       DrawIcon(hdc, 2, 2, iconCurrent);\r
8698     }\r
8699   }\r
8700   (void) ReleaseDC(hwndMain, hdc);\r
8701   if (hwndConsole)\r
8702     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8703 }\r
8704 \r
8705 \r
8706 int\r
8707 LoadGameTimerRunning()\r
8708 {\r
8709   return loadGameTimerEvent != 0;\r
8710 }\r
8711 \r
8712 int\r
8713 StopLoadGameTimer()\r
8714 {\r
8715   if (loadGameTimerEvent == 0) return FALSE;\r
8716   KillTimer(hwndMain, loadGameTimerEvent);\r
8717   loadGameTimerEvent = 0;\r
8718   return TRUE;\r
8719 }\r
8720 \r
8721 void\r
8722 StartLoadGameTimer(long millisec)\r
8723 {\r
8724   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8725                                 (UINT) millisec, NULL);\r
8726 }\r
8727 \r
8728 void\r
8729 AutoSaveGame()\r
8730 {\r
8731   char *defName;\r
8732   FILE *f;\r
8733   char fileTitle[MSG_SIZ];\r
8734 \r
8735   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8736   f = OpenFileDialog(hwndMain, "a", defName,\r
8737                      appData.oldSaveStyle ? "gam" : "pgn",\r
8738                      GAME_FILT, \r
8739                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8740   if (f != NULL) {\r
8741     SaveGame(f, 0, "");\r
8742     fclose(f);\r
8743   }\r
8744 }\r
8745 \r
8746 \r
8747 void\r
8748 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8749 {\r
8750   if (delayedTimerEvent != 0) {\r
8751     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8752       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8753     }\r
8754     KillTimer(hwndMain, delayedTimerEvent);\r
8755     delayedTimerEvent = 0;\r
8756     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8757     delayedTimerCallback();\r
8758   }\r
8759   delayedTimerCallback = cb;\r
8760   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8761                                 (UINT) millisec, NULL);\r
8762 }\r
8763 \r
8764 DelayedEventCallback\r
8765 GetDelayedEvent()\r
8766 {\r
8767   if (delayedTimerEvent) {\r
8768     return delayedTimerCallback;\r
8769   } else {\r
8770     return NULL;\r
8771   }\r
8772 }\r
8773 \r
8774 void\r
8775 CancelDelayedEvent()\r
8776 {\r
8777   if (delayedTimerEvent) {\r
8778     KillTimer(hwndMain, delayedTimerEvent);\r
8779     delayedTimerEvent = 0;\r
8780   }\r
8781 }\r
8782 \r
8783 DWORD GetWin32Priority(int nice)\r
8784 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8785 /*\r
8786 REALTIME_PRIORITY_CLASS     0x00000100\r
8787 HIGH_PRIORITY_CLASS         0x00000080\r
8788 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8789 NORMAL_PRIORITY_CLASS       0x00000020\r
8790 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8791 IDLE_PRIORITY_CLASS         0x00000040\r
8792 */\r
8793         if (nice < -15) return 0x00000080;\r
8794         if (nice < 0)   return 0x00008000;\r
8795         if (nice == 0)  return 0x00000020;\r
8796         if (nice < 15)  return 0x00004000;\r
8797         return 0x00000040;\r
8798 }\r
8799 \r
8800 /* Start a child process running the given program.\r
8801    The process's standard output can be read from "from", and its\r
8802    standard input can be written to "to".\r
8803    Exit with fatal error if anything goes wrong.\r
8804    Returns an opaque pointer that can be used to destroy the process\r
8805    later.\r
8806 */\r
8807 int\r
8808 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8809 {\r
8810 #define BUFSIZE 4096\r
8811 \r
8812   HANDLE hChildStdinRd, hChildStdinWr,\r
8813     hChildStdoutRd, hChildStdoutWr;\r
8814   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8815   SECURITY_ATTRIBUTES saAttr;\r
8816   BOOL fSuccess;\r
8817   PROCESS_INFORMATION piProcInfo;\r
8818   STARTUPINFO siStartInfo;\r
8819   ChildProc *cp;\r
8820   char buf[MSG_SIZ];\r
8821   DWORD err;\r
8822 \r
8823   if (appData.debugMode) {\r
8824     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8825   }\r
8826 \r
8827   *pr = NoProc;\r
8828 \r
8829   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8830   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8831   saAttr.bInheritHandle = TRUE;\r
8832   saAttr.lpSecurityDescriptor = NULL;\r
8833 \r
8834   /*\r
8835    * The steps for redirecting child's STDOUT:\r
8836    *     1. Create anonymous pipe to be STDOUT for child.\r
8837    *     2. Create a noninheritable duplicate of read handle,\r
8838    *         and close the inheritable read handle.\r
8839    */\r
8840 \r
8841   /* Create a pipe for the child's STDOUT. */\r
8842   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8843     return GetLastError();\r
8844   }\r
8845 \r
8846   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8847   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8848                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8849                              FALSE,     /* not inherited */\r
8850                              DUPLICATE_SAME_ACCESS);\r
8851   if (! fSuccess) {\r
8852     return GetLastError();\r
8853   }\r
8854   CloseHandle(hChildStdoutRd);\r
8855 \r
8856   /*\r
8857    * The steps for redirecting child's STDIN:\r
8858    *     1. Create anonymous pipe to be STDIN for child.\r
8859    *     2. Create a noninheritable duplicate of write handle,\r
8860    *         and close the inheritable write handle.\r
8861    */\r
8862 \r
8863   /* Create a pipe for the child's STDIN. */\r
8864   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8865     return GetLastError();\r
8866   }\r
8867 \r
8868   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8869   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8870                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8871                              FALSE,     /* not inherited */\r
8872                              DUPLICATE_SAME_ACCESS);\r
8873   if (! fSuccess) {\r
8874     return GetLastError();\r
8875   }\r
8876   CloseHandle(hChildStdinWr);\r
8877 \r
8878   /* Arrange to (1) look in dir for the child .exe file, and\r
8879    * (2) have dir be the child's working directory.  Interpret\r
8880    * dir relative to the directory WinBoard loaded from. */\r
8881   GetCurrentDirectory(MSG_SIZ, buf);\r
8882   SetCurrentDirectory(installDir);\r
8883   SetCurrentDirectory(dir);\r
8884 \r
8885   /* Now create the child process. */\r
8886 \r
8887   siStartInfo.cb = sizeof(STARTUPINFO);\r
8888   siStartInfo.lpReserved = NULL;\r
8889   siStartInfo.lpDesktop = NULL;\r
8890   siStartInfo.lpTitle = NULL;\r
8891   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8892   siStartInfo.cbReserved2 = 0;\r
8893   siStartInfo.lpReserved2 = NULL;\r
8894   siStartInfo.hStdInput = hChildStdinRd;\r
8895   siStartInfo.hStdOutput = hChildStdoutWr;\r
8896   siStartInfo.hStdError = hChildStdoutWr;\r
8897 \r
8898   fSuccess = CreateProcess(NULL,\r
8899                            cmdLine,        /* command line */\r
8900                            NULL,           /* process security attributes */\r
8901                            NULL,           /* primary thread security attrs */\r
8902                            TRUE,           /* handles are inherited */\r
8903                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8904                            NULL,           /* use parent's environment */\r
8905                            NULL,\r
8906                            &siStartInfo, /* STARTUPINFO pointer */\r
8907                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8908 \r
8909   err = GetLastError();\r
8910   SetCurrentDirectory(buf); /* return to prev directory */\r
8911   if (! fSuccess) {\r
8912     return err;\r
8913   }\r
8914 \r
8915   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8916     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8917     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8918   }\r
8919 \r
8920   /* Close the handles we don't need in the parent */\r
8921   CloseHandle(piProcInfo.hThread);\r
8922   CloseHandle(hChildStdinRd);\r
8923   CloseHandle(hChildStdoutWr);\r
8924 \r
8925   /* Prepare return value */\r
8926   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8927   cp->kind = CPReal;\r
8928   cp->hProcess = piProcInfo.hProcess;\r
8929   cp->pid = piProcInfo.dwProcessId;\r
8930   cp->hFrom = hChildStdoutRdDup;\r
8931   cp->hTo = hChildStdinWrDup;\r
8932 \r
8933   *pr = (void *) cp;\r
8934 \r
8935   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8936      2000 where engines sometimes don't see the initial command(s)\r
8937      from WinBoard and hang.  I don't understand how that can happen,\r
8938      but the Sleep is harmless, so I've put it in.  Others have also\r
8939      reported what may be the same problem, so hopefully this will fix\r
8940      it for them too.  */\r
8941   Sleep(500);\r
8942 \r
8943   return NO_ERROR;\r
8944 }\r
8945 \r
8946 \r
8947 void\r
8948 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8949 {\r
8950   ChildProc *cp; int result;\r
8951 \r
8952   cp = (ChildProc *) pr;\r
8953   if (cp == NULL) return;\r
8954 \r
8955   switch (cp->kind) {\r
8956   case CPReal:\r
8957     /* TerminateProcess is considered harmful, so... */\r
8958     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8959     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8960     /* The following doesn't work because the chess program\r
8961        doesn't "have the same console" as WinBoard.  Maybe\r
8962        we could arrange for this even though neither WinBoard\r
8963        nor the chess program uses a console for stdio? */\r
8964     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8965 \r
8966     /* [AS] Special termination modes for misbehaving programs... */\r
8967     if( signal == 9 ) { \r
8968         result = TerminateProcess( cp->hProcess, 0 );\r
8969 \r
8970         if ( appData.debugMode) {\r
8971             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
8972         }\r
8973     }\r
8974     else if( signal == 10 ) {\r
8975         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
8976 \r
8977         if( dw != WAIT_OBJECT_0 ) {\r
8978             result = TerminateProcess( cp->hProcess, 0 );\r
8979 \r
8980             if ( appData.debugMode) {\r
8981                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
8982             }\r
8983 \r
8984         }\r
8985     }\r
8986 \r
8987     CloseHandle(cp->hProcess);\r
8988     break;\r
8989 \r
8990   case CPComm:\r
8991     if (cp->hFrom) CloseHandle(cp->hFrom);\r
8992     break;\r
8993 \r
8994   case CPSock:\r
8995     closesocket(cp->sock);\r
8996     WSACleanup();\r
8997     break;\r
8998 \r
8999   case CPRcmd:\r
9000     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9001     closesocket(cp->sock);\r
9002     closesocket(cp->sock2);\r
9003     WSACleanup();\r
9004     break;\r
9005   }\r
9006   free(cp);\r
9007 }\r
9008 \r
9009 void\r
9010 InterruptChildProcess(ProcRef pr)\r
9011 {\r
9012   ChildProc *cp;\r
9013 \r
9014   cp = (ChildProc *) pr;\r
9015   if (cp == NULL) return;\r
9016   switch (cp->kind) {\r
9017   case CPReal:\r
9018     /* The following doesn't work because the chess program\r
9019        doesn't "have the same console" as WinBoard.  Maybe\r
9020        we could arrange for this even though neither WinBoard\r
9021        nor the chess program uses a console for stdio */\r
9022     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9023     break;\r
9024 \r
9025   case CPComm:\r
9026   case CPSock:\r
9027     /* Can't interrupt */\r
9028     break;\r
9029 \r
9030   case CPRcmd:\r
9031     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9032     break;\r
9033   }\r
9034 }\r
9035 \r
9036 \r
9037 int\r
9038 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9039 {\r
9040   char cmdLine[MSG_SIZ];\r
9041 \r
9042   if (port[0] == NULLCHAR) {\r
9043     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9044   } else {\r
9045     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9046   }\r
9047   return StartChildProcess(cmdLine, "", pr);\r
9048 }\r
9049 \r
9050 \r
9051 /* Code to open TCP sockets */\r
9052 \r
9053 int\r
9054 OpenTCP(char *host, char *port, ProcRef *pr)\r
9055 {\r
9056   ChildProc *cp;\r
9057   int err;\r
9058   SOCKET s;\r
9059   struct sockaddr_in sa, mysa;\r
9060   struct hostent FAR *hp;\r
9061   unsigned short uport;\r
9062   WORD wVersionRequested;\r
9063   WSADATA wsaData;\r
9064 \r
9065   /* Initialize socket DLL */\r
9066   wVersionRequested = MAKEWORD(1, 1);\r
9067   err = WSAStartup(wVersionRequested, &wsaData);\r
9068   if (err != 0) return err;\r
9069 \r
9070   /* Make socket */\r
9071   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9072     err = WSAGetLastError();\r
9073     WSACleanup();\r
9074     return err;\r
9075   }\r
9076 \r
9077   /* Bind local address using (mostly) don't-care values.\r
9078    */\r
9079   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9080   mysa.sin_family = AF_INET;\r
9081   mysa.sin_addr.s_addr = INADDR_ANY;\r
9082   uport = (unsigned short) 0;\r
9083   mysa.sin_port = htons(uport);\r
9084   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9085       == SOCKET_ERROR) {\r
9086     err = WSAGetLastError();\r
9087     WSACleanup();\r
9088     return err;\r
9089   }\r
9090 \r
9091   /* Resolve remote host name */\r
9092   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9093   if (!(hp = gethostbyname(host))) {\r
9094     unsigned int b0, b1, b2, b3;\r
9095 \r
9096     err = WSAGetLastError();\r
9097 \r
9098     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9099       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9100       hp->h_addrtype = AF_INET;\r
9101       hp->h_length = 4;\r
9102       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9103       hp->h_addr_list[0] = (char *) malloc(4);\r
9104       hp->h_addr_list[0][0] = (char) b0;\r
9105       hp->h_addr_list[0][1] = (char) b1;\r
9106       hp->h_addr_list[0][2] = (char) b2;\r
9107       hp->h_addr_list[0][3] = (char) b3;\r
9108     } else {\r
9109       WSACleanup();\r
9110       return err;\r
9111     }\r
9112   }\r
9113   sa.sin_family = hp->h_addrtype;\r
9114   uport = (unsigned short) atoi(port);\r
9115   sa.sin_port = htons(uport);\r
9116   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9117 \r
9118   /* Make connection */\r
9119   if (connect(s, (struct sockaddr *) &sa,\r
9120               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9121     err = WSAGetLastError();\r
9122     WSACleanup();\r
9123     return err;\r
9124   }\r
9125 \r
9126   /* Prepare return value */\r
9127   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9128   cp->kind = CPSock;\r
9129   cp->sock = s;\r
9130   *pr = (ProcRef *) cp;\r
9131 \r
9132   return NO_ERROR;\r
9133 }\r
9134 \r
9135 int\r
9136 OpenCommPort(char *name, ProcRef *pr)\r
9137 {\r
9138   HANDLE h;\r
9139   COMMTIMEOUTS ct;\r
9140   ChildProc *cp;\r
9141   char fullname[MSG_SIZ];\r
9142 \r
9143   if (*name != '\\')\r
9144     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9145   else\r
9146     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9147 \r
9148   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9149                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9150   if (h == (HANDLE) -1) {\r
9151     return GetLastError();\r
9152   }\r
9153   hCommPort = h;\r
9154 \r
9155   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9156 \r
9157   /* Accumulate characters until a 100ms pause, then parse */\r
9158   ct.ReadIntervalTimeout = 100;\r
9159   ct.ReadTotalTimeoutMultiplier = 0;\r
9160   ct.ReadTotalTimeoutConstant = 0;\r
9161   ct.WriteTotalTimeoutMultiplier = 0;\r
9162   ct.WriteTotalTimeoutConstant = 0;\r
9163   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9164 \r
9165   /* Prepare return value */\r
9166   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9167   cp->kind = CPComm;\r
9168   cp->hFrom = h;\r
9169   cp->hTo = h;\r
9170   *pr = (ProcRef *) cp;\r
9171 \r
9172   return NO_ERROR;\r
9173 }\r
9174 \r
9175 int\r
9176 OpenLoopback(ProcRef *pr)\r
9177 {\r
9178   DisplayFatalError(_("Not implemented"), 0, 1);\r
9179   return NO_ERROR;\r
9180 }\r
9181 \r
9182 \r
9183 int\r
9184 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9185 {\r
9186   ChildProc *cp;\r
9187   int err;\r
9188   SOCKET s, s2, s3;\r
9189   struct sockaddr_in sa, mysa;\r
9190   struct hostent FAR *hp;\r
9191   unsigned short uport;\r
9192   WORD wVersionRequested;\r
9193   WSADATA wsaData;\r
9194   int fromPort;\r
9195   char stderrPortStr[MSG_SIZ];\r
9196 \r
9197   /* Initialize socket DLL */\r
9198   wVersionRequested = MAKEWORD(1, 1);\r
9199   err = WSAStartup(wVersionRequested, &wsaData);\r
9200   if (err != 0) return err;\r
9201 \r
9202   /* Resolve remote host name */\r
9203   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9204   if (!(hp = gethostbyname(host))) {\r
9205     unsigned int b0, b1, b2, b3;\r
9206 \r
9207     err = WSAGetLastError();\r
9208 \r
9209     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9210       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9211       hp->h_addrtype = AF_INET;\r
9212       hp->h_length = 4;\r
9213       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9214       hp->h_addr_list[0] = (char *) malloc(4);\r
9215       hp->h_addr_list[0][0] = (char) b0;\r
9216       hp->h_addr_list[0][1] = (char) b1;\r
9217       hp->h_addr_list[0][2] = (char) b2;\r
9218       hp->h_addr_list[0][3] = (char) b3;\r
9219     } else {\r
9220       WSACleanup();\r
9221       return err;\r
9222     }\r
9223   }\r
9224   sa.sin_family = hp->h_addrtype;\r
9225   uport = (unsigned short) 514;\r
9226   sa.sin_port = htons(uport);\r
9227   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9228 \r
9229   /* Bind local socket to unused "privileged" port address\r
9230    */\r
9231   s = INVALID_SOCKET;\r
9232   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9233   mysa.sin_family = AF_INET;\r
9234   mysa.sin_addr.s_addr = INADDR_ANY;\r
9235   for (fromPort = 1023;; fromPort--) {\r
9236     if (fromPort < 0) {\r
9237       WSACleanup();\r
9238       return WSAEADDRINUSE;\r
9239     }\r
9240     if (s == INVALID_SOCKET) {\r
9241       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9242         err = WSAGetLastError();\r
9243         WSACleanup();\r
9244         return err;\r
9245       }\r
9246     }\r
9247     uport = (unsigned short) fromPort;\r
9248     mysa.sin_port = htons(uport);\r
9249     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9250         == SOCKET_ERROR) {\r
9251       err = WSAGetLastError();\r
9252       if (err == WSAEADDRINUSE) continue;\r
9253       WSACleanup();\r
9254       return err;\r
9255     }\r
9256     if (connect(s, (struct sockaddr *) &sa,\r
9257       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9258       err = WSAGetLastError();\r
9259       if (err == WSAEADDRINUSE) {\r
9260         closesocket(s);\r
9261         s = -1;\r
9262         continue;\r
9263       }\r
9264       WSACleanup();\r
9265       return err;\r
9266     }\r
9267     break;\r
9268   }\r
9269 \r
9270   /* Bind stderr local socket to unused "privileged" port address\r
9271    */\r
9272   s2 = INVALID_SOCKET;\r
9273   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9274   mysa.sin_family = AF_INET;\r
9275   mysa.sin_addr.s_addr = INADDR_ANY;\r
9276   for (fromPort = 1023;; fromPort--) {\r
9277     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9278     if (fromPort < 0) {\r
9279       (void) closesocket(s);\r
9280       WSACleanup();\r
9281       return WSAEADDRINUSE;\r
9282     }\r
9283     if (s2 == INVALID_SOCKET) {\r
9284       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9285         err = WSAGetLastError();\r
9286         closesocket(s);\r
9287         WSACleanup();\r
9288         return err;\r
9289       }\r
9290     }\r
9291     uport = (unsigned short) fromPort;\r
9292     mysa.sin_port = htons(uport);\r
9293     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9294         == SOCKET_ERROR) {\r
9295       err = WSAGetLastError();\r
9296       if (err == WSAEADDRINUSE) continue;\r
9297       (void) closesocket(s);\r
9298       WSACleanup();\r
9299       return err;\r
9300     }\r
9301     if (listen(s2, 1) == SOCKET_ERROR) {\r
9302       err = WSAGetLastError();\r
9303       if (err == WSAEADDRINUSE) {\r
9304         closesocket(s2);\r
9305         s2 = INVALID_SOCKET;\r
9306         continue;\r
9307       }\r
9308       (void) closesocket(s);\r
9309       (void) closesocket(s2);\r
9310       WSACleanup();\r
9311       return err;\r
9312     }\r
9313     break;\r
9314   }\r
9315   prevStderrPort = fromPort; // remember port used\r
9316   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9317 \r
9318   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9319     err = WSAGetLastError();\r
9320     (void) closesocket(s);\r
9321     (void) closesocket(s2);\r
9322     WSACleanup();\r
9323     return err;\r
9324   }\r
9325 \r
9326   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9327     err = WSAGetLastError();\r
9328     (void) closesocket(s);\r
9329     (void) closesocket(s2);\r
9330     WSACleanup();\r
9331     return err;\r
9332   }\r
9333   if (*user == NULLCHAR) user = UserName();\r
9334   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9335     err = WSAGetLastError();\r
9336     (void) closesocket(s);\r
9337     (void) closesocket(s2);\r
9338     WSACleanup();\r
9339     return err;\r
9340   }\r
9341   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9342     err = WSAGetLastError();\r
9343     (void) closesocket(s);\r
9344     (void) closesocket(s2);\r
9345     WSACleanup();\r
9346     return err;\r
9347   }\r
9348 \r
9349   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9350     err = WSAGetLastError();\r
9351     (void) closesocket(s);\r
9352     (void) closesocket(s2);\r
9353     WSACleanup();\r
9354     return err;\r
9355   }\r
9356   (void) closesocket(s2);  /* Stop listening */\r
9357 \r
9358   /* Prepare return value */\r
9359   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9360   cp->kind = CPRcmd;\r
9361   cp->sock = s;\r
9362   cp->sock2 = s3;\r
9363   *pr = (ProcRef *) cp;\r
9364 \r
9365   return NO_ERROR;\r
9366 }\r
9367 \r
9368 \r
9369 InputSourceRef\r
9370 AddInputSource(ProcRef pr, int lineByLine,\r
9371                InputCallback func, VOIDSTAR closure)\r
9372 {\r
9373   InputSource *is, *is2 = NULL;\r
9374   ChildProc *cp = (ChildProc *) pr;\r
9375 \r
9376   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9377   is->lineByLine = lineByLine;\r
9378   is->func = func;\r
9379   is->closure = closure;\r
9380   is->second = NULL;\r
9381   is->next = is->buf;\r
9382   if (pr == NoProc) {\r
9383     is->kind = CPReal;\r
9384     consoleInputSource = is;\r
9385   } else {\r
9386     is->kind = cp->kind;\r
9387     /* \r
9388         [AS] Try to avoid a race condition if the thread is given control too early:\r
9389         we create all threads suspended so that the is->hThread variable can be\r
9390         safely assigned, then let the threads start with ResumeThread.\r
9391     */\r
9392     switch (cp->kind) {\r
9393     case CPReal:\r
9394       is->hFile = cp->hFrom;\r
9395       cp->hFrom = NULL; /* now owned by InputThread */\r
9396       is->hThread =\r
9397         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9398                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9399       break;\r
9400 \r
9401     case CPComm:\r
9402       is->hFile = cp->hFrom;\r
9403       cp->hFrom = NULL; /* now owned by InputThread */\r
9404       is->hThread =\r
9405         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9406                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9407       break;\r
9408 \r
9409     case CPSock:\r
9410       is->sock = cp->sock;\r
9411       is->hThread =\r
9412         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9413                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9414       break;\r
9415 \r
9416     case CPRcmd:\r
9417       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9418       *is2 = *is;\r
9419       is->sock = cp->sock;\r
9420       is->second = is2;\r
9421       is2->sock = cp->sock2;\r
9422       is2->second = is2;\r
9423       is->hThread =\r
9424         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9425                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9426       is2->hThread =\r
9427         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9428                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9429       break;\r
9430     }\r
9431 \r
9432     if( is->hThread != NULL ) {\r
9433         ResumeThread( is->hThread );\r
9434     }\r
9435 \r
9436     if( is2 != NULL && is2->hThread != NULL ) {\r
9437         ResumeThread( is2->hThread );\r
9438     }\r
9439   }\r
9440 \r
9441   return (InputSourceRef) is;\r
9442 }\r
9443 \r
9444 void\r
9445 RemoveInputSource(InputSourceRef isr)\r
9446 {\r
9447   InputSource *is;\r
9448 \r
9449   is = (InputSource *) isr;\r
9450   is->hThread = NULL;  /* tell thread to stop */\r
9451   CloseHandle(is->hThread);\r
9452   if (is->second != NULL) {\r
9453     is->second->hThread = NULL;\r
9454     CloseHandle(is->second->hThread);\r
9455   }\r
9456 }\r
9457 \r
9458 int no_wrap(char *message, int count)\r
9459 {\r
9460     ConsoleOutput(message, count, FALSE);\r
9461     return count;\r
9462 }\r
9463 \r
9464 int\r
9465 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9466 {\r
9467   DWORD dOutCount;\r
9468   int outCount = SOCKET_ERROR;\r
9469   ChildProc *cp = (ChildProc *) pr;\r
9470   static OVERLAPPED ovl;\r
9471   static int line = 0;\r
9472 \r
9473   if (pr == NoProc)\r
9474   {\r
9475     if (appData.noJoin || !appData.useInternalWrap)\r
9476       return no_wrap(message, count);\r
9477     else\r
9478     {\r
9479       int width = get_term_width();\r
9480       int len = wrap(NULL, message, count, width, &line);\r
9481       char *msg = malloc(len);\r
9482       int dbgchk;\r
9483 \r
9484       if (!msg)\r
9485         return no_wrap(message, count);\r
9486       else\r
9487       {\r
9488         dbgchk = wrap(msg, message, count, width, &line);\r
9489         if (dbgchk != len && appData.debugMode)\r
9490             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9491         ConsoleOutput(msg, len, FALSE);\r
9492         free(msg);\r
9493         return len;\r
9494       }\r
9495     }\r
9496   }\r
9497 \r
9498   if (ovl.hEvent == NULL) {\r
9499     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9500   }\r
9501   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9502 \r
9503   switch (cp->kind) {\r
9504   case CPSock:\r
9505   case CPRcmd:\r
9506     outCount = send(cp->sock, message, count, 0);\r
9507     if (outCount == SOCKET_ERROR) {\r
9508       *outError = WSAGetLastError();\r
9509     } else {\r
9510       *outError = NO_ERROR;\r
9511     }\r
9512     break;\r
9513 \r
9514   case CPReal:\r
9515     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9516                   &dOutCount, NULL)) {\r
9517       *outError = NO_ERROR;\r
9518       outCount = (int) dOutCount;\r
9519     } else {\r
9520       *outError = GetLastError();\r
9521     }\r
9522     break;\r
9523 \r
9524   case CPComm:\r
9525     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9526                             &dOutCount, &ovl);\r
9527     if (*outError == NO_ERROR) {\r
9528       outCount = (int) dOutCount;\r
9529     }\r
9530     break;\r
9531   }\r
9532   return outCount;\r
9533 }\r
9534 \r
9535 int\r
9536 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9537                        long msdelay)\r
9538 {\r
9539   /* Ignore delay, not implemented for WinBoard */\r
9540   return OutputToProcess(pr, message, count, outError);\r
9541 }\r
9542 \r
9543 \r
9544 void\r
9545 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9546                         char *buf, int count, int error)\r
9547 {\r
9548   DisplayFatalError(_("Not implemented"), 0, 1);\r
9549 }\r
9550 \r
9551 /* see wgamelist.c for Game List functions */\r
9552 /* see wedittags.c for Edit Tags functions */\r
9553 \r
9554 \r
9555 VOID\r
9556 ICSInitScript()\r
9557 {\r
9558   FILE *f;\r
9559   char buf[MSG_SIZ];\r
9560   char *dummy;\r
9561 \r
9562   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9563     f = fopen(buf, "r");\r
9564     if (f != NULL) {\r
9565       ProcessICSInitScript(f);\r
9566       fclose(f);\r
9567     }\r
9568   }\r
9569 }\r
9570 \r
9571 \r
9572 VOID\r
9573 StartAnalysisClock()\r
9574 {\r
9575   if (analysisTimerEvent) return;\r
9576   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9577                                         (UINT) 2000, NULL);\r
9578 }\r
9579 \r
9580 VOID\r
9581 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9582 {\r
9583   highlightInfo.sq[0].x = fromX;\r
9584   highlightInfo.sq[0].y = fromY;\r
9585   highlightInfo.sq[1].x = toX;\r
9586   highlightInfo.sq[1].y = toY;\r
9587 }\r
9588 \r
9589 VOID\r
9590 ClearHighlights()\r
9591 {\r
9592   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9593     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9594 }\r
9595 \r
9596 VOID\r
9597 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9598 {\r
9599   premoveHighlightInfo.sq[0].x = fromX;\r
9600   premoveHighlightInfo.sq[0].y = fromY;\r
9601   premoveHighlightInfo.sq[1].x = toX;\r
9602   premoveHighlightInfo.sq[1].y = toY;\r
9603 }\r
9604 \r
9605 VOID\r
9606 ClearPremoveHighlights()\r
9607 {\r
9608   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9609     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9610 }\r
9611 \r
9612 VOID\r
9613 ShutDownFrontEnd()\r
9614 {\r
9615   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9616   DeleteClipboardTempFiles();\r
9617 }\r
9618 \r
9619 void\r
9620 BoardToTop()\r
9621 {\r
9622     if (IsIconic(hwndMain))\r
9623       ShowWindow(hwndMain, SW_RESTORE);\r
9624 \r
9625     SetActiveWindow(hwndMain);\r
9626 }\r
9627 \r
9628 /*\r
9629  * Prototypes for animation support routines\r
9630  */\r
9631 static void ScreenSquare(int column, int row, POINT * pt);\r
9632 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9633      POINT frames[], int * nFrames);\r
9634 \r
9635 \r
9636 #define kFactor 4\r
9637 \r
9638 void\r
9639 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9640 {       // [HGM] atomic: animate blast wave\r
9641         int i;\r
9642 \r
9643         explodeInfo.fromX = fromX;\r
9644         explodeInfo.fromY = fromY;\r
9645         explodeInfo.toX = toX;\r
9646         explodeInfo.toY = toY;\r
9647         for(i=1; i<4*kFactor; i++) {\r
9648             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9649             DrawPosition(FALSE, board);\r
9650             Sleep(appData.animSpeed);\r
9651         }\r
9652         explodeInfo.radius = 0;\r
9653         DrawPosition(TRUE, board);\r
9654 }\r
9655 \r
9656 void\r
9657 AnimateMove(board, fromX, fromY, toX, toY)\r
9658      Board board;\r
9659      int fromX;\r
9660      int fromY;\r
9661      int toX;\r
9662      int toY;\r
9663 {\r
9664   ChessSquare piece;\r
9665   POINT start, finish, mid;\r
9666   POINT frames[kFactor * 2 + 1];\r
9667   int nFrames, n;\r
9668 \r
9669   if (!appData.animate) return;\r
9670   if (doingSizing) return;\r
9671   if (fromY < 0 || fromX < 0) return;\r
9672   piece = board[fromY][fromX];\r
9673   if (piece >= EmptySquare) return;\r
9674 \r
9675   ScreenSquare(fromX, fromY, &start);\r
9676   ScreenSquare(toX, toY, &finish);\r
9677 \r
9678   /* All moves except knight jumps move in straight line */\r
9679   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9680     mid.x = start.x + (finish.x - start.x) / 2;\r
9681     mid.y = start.y + (finish.y - start.y) / 2;\r
9682   } else {\r
9683     /* Knight: make straight movement then diagonal */\r
9684     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9685        mid.x = start.x + (finish.x - start.x) / 2;\r
9686        mid.y = start.y;\r
9687      } else {\r
9688        mid.x = start.x;\r
9689        mid.y = start.y + (finish.y - start.y) / 2;\r
9690      }\r
9691   }\r
9692   \r
9693   /* Don't use as many frames for very short moves */\r
9694   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9695     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9696   else\r
9697     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9698 \r
9699   animInfo.from.x = fromX;\r
9700   animInfo.from.y = fromY;\r
9701   animInfo.to.x = toX;\r
9702   animInfo.to.y = toY;\r
9703   animInfo.lastpos = start;\r
9704   animInfo.piece = piece;\r
9705   for (n = 0; n < nFrames; n++) {\r
9706     animInfo.pos = frames[n];\r
9707     DrawPosition(FALSE, NULL);\r
9708     animInfo.lastpos = animInfo.pos;\r
9709     Sleep(appData.animSpeed);\r
9710   }\r
9711   animInfo.pos = finish;\r
9712   DrawPosition(FALSE, NULL);\r
9713   animInfo.piece = EmptySquare;\r
9714   Explode(board, fromX, fromY, toX, toY);\r
9715 }\r
9716 \r
9717 /*      Convert board position to corner of screen rect and color       */\r
9718 \r
9719 static void\r
9720 ScreenSquare(column, row, pt)\r
9721      int column; int row; POINT * pt;\r
9722 {\r
9723   if (flipView) {\r
9724     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9725     pt->y = lineGap + row * (squareSize + lineGap);\r
9726   } else {\r
9727     pt->x = lineGap + column * (squareSize + lineGap);\r
9728     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9729   }\r
9730 }\r
9731 \r
9732 /*      Generate a series of frame coords from start->mid->finish.\r
9733         The movement rate doubles until the half way point is\r
9734         reached, then halves back down to the final destination,\r
9735         which gives a nice slow in/out effect. The algorithmn\r
9736         may seem to generate too many intermediates for short\r
9737         moves, but remember that the purpose is to attract the\r
9738         viewers attention to the piece about to be moved and\r
9739         then to where it ends up. Too few frames would be less\r
9740         noticeable.                                             */\r
9741 \r
9742 static void\r
9743 Tween(start, mid, finish, factor, frames, nFrames)\r
9744      POINT * start; POINT * mid;\r
9745      POINT * finish; int factor;\r
9746      POINT frames[]; int * nFrames;\r
9747 {\r
9748   int n, fraction = 1, count = 0;\r
9749 \r
9750   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9751   for (n = 0; n < factor; n++)\r
9752     fraction *= 2;\r
9753   for (n = 0; n < factor; n++) {\r
9754     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9755     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9756     count ++;\r
9757     fraction = fraction / 2;\r
9758   }\r
9759   \r
9760   /* Midpoint */\r
9761   frames[count] = *mid;\r
9762   count ++;\r
9763   \r
9764   /* Slow out, stepping 1/2, then 1/4, ... */\r
9765   fraction = 2;\r
9766   for (n = 0; n < factor; n++) {\r
9767     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9768     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9769     count ++;\r
9770     fraction = fraction * 2;\r
9771   }\r
9772   *nFrames = count;\r
9773 }\r
9774 \r
9775 void\r
9776 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9777 {\r
9778     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9779 \r
9780     EvalGraphSet( first, last, current, pvInfoList );\r
9781 }\r
9782 \r
9783 void\r
9784 SettingsPopUp(ChessProgramState *cps)\r
9785 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9786       EngineOptionsPopup(savedHwnd, cps);\r
9787 }\r