Make starting new variation dependent on shift key
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 \r
77 #if __GNUC__\r
78 #include <errno.h>\r
79 #include <string.h>\r
80 #endif\r
81 \r
82 #include "common.h"\r
83 #include "frontend.h"\r
84 #include "backend.h"\r
85 #include "winboard.h"\r
86 #include "moves.h"\r
87 #include "wclipbrd.h"\r
88 #include "woptions.h"\r
89 #include "wsockerr.h"\r
90 #include "defaults.h"\r
91 #include "help.h"\r
92 #include "wsnap.h"\r
93 \r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
95 \r
96   int myrandom(void);\r
97   void mysrandom(unsigned int seed);\r
98 \r
99 extern int whiteFlag, blackFlag;\r
100 Boolean flipClock = FALSE;\r
101 extern HANDLE chatHandle[];\r
102 extern int ics_type;\r
103 \r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
105 VOID NewVariantPopup(HWND hwnd);\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
107                    /*char*/int promoChar));\r
108 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
109 void DisplayMove P((int moveNumber));\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
111 void ChatPopUp P((char *s));\r
112 typedef struct {\r
113   ChessSquare piece;  \r
114   POINT pos;      /* window coordinates of current pos */\r
115   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
116   POINT from;     /* board coordinates of the piece's orig pos */\r
117   POINT to;       /* board coordinates of the piece's new pos */\r
118 } AnimInfo;\r
119 \r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
121 \r
122 typedef struct {\r
123   POINT start;    /* window coordinates of start pos */\r
124   POINT pos;      /* window coordinates of current pos */\r
125   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
126   POINT from;     /* board coordinates of the piece's orig pos */\r
127 } DragInfo;\r
128 \r
129 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\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 HWND hCommPort = NULL;    /* currently open comm port */\r
191 static HWND hwndPause;    /* pause button */\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,\r
194   blackSquareBrush, /* [HGM] for band between board and holdings */\r
195   explodeBrush,     /* [HGM] atomic */\r
196   markerBrush,      /* [HGM] markers */\r
197   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
198 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
199 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
200 static HPEN gridPen = NULL;\r
201 static HPEN highlightPen = NULL;\r
202 static HPEN premovePen = NULL;\r
203 static NPLOGPALETTE pLogPal;\r
204 static BOOL paletteChanged = FALSE;\r
205 static HICON iconWhite, iconBlack, iconCurrent;\r
206 static int doingSizing = FALSE;\r
207 static int lastSizing = 0;\r
208 static int prevStderrPort;\r
209 static HBITMAP userLogo;\r
210 \r
211 static HBITMAP liteBackTexture = NULL;\r
212 static HBITMAP darkBackTexture = NULL;\r
213 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
214 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
215 static int backTextureSquareSize = 0;\r
216 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
217 \r
218 #if __GNUC__ && !defined(_winmajor)\r
219 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
220 #else\r
221 #if defined(_winmajor)\r
222 #define oldDialog (_winmajor < 4)\r
223 #else\r
224 #define oldDialog 0\r
225 #endif\r
226 #endif\r
227 \r
228 #define INTERNATIONAL\r
229 \r
230 #ifdef INTERNATIONAL\r
231 #  define _(s) T_(s)\r
232 #  define N_(s) s\r
233 #else\r
234 #  define _(s) s\r
235 #  define N_(s) s\r
236 #  define T_(s) s\r
237 #  define Translate(x, y)\r
238 #  define LoadLanguageFile(s)\r
239 #endif\r
240 \r
241 #ifdef INTERNATIONAL\r
242 \r
243 Boolean barbaric; // flag indicating if translation is needed\r
244 \r
245 // list of item numbers used in each dialog (used to alter language at run time)\r
246 \r
247 #define ABOUTBOX -1  /* not sure why these are needed */\r
248 #define ABOUTBOX2 -1\r
249 \r
250 int dialogItems[][40] = {\r
251 { ABOUTBOX, IDOK, 400 }, \r
252 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
253   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
254 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL }, \r
255 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
256   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
257 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
258 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
259   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
260 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
261 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
262   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
263 { ABOUTBOX2, IDC_ChessBoard }, \r
264 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
265   OPT_GameListClose, IDC_GameListDoFilter }, \r
266 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
267 { DLG_Error, IDOK }, \r
268 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
269   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
270 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
271 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
272   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
273   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
274 { DLG_IndexNumber, IDC_Index }, \r
275 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
276 { DLG_TypeInName, IDOK, IDCANCEL }, \r
277 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
278   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
279 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
280   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
281   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
282   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
283   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
284   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
285   OPT_HighlightMoveArrow, OPT_AutoLogo }, \r
286 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
287   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
288   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
289   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
290   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
291   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
292   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
293   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
294   GPB_General, GPB_Alarm }, \r
295 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
296   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
297   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
298   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
299   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
300   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
301   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
302   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size }, \r
303 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
304   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
305   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
306   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
307   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
308   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
309   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat,\r
310   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
311   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
312 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
313   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
314   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,\r
315   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
316 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
317 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
318   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
319 { DLG_MoveHistory }, \r
320 { DLG_EvalGraph }, \r
321 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
322 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
323 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
324   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
325   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
326   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
327 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
328   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
329   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
330 { 0 }\r
331 };\r
332 \r
333 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
334 static int lastChecked;\r
335 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
336 extern int tinyLayout;\r
337 extern char * menuBarText[][8];\r
338 \r
339 void\r
340 LoadLanguageFile(char *name)\r
341 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
342     FILE *f;\r
343     int i=0, j=0, n=0, k;\r
344     char buf[MSG_SIZ];\r
345 \r
346     if(!name || name[0] == NULLCHAR) return;\r
347       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
348     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
349     if((f = fopen(buf, "r")) == NULL) return;\r
350     while((k = fgetc(f)) != EOF) {\r
351         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
352         languageBuf[i] = k;\r
353         if(k == '\n') {\r
354             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
355                 char *p;\r
356                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
357                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
358                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
359                         english[j] = languageBuf + n + 1; *p = 0;\r
360                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
361 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
362                     }\r
363                 }\r
364             }\r
365             n = i + 1;\r
366         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
367             switch(k) {\r
368               case 'n': k = '\n'; break;\r
369               case 'r': k = '\r'; break;\r
370               case 't': k = '\t'; break;\r
371             }\r
372             languageBuf[--i] = k;\r
373         }\r
374         i++;\r
375     }\r
376     fclose(f);\r
377     barbaric = (j != 0);\r
378     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
379 }\r
380 \r
381 char *\r
382 T_(char *s)\r
383 {   // return the translation of the given string\r
384     // efficiency can be improved a lot...\r
385     int i=0;\r
386 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
387     if(!barbaric) return s;\r
388     if(!s) return ""; // sanity\r
389     while(english[i]) {\r
390         if(!strcmp(s, english[i])) return foreign[i];\r
391         i++;\r
392     }\r
393     return s;\r
394 }\r
395 \r
396 void\r
397 Translate(HWND hDlg, int dialogID)\r
398 {   // translate all text items in the given dialog\r
399     int i=0, j, k;\r
400     char buf[MSG_SIZ], *s;\r
401 //if(appData.debugMode) fprintf(debugFP, "Translate(%d)\n", dialogID);\r
402     if(!barbaric) return;\r
403     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
404     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
405     GetWindowText( hDlg, buf, MSG_SIZ );\r
406     s = T_(buf);\r
407 //if(appData.debugMode) fprintf(debugFP, "WindowText '%s' -> '%s'\n", buf, s);\r
408     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
409     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
410         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
411         if(strlen(buf) == 0) continue;\r
412         s = T_(buf);\r
413         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
414     }\r
415 }\r
416 \r
417 void\r
418 TranslateMenus(int addLanguage)\r
419 {\r
420     int i, j;\r
421     WIN32_FIND_DATA fileData;\r
422     HANDLE hFind;\r
423 #define IDM_English 1895\r
424     if(1) {\r
425         HMENU mainMenu = GetMenu(hwndMain);\r
426         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
427           HMENU subMenu = GetSubMenu(mainMenu, i);\r
428           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
429                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
430           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
431             char buf[MSG_SIZ];\r
432             UINT k = GetMenuItemID(subMenu, j);\r
433               if(menuText[i][j])
434                 safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) ); else {\r
435                 GetMenuString(subMenu, j, buf, MSG_SIZ, MF_BYPOSITION);\r
436                 menuText[i][j] = strdup(buf); // remember original on first change\r
437             }\r
438             if(buf[0] == NULLCHAR) continue;\r
439 //fprintf(debugFP, "menu(%d,%d) = %s (%08x, %08x) %d\n", i, j, buf, mainMenu, subMenu, k);\r
440             ModifyMenu(subMenu, j, MF_STRING|MF_BYPOSITION\r
441                                    |CheckMenuItem(subMenu, j, MF_BYPOSITION)\r
442                                    |EnableMenuItem(subMenu, j, MF_BYPOSITION), k, T_(buf));\r
443           }\r
444         }\r
445         DrawMenuBar(hwndMain);\r
446     }\r
447 \r
448     if(!addLanguage) return;\r
449     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
450         HMENU mainMenu = GetMenu(hwndMain);\r
451         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
452         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
453         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
454         i = 0; lastChecked = IDM_English;\r
455         do {\r
456             char *p, *q = fileData.cFileName;\r
457             int checkFlag = MF_UNCHECKED;\r
458             languageFile[i] = strdup(q);\r
459             if(barbaric && !strcmp(oldLanguage, q)) {\r
460                 checkFlag = MF_CHECKED;\r
461                 lastChecked = IDM_English + i + 1;\r
462                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
463             }\r
464             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
465             p = strstr(fileData.cFileName, ".lng");\r
466             if(p) *p = 0;\r
467             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
468         } while(FindNextFile(hFind, &fileData));\r
469         FindClose(hFind);\r
470     }\r
471 }\r
472 \r
473 #endif\r
474 \r
475 typedef struct {\r
476   char *name;\r
477   int squareSize;\r
478   int lineGap;\r
479   int smallLayout;\r
480   int tinyLayout;\r
481   int cliWidth, cliHeight;\r
482 } SizeInfo;\r
483 \r
484 SizeInfo sizeInfo[] = \r
485 {\r
486   { "tiny",     21, 0, 1, 1, 0, 0 },\r
487   { "teeny",    25, 1, 1, 1, 0, 0 },\r
488   { "dinky",    29, 1, 1, 1, 0, 0 },\r
489   { "petite",   33, 1, 1, 1, 0, 0 },\r
490   { "slim",     37, 2, 1, 0, 0, 0 },\r
491   { "small",    40, 2, 1, 0, 0, 0 },\r
492   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
493   { "middling", 49, 2, 0, 0, 0, 0 },\r
494   { "average",  54, 2, 0, 0, 0, 0 },\r
495   { "moderate", 58, 3, 0, 0, 0, 0 },\r
496   { "medium",   64, 3, 0, 0, 0, 0 },\r
497   { "bulky",    72, 3, 0, 0, 0, 0 },\r
498   { "large",    80, 3, 0, 0, 0, 0 },\r
499   { "big",      87, 3, 0, 0, 0, 0 },\r
500   { "huge",     95, 3, 0, 0, 0, 0 },\r
501   { "giant",    108, 3, 0, 0, 0, 0 },\r
502   { "colossal", 116, 4, 0, 0, 0, 0 },\r
503   { "titanic",  129, 4, 0, 0, 0, 0 },\r
504   { NULL, 0, 0, 0, 0, 0, 0 }\r
505 };\r
506 \r
507 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
508 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
509 {\r
510   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },\r
511   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },\r
512   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },\r
513   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },\r
514   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },\r
515   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },\r
516   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },\r
517   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },\r
518   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },\r
519   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },\r
520   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },\r
521   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },\r
522   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },\r
523   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },\r
524   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },\r
525   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },\r
526   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },\r
527   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },\r
528 };\r
529 \r
530 MyFont *font[NUM_SIZES][NUM_FONTS];\r
531 \r
532 typedef struct {\r
533   char *label;\r
534   int id;\r
535   HWND hwnd;\r
536   WNDPROC wndproc;\r
537 } MyButtonDesc;\r
538 \r
539 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
540 #define N_BUTTONS 5\r
541 \r
542 MyButtonDesc buttonDesc[N_BUTTONS] =\r
543 {\r
544   {"<<", IDM_ToStart, NULL, NULL},\r
545   {"<", IDM_Backward, NULL, NULL},\r
546   {"P", IDM_Pause, NULL, NULL},\r
547   {">", IDM_Forward, NULL, NULL},\r
548   {">>", IDM_ToEnd, NULL, NULL},\r
549 };\r
550 \r
551 int tinyLayout = 0, smallLayout = 0;\r
552 #define MENU_BAR_ITEMS 7\r
553 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
554   { N_("&File"), N_("&Mode"), N_("&Action"), N_("&Step"), N_("&Options"), N_("&Help"), NULL },\r
555   { N_("&F"), N_("&M"), N_("&A"), N_("&S"), N_("&O"), N_("&H"), NULL },\r
556 };\r
557 \r
558 \r
559 MySound sounds[(int)NSoundClasses];\r
560 MyTextAttribs textAttribs[(int)NColorClasses];\r
561 \r
562 MyColorizeAttribs colorizeAttribs[] = {\r
563   { (COLORREF)0, 0, N_("Shout Text") },\r
564   { (COLORREF)0, 0, N_("SShout/CShout") },\r
565   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
566   { (COLORREF)0, 0, N_("Channel Text") },\r
567   { (COLORREF)0, 0, N_("Kibitz Text") },\r
568   { (COLORREF)0, 0, N_("Tell Text") },\r
569   { (COLORREF)0, 0, N_("Challenge Text") },\r
570   { (COLORREF)0, 0, N_("Request Text") },\r
571   { (COLORREF)0, 0, N_("Seek Text") },\r
572   { (COLORREF)0, 0, N_("Normal Text") },\r
573   { (COLORREF)0, 0, N_("None") }\r
574 };\r
575 \r
576 \r
577 \r
578 static char *commentTitle;\r
579 static char *commentText;\r
580 static int commentIndex;\r
581 static Boolean editComment = FALSE;\r
582 \r
583 \r
584 char errorTitle[MSG_SIZ];\r
585 char errorMessage[2*MSG_SIZ];\r
586 HWND errorDialog = NULL;\r
587 BOOLEAN moveErrorMessageUp = FALSE;\r
588 BOOLEAN consoleEcho = TRUE;\r
589 CHARFORMAT consoleCF;\r
590 COLORREF consoleBackgroundColor;\r
591 \r
592 char *programVersion;\r
593 \r
594 #define CPReal 1\r
595 #define CPComm 2\r
596 #define CPSock 3\r
597 #define CPRcmd 4\r
598 typedef int CPKind;\r
599 \r
600 typedef struct {\r
601   CPKind kind;\r
602   HANDLE hProcess;\r
603   DWORD pid;\r
604   HANDLE hTo;\r
605   HANDLE hFrom;\r
606   SOCKET sock;\r
607   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
608 } ChildProc;\r
609 \r
610 #define INPUT_SOURCE_BUF_SIZE 4096\r
611 \r
612 typedef struct _InputSource {\r
613   CPKind kind;\r
614   HANDLE hFile;\r
615   SOCKET sock;\r
616   int lineByLine;\r
617   HANDLE hThread;\r
618   DWORD id;\r
619   char buf[INPUT_SOURCE_BUF_SIZE];\r
620   char *next;\r
621   DWORD count;\r
622   int error;\r
623   InputCallback func;\r
624   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
625   VOIDSTAR closure;\r
626 } InputSource;\r
627 \r
628 InputSource *consoleInputSource;\r
629 \r
630 DCB dcb;\r
631 \r
632 /* forward */\r
633 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
634 VOID ConsoleCreate();\r
635 LRESULT CALLBACK\r
636   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
637 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
638 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
639 VOID ParseCommSettings(char *arg, DCB *dcb);\r
640 LRESULT CALLBACK\r
641   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
642 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
643 void ParseIcsTextMenu(char *icsTextMenuString);\r
644 VOID PopUpMoveDialog(char firstchar);\r
645 VOID PopUpNameDialog(char firstchar);\r
646 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
647 \r
648 /* [AS] */\r
649 int NewGameFRC();\r
650 int GameListOptions();\r
651 \r
652 int dummy; // [HGM] for obsolete args\r
653 \r
654 HWND hwndMain = NULL;        /* root window*/\r
655 HWND hwndConsole = NULL;\r
656 HWND commentDialog = NULL;\r
657 HWND moveHistoryDialog = NULL;\r
658 HWND evalGraphDialog = NULL;\r
659 HWND engineOutputDialog = NULL;\r
660 HWND gameListDialog = NULL;\r
661 HWND editTagsDialog = NULL;\r
662 \r
663 int commentUp = FALSE;\r
664 \r
665 WindowPlacement wpMain;\r
666 WindowPlacement wpConsole;\r
667 WindowPlacement wpComment;\r
668 WindowPlacement wpMoveHistory;\r
669 WindowPlacement wpEvalGraph;\r
670 WindowPlacement wpEngineOutput;\r
671 WindowPlacement wpGameList;\r
672 WindowPlacement wpTags;\r
673 \r
674 VOID EngineOptionsPopup(); // [HGM] settings\r
675 \r
676 VOID GothicPopUp(char *title, VariantClass variant);\r
677 /*\r
678  * Setting "frozen" should disable all user input other than deleting\r
679  * the window.  We do this while engines are initializing themselves.\r
680  */\r
681 static int frozen = 0;\r
682 static int oldMenuItemState[MENU_BAR_ITEMS];\r
683 void FreezeUI()\r
684 {\r
685   HMENU hmenu;\r
686   int i;\r
687 \r
688   if (frozen) return;\r
689   frozen = 1;\r
690   hmenu = GetMenu(hwndMain);\r
691   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
692     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
693   }\r
694   DrawMenuBar(hwndMain);\r
695 }\r
696 \r
697 /* Undo a FreezeUI */\r
698 void ThawUI()\r
699 {\r
700   HMENU hmenu;\r
701   int i;\r
702 \r
703   if (!frozen) return;\r
704   frozen = 0;\r
705   hmenu = GetMenu(hwndMain);\r
706   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
707     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
708   }\r
709   DrawMenuBar(hwndMain);\r
710 }\r
711 \r
712 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
713 \r
714 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
715 #ifdef JAWS\r
716 #include "jaws.c"\r
717 #else\r
718 #define JAWS_INIT\r
719 #define JAWS_ARGS\r
720 #define JAWS_ALT_INTERCEPT\r
721 #define JAWS_KB_NAVIGATION\r
722 #define JAWS_MENU_ITEMS\r
723 #define JAWS_SILENCE\r
724 #define JAWS_REPLAY\r
725 #define JAWS_ACCEL\r
726 #define JAWS_COPYRIGHT\r
727 #define JAWS_DELETE(X) X\r
728 #define SAYMACHINEMOVE()\r
729 #define SAY(X)\r
730 #endif\r
731 \r
732 /*---------------------------------------------------------------------------*\\r
733  *\r
734  * WinMain\r
735  *\r
736 \*---------------------------------------------------------------------------*/\r
737 \r
738 int APIENTRY\r
739 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
740         LPSTR lpCmdLine, int nCmdShow)\r
741 {\r
742   MSG msg;\r
743   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
744 //  INITCOMMONCONTROLSEX ex;\r
745 \r
746   debugFP = stderr;\r
747 \r
748   LoadLibrary("RICHED32.DLL");\r
749   consoleCF.cbSize = sizeof(CHARFORMAT);\r
750 \r
751   if (!InitApplication(hInstance)) {\r
752     return (FALSE);\r
753   }\r
754   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
755     return (FALSE);\r
756   }\r
757 \r
758   JAWS_INIT\r
759 \r
760 //  InitCommonControlsEx(&ex);\r
761   InitCommonControls();\r
762 \r
763   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
764   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
765   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
766 \r
767   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
768 \r
769   while (GetMessage(&msg, /* message structure */\r
770                     NULL, /* handle of window receiving the message */\r
771                     0,    /* lowest message to examine */\r
772                     0))   /* highest message to examine */\r
773     {\r
774 \r
775       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
776         // [HGM] navigate: switch between all windows with tab\r
777         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
778         int i, currentElement = 0;\r
779 \r
780         // first determine what element of the chain we come from (if any)\r
781         if(appData.icsActive) {\r
782             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
783             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
784         }\r
785         if(engineOutputDialog && EngineOutputIsUp()) {\r
786             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
787             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
788         }\r
789         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
790             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
791         }\r
792         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
793         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
794         if(msg.hwnd == e1)                 currentElement = 2; else\r
795         if(msg.hwnd == e2)                 currentElement = 3; else\r
796         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
797         if(msg.hwnd == mh)                currentElement = 4; else\r
798         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
799         if(msg.hwnd == hText)  currentElement = 5; else\r
800         if(msg.hwnd == hInput) currentElement = 6; else\r
801         for (i = 0; i < N_BUTTONS; i++) {\r
802             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
803         }\r
804 \r
805         // determine where to go to\r
806         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
807           do {\r
808             currentElement = (currentElement + direction) % 7;\r
809             switch(currentElement) {\r
810                 case 0:\r
811                   h = hwndMain; break; // passing this case always makes the loop exit\r
812                 case 1:\r
813                   h = buttonDesc[0].hwnd; break; // could be NULL\r
814                 case 2:\r
815                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
816                   h = e1; break;\r
817                 case 3:\r
818                   if(!EngineOutputIsUp()) continue;\r
819                   h = e2; break;\r
820                 case 4:\r
821                   if(!MoveHistoryIsUp()) continue;\r
822                   h = mh; break;\r
823 //              case 6: // input to eval graph does not seem to get here!\r
824 //                if(!EvalGraphIsUp()) continue;\r
825 //                h = evalGraphDialog; break;\r
826                 case 5:\r
827                   if(!appData.icsActive) continue;\r
828                   SAY("display");\r
829                   h = hText; break;\r
830                 case 6:\r
831                   if(!appData.icsActive) continue;\r
832                   SAY("input");\r
833                   h = hInput; break;\r
834             }\r
835           } while(h == 0);\r
836 \r
837           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
838           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
839           SetFocus(h);\r
840 \r
841           continue; // this message now has been processed\r
842         }\r
843       }\r
844 \r
845       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
846           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
847           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
848           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
849           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
850           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
851           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
852           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
853           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
854           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
855         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
856         for(i=0; i<MAX_CHAT; i++) \r
857             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
858                 done = 1; break;\r
859         }\r
860         if(done) continue; // [HGM] chat: end patch\r
861         TranslateMessage(&msg); /* Translates virtual key codes */\r
862         DispatchMessage(&msg);  /* Dispatches message to window */\r
863       }\r
864     }\r
865 \r
866 \r
867   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
868 }\r
869 \r
870 /*---------------------------------------------------------------------------*\\r
871  *\r
872  * Initialization functions\r
873  *\r
874 \*---------------------------------------------------------------------------*/\r
875 \r
876 void\r
877 SetUserLogo()\r
878 {   // update user logo if necessary\r
879     static char oldUserName[MSG_SIZ], *curName;\r
880 \r
881     if(appData.autoLogo) {\r
882           curName = UserName();\r
883           if(strcmp(curName, oldUserName)) {\r
884             snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
885                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
886                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
887           }\r
888     }\r
889 }\r
890 \r
891 BOOL\r
892 InitApplication(HINSTANCE hInstance)\r
893 {\r
894   WNDCLASS wc;\r
895 \r
896   /* Fill in window class structure with parameters that describe the */\r
897   /* main window. */\r
898 \r
899   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
900   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
901   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
902   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
903   wc.hInstance     = hInstance;         /* Owner of this class */\r
904   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
905   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
906   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
907   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
908   wc.lpszClassName = szAppName;                 /* Name to register as */\r
909 \r
910   /* Register the window class and return success/failure code. */\r
911   if (!RegisterClass(&wc)) return FALSE;\r
912 \r
913   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
914   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
915   wc.cbClsExtra    = 0;\r
916   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
917   wc.hInstance     = hInstance;\r
918   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
919   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
920   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
921   wc.lpszMenuName  = NULL;\r
922   wc.lpszClassName = szConsoleName;\r
923 \r
924   if (!RegisterClass(&wc)) return FALSE;\r
925   return TRUE;\r
926 }\r
927 \r
928 \r
929 /* Set by InitInstance, used by EnsureOnScreen */\r
930 int screenHeight, screenWidth;\r
931 \r
932 void\r
933 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
934 {\r
935 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
936   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
937   if (*x > screenWidth - 32) *x = 0;\r
938   if (*y > screenHeight - 32) *y = 0;\r
939   if (*x < minX) *x = minX;\r
940   if (*y < minY) *y = minY;\r
941 }\r
942 \r
943 BOOL\r
944 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
945 {\r
946   HWND hwnd; /* Main window handle. */\r
947   int ibs;\r
948   WINDOWPLACEMENT wp;\r
949   char *filepart;\r
950 \r
951   hInst = hInstance;    /* Store instance handle in our global variable */\r
952   programName = szAppName;\r
953 \r
954   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
955     *filepart = NULLCHAR;\r
956   } else {\r
957     GetCurrentDirectory(MSG_SIZ, installDir);\r
958   }\r
959   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
960   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
961   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
962   /* xboard, and older WinBoards, controlled the move sound with the\r
963      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
964      always turn the option on (so that the backend will call us),\r
965      then let the user turn the sound off by setting it to silence if\r
966      desired.  To accommodate old winboard.ini files saved by old\r
967      versions of WinBoard, we also turn off the sound if the option\r
968      was initially set to false. [HGM] taken out of InitAppData */\r
969   if (!appData.ringBellAfterMoves) {\r
970     sounds[(int)SoundMove].name = strdup("");\r
971     appData.ringBellAfterMoves = TRUE;\r
972   }\r
973   if (appData.debugMode) {\r
974     debugFP = fopen(appData.nameOfDebugFile, "w");\r
975     setbuf(debugFP, NULL);\r
976   }\r
977 \r
978   LoadLanguageFile(appData.language);\r
979 \r
980   InitBackEnd1();\r
981 \r
982 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
983 //  InitEngineUCI( installDir, &second );\r
984 \r
985   /* Create a main window for this application instance. */\r
986   hwnd = CreateWindow(szAppName, szTitle,\r
987                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
988                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
989                       NULL, NULL, hInstance, NULL);\r
990   hwndMain = hwnd;\r
991 \r
992   /* If window could not be created, return "failure" */\r
993   if (!hwnd) {\r
994     return (FALSE);\r
995   }\r
996 \r
997   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
998   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
999       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1000 \r
1001       if (first.programLogo == NULL && appData.debugMode) {\r
1002           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
1003       }\r
1004   } else if(appData.autoLogo) {\r
1005       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
1006         char buf[MSG_SIZ];\r
1007           snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.firstDirectory);\r
1008         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
1009       }\r
1010   }\r
1011 \r
1012   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
1013       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1014 \r
1015       if (second.programLogo == NULL && appData.debugMode) {\r
1016           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
1017       }\r
1018   } else if(appData.autoLogo) {\r
1019       char buf[MSG_SIZ];\r
1020       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
1021         snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);\r
1022         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1023       } else\r
1024       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
1025         snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);\r
1026         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
1027       }\r
1028   }\r
1029 \r
1030   SetUserLogo();\r
1031 \r
1032   iconWhite = LoadIcon(hInstance, "icon_white");\r
1033   iconBlack = LoadIcon(hInstance, "icon_black");\r
1034   iconCurrent = iconWhite;\r
1035   InitDrawingColors();\r
1036   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1037   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1038   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1039     /* Compute window size for each board size, and use the largest\r
1040        size that fits on this screen as the default. */\r
1041     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1042     if (boardSize == (BoardSize)-1 &&\r
1043         winH <= screenHeight\r
1044            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1045         && winW <= screenWidth) {\r
1046       boardSize = (BoardSize)ibs;\r
1047     }\r
1048   }\r
1049 \r
1050   InitDrawingSizes(boardSize, 0);\r
1051   TranslateMenus(1);\r
1052   InitMenuChecks();\r
1053   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1054 \r
1055   /* [AS] Load textures if specified */\r
1056   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1057   \r
1058   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1059       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1060       liteBackTextureMode = appData.liteBackTextureMode;\r
1061 \r
1062       if (liteBackTexture == NULL && appData.debugMode) {\r
1063           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1064       }\r
1065   }\r
1066   \r
1067   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1068       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1069       darkBackTextureMode = appData.darkBackTextureMode;\r
1070 \r
1071       if (darkBackTexture == NULL && appData.debugMode) {\r
1072           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1073       }\r
1074   }\r
1075 \r
1076   mysrandom( (unsigned) time(NULL) );\r
1077 \r
1078   /* [AS] Restore layout */\r
1079   if( wpMoveHistory.visible ) {\r
1080       MoveHistoryPopUp();\r
1081   }\r
1082 \r
1083   if( wpEvalGraph.visible ) {\r
1084       EvalGraphPopUp();\r
1085   }\r
1086 \r
1087   if( wpEngineOutput.visible ) {\r
1088       EngineOutputPopUp();\r
1089   }\r
1090 \r
1091   /* Make the window visible; update its client area; and return "success" */\r
1092   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1093   wp.length = sizeof(WINDOWPLACEMENT);\r
1094   wp.flags = 0;\r
1095   wp.showCmd = nCmdShow;\r
1096   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1097   wp.rcNormalPosition.left = wpMain.x;\r
1098   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1099   wp.rcNormalPosition.top = wpMain.y;\r
1100   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1101   SetWindowPlacement(hwndMain, &wp);\r
1102 \r
1103   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1104 \r
1105   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1106                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1107 \r
1108   if (hwndConsole) {\r
1109 #if AOT_CONSOLE\r
1110     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1111                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1112 #endif\r
1113     ShowWindow(hwndConsole, nCmdShow);\r
1114     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
1115       char buf[MSG_SIZ], *p = buf, *q;\r
1116         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
1117       do {\r
1118         q = strchr(p, ';');\r
1119         if(q) *q++ = 0;\r
1120         if(*p) ChatPopUp(p);\r
1121       } while(p=q);\r
1122     }\r
1123     SetActiveWindow(hwndConsole);\r
1124   }\r
1125   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1126   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1127 \r
1128   return TRUE;\r
1129 \r
1130 }\r
1131 \r
1132 VOID\r
1133 InitMenuChecks()\r
1134 {\r
1135   HMENU hmenu = GetMenu(hwndMain);\r
1136 \r
1137   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1138                         MF_BYCOMMAND|((appData.icsActive &&\r
1139                                        *appData.icsCommPort != NULLCHAR) ?\r
1140                                       MF_ENABLED : MF_GRAYED));\r
1141   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1142                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1143                                      MF_CHECKED : MF_UNCHECKED));\r
1144 }\r
1145 \r
1146 //---------------------------------------------------------------------------------------------------------\r
1147 \r
1148 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1149 #define XBOARD FALSE\r
1150 \r
1151 #define OPTCHAR "/"\r
1152 #define SEPCHAR "="\r
1153 \r
1154 #include "args.h"\r
1155 \r
1156 // front-end part of option handling\r
1157 \r
1158 VOID\r
1159 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1160 {\r
1161   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1162   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1163   DeleteDC(hdc);\r
1164   lf->lfWidth = 0;\r
1165   lf->lfEscapement = 0;\r
1166   lf->lfOrientation = 0;\r
1167   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1168   lf->lfItalic = mfp->italic;\r
1169   lf->lfUnderline = mfp->underline;\r
1170   lf->lfStrikeOut = mfp->strikeout;\r
1171   lf->lfCharSet = mfp->charset;\r
1172   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1173   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1174   lf->lfQuality = DEFAULT_QUALITY;\r
1175   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1176     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1177 }\r
1178 \r
1179 void\r
1180 CreateFontInMF(MyFont *mf)\r
1181\r
1182   LFfromMFP(&mf->lf, &mf->mfp);\r
1183   if (mf->hf) DeleteObject(mf->hf);\r
1184   mf->hf = CreateFontIndirect(&mf->lf);\r
1185 }\r
1186 \r
1187 // [HGM] This platform-dependent table provides the location for storing the color info\r
1188 void *\r
1189 colorVariable[] = {\r
1190   &whitePieceColor, \r
1191   &blackPieceColor, \r
1192   &lightSquareColor,\r
1193   &darkSquareColor, \r
1194   &highlightSquareColor,\r
1195   &premoveHighlightColor,\r
1196   NULL,\r
1197   &consoleBackgroundColor,\r
1198   &appData.fontForeColorWhite,\r
1199   &appData.fontBackColorWhite,\r
1200   &appData.fontForeColorBlack,\r
1201   &appData.fontBackColorBlack,\r
1202   &appData.evalHistColorWhite,\r
1203   &appData.evalHistColorBlack,\r
1204   &appData.highlightArrowColor,\r
1205 };\r
1206 \r
1207 /* Command line font name parser.  NULL name means do nothing.\r
1208    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1209    For backward compatibility, syntax without the colon is also\r
1210    accepted, but font names with digits in them won't work in that case.\r
1211 */\r
1212 VOID\r
1213 ParseFontName(char *name, MyFontParams *mfp)\r
1214 {\r
1215   char *p, *q;\r
1216   if (name == NULL) return;\r
1217   p = name;\r
1218   q = strchr(p, ':');\r
1219   if (q) {\r
1220     if (q - p >= sizeof(mfp->faceName))\r
1221       ExitArgError(_("Font name too long:"), name);\r
1222     memcpy(mfp->faceName, p, q - p);\r
1223     mfp->faceName[q - p] = NULLCHAR;\r
1224     p = q + 1;\r
1225   } else {\r
1226     q = mfp->faceName;\r
1227     while (*p && !isdigit(*p)) {\r
1228       *q++ = *p++;\r
1229       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1230         ExitArgError(_("Font name too long:"), name);\r
1231     }\r
1232     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1233     *q = NULLCHAR;\r
1234   }\r
1235   if (!*p) ExitArgError(_("Font point size missing:"), name);\r
1236   mfp->pointSize = (float) atof(p);\r
1237   mfp->bold = (strchr(p, 'b') != NULL);\r
1238   mfp->italic = (strchr(p, 'i') != NULL);\r
1239   mfp->underline = (strchr(p, 'u') != NULL);\r
1240   mfp->strikeout = (strchr(p, 's') != NULL);\r
1241   mfp->charset = DEFAULT_CHARSET;\r
1242   q = strchr(p, 'c');\r
1243   if (q)\r
1244     mfp->charset = (BYTE) atoi(q+1);\r
1245 }\r
1246 \r
1247 void\r
1248 ParseFont(char *name, int number)\r
1249 { // wrapper to shield back-end from 'font'\r
1250   ParseFontName(name, &font[boardSize][number]->mfp);\r
1251 }\r
1252 \r
1253 void\r
1254 SetFontDefaults()\r
1255 { // in WB  we have a 2D array of fonts; this initializes their description\r
1256   int i, j;\r
1257   /* Point font array elements to structures and\r
1258      parse default font names */\r
1259   for (i=0; i<NUM_FONTS; i++) {\r
1260     for (j=0; j<NUM_SIZES; j++) {\r
1261       font[j][i] = &fontRec[j][i];\r
1262       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1263     }\r
1264   }\r
1265 }\r
1266 \r
1267 void\r
1268 CreateFonts()\r
1269 { // here we create the actual fonts from the selected descriptions\r
1270   int i, j;\r
1271   for (i=0; i<NUM_FONTS; i++) {\r
1272     for (j=0; j<NUM_SIZES; j++) {\r
1273       CreateFontInMF(font[j][i]);\r
1274     }\r
1275   }\r
1276 }\r
1277 /* Color name parser.\r
1278    X version accepts X color names, but this one\r
1279    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1280 COLORREF\r
1281 ParseColorName(char *name)\r
1282 {\r
1283   int red, green, blue, count;\r
1284   char buf[MSG_SIZ];\r
1285 \r
1286   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1287   if (count != 3) {\r
1288     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1289       &red, &green, &blue);\r
1290   }\r
1291   if (count != 3) {\r
1292     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1293     DisplayError(buf, 0);\r
1294     return RGB(0, 0, 0);\r
1295   }\r
1296   return PALETTERGB(red, green, blue);\r
1297 }\r
1298 \r
1299 void\r
1300 ParseColor(int n, char *name)\r
1301 { // for WinBoard the color is an int, which needs to be derived from the string\r
1302   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1303 }\r
1304 \r
1305 void\r
1306 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1307 {\r
1308   char *e = argValue;\r
1309   int eff = 0;\r
1310 \r
1311   while (*e) {\r
1312     if (*e == 'b')      eff |= CFE_BOLD;\r
1313     else if (*e == 'i') eff |= CFE_ITALIC;\r
1314     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1315     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1316     else if (*e == '#' || isdigit(*e)) break;\r
1317     e++;\r
1318   }\r
1319   *effects = eff;\r
1320   *color   = ParseColorName(e);\r
1321 }\r
1322 \r
1323 void\r
1324 ParseTextAttribs(ColorClass cc, char *s)\r
1325 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1326     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1327     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1328 }\r
1329 \r
1330 void\r
1331 ParseBoardSize(void *addr, char *name)\r
1332 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1333   BoardSize bs = SizeTiny;\r
1334   while (sizeInfo[bs].name != NULL) {\r
1335     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1336         *(BoardSize *)addr = bs;\r
1337         return;\r
1338     }\r
1339     bs++;\r
1340   }\r
1341   ExitArgError(_("Unrecognized board size value"), name);\r
1342 }\r
1343 \r
1344 void\r
1345 LoadAllSounds()\r
1346 { // [HGM] import name from appData first\r
1347   ColorClass cc;\r
1348   SoundClass sc;\r
1349   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1350     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1351     textAttribs[cc].sound.data = NULL;\r
1352     MyLoadSound(&textAttribs[cc].sound);\r
1353   }\r
1354   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1355     textAttribs[cc].sound.name = strdup("");\r
1356     textAttribs[cc].sound.data = NULL;\r
1357   }\r
1358   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1359     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1360     sounds[sc].data = NULL;\r
1361     MyLoadSound(&sounds[sc]);\r
1362   }\r
1363 }\r
1364 \r
1365 void\r
1366 SetCommPortDefaults()\r
1367 {\r
1368    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1369   dcb.DCBlength = sizeof(DCB);\r
1370   dcb.BaudRate = 9600;\r
1371   dcb.fBinary = TRUE;\r
1372   dcb.fParity = FALSE;\r
1373   dcb.fOutxCtsFlow = FALSE;\r
1374   dcb.fOutxDsrFlow = FALSE;\r
1375   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1376   dcb.fDsrSensitivity = FALSE;\r
1377   dcb.fTXContinueOnXoff = TRUE;\r
1378   dcb.fOutX = FALSE;\r
1379   dcb.fInX = FALSE;\r
1380   dcb.fNull = FALSE;\r
1381   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1382   dcb.fAbortOnError = FALSE;\r
1383   dcb.ByteSize = 7;\r
1384   dcb.Parity = SPACEPARITY;\r
1385   dcb.StopBits = ONESTOPBIT;\r
1386 }\r
1387 \r
1388 // [HGM] args: these three cases taken out to stay in front-end\r
1389 void\r
1390 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1391 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1392         // while the curent board size determines the element. This system should be ported to XBoard.\r
1393         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1394         int bs;\r
1395         for (bs=0; bs<NUM_SIZES; bs++) {\r
1396           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1397           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1398           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1399             ad->argName, mfp->faceName, mfp->pointSize,\r
1400             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1401             mfp->bold ? "b" : "",\r
1402             mfp->italic ? "i" : "",\r
1403             mfp->underline ? "u" : "",\r
1404             mfp->strikeout ? "s" : "",\r
1405             (int)mfp->charset);\r
1406         }\r
1407       }\r
1408 \r
1409 void\r
1410 ExportSounds()\r
1411 { // [HGM] copy the names from the internal WB variables to appData\r
1412   ColorClass cc;\r
1413   SoundClass sc;\r
1414   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1415     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1416   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1417     (&appData.soundMove)[sc] = sounds[sc].name;\r
1418 }\r
1419 \r
1420 void\r
1421 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1422 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1423         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1424         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1425           (ta->effects & CFE_BOLD) ? "b" : "",\r
1426           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1427           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1428           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1429           (ta->effects) ? " " : "",\r
1430           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1431       }\r
1432 \r
1433 void\r
1434 SaveColor(FILE *f, ArgDescriptor *ad)\r
1435 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1436         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1437         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1438           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1439 }\r
1440 \r
1441 void\r
1442 SaveBoardSize(FILE *f, char *name, void *addr)\r
1443 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1444   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1445 }\r
1446 \r
1447 void\r
1448 ParseCommPortSettings(char *s)\r
1449 { // wrapper to keep dcb from back-end\r
1450   ParseCommSettings(s, &dcb);\r
1451 }\r
1452 \r
1453 void\r
1454 GetWindowCoords()\r
1455 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1456   GetActualPlacement(hwndMain, &wpMain);\r
1457   GetActualPlacement(hwndConsole, &wpConsole);\r
1458   GetActualPlacement(commentDialog, &wpComment);\r
1459   GetActualPlacement(editTagsDialog, &wpTags);\r
1460   GetActualPlacement(gameListDialog, &wpGameList);\r
1461   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1462   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1463   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1464 }\r
1465 \r
1466 void\r
1467 PrintCommPortSettings(FILE *f, char *name)\r
1468 { // wrapper to shield back-end from DCB\r
1469       PrintCommSettings(f, name, &dcb);\r
1470 }\r
1471 \r
1472 int\r
1473 MySearchPath(char *installDir, char *name, char *fullname)\r
1474 {\r
1475   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1476   if(name[0]== '%') {\r
1477     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1478     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1479       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1480       *strchr(buf, '%') = 0;\r
1481       strcat(fullname, getenv(buf));\r
1482       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1483     }\r
1484     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1485     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1486     return (int) strlen(fullname);\r
1487   }\r
1488   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1489 }\r
1490 \r
1491 int\r
1492 MyGetFullPathName(char *name, char *fullname)\r
1493 {\r
1494   char *dummy;\r
1495   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1496 }\r
1497 \r
1498 int\r
1499 MainWindowUp()\r
1500 { // [HGM] args: allows testing if main window is realized from back-end\r
1501   return hwndMain != NULL;\r
1502 }\r
1503 \r
1504 void\r
1505 PopUpStartupDialog()\r
1506 {\r
1507     FARPROC lpProc;\r
1508     \r
1509     LoadLanguageFile(appData.language);\r
1510     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1511     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1512     FreeProcInstance(lpProc);\r
1513 }\r
1514 \r
1515 /*---------------------------------------------------------------------------*\\r
1516  *\r
1517  * GDI board drawing routines\r
1518  *\r
1519 \*---------------------------------------------------------------------------*/\r
1520 \r
1521 /* [AS] Draw square using background texture */\r
1522 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1523 {\r
1524     XFORM   x;\r
1525 \r
1526     if( mode == 0 ) {\r
1527         return; /* Should never happen! */\r
1528     }\r
1529 \r
1530     SetGraphicsMode( dst, GM_ADVANCED );\r
1531 \r
1532     switch( mode ) {\r
1533     case 1:\r
1534         /* Identity */\r
1535         break;\r
1536     case 2:\r
1537         /* X reflection */\r
1538         x.eM11 = -1.0;\r
1539         x.eM12 = 0;\r
1540         x.eM21 = 0;\r
1541         x.eM22 = 1.0;\r
1542         x.eDx = (FLOAT) dw + dx - 1;\r
1543         x.eDy = 0;\r
1544         dx = 0;\r
1545         SetWorldTransform( dst, &x );\r
1546         break;\r
1547     case 3:\r
1548         /* Y reflection */\r
1549         x.eM11 = 1.0;\r
1550         x.eM12 = 0;\r
1551         x.eM21 = 0;\r
1552         x.eM22 = -1.0;\r
1553         x.eDx = 0;\r
1554         x.eDy = (FLOAT) dh + dy - 1;\r
1555         dy = 0;\r
1556         SetWorldTransform( dst, &x );\r
1557         break;\r
1558     case 4:\r
1559         /* X/Y flip */\r
1560         x.eM11 = 0;\r
1561         x.eM12 = 1.0;\r
1562         x.eM21 = 1.0;\r
1563         x.eM22 = 0;\r
1564         x.eDx = (FLOAT) dx;\r
1565         x.eDy = (FLOAT) dy;\r
1566         dx = 0;\r
1567         dy = 0;\r
1568         SetWorldTransform( dst, &x );\r
1569         break;\r
1570     }\r
1571 \r
1572     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1573 \r
1574     x.eM11 = 1.0;\r
1575     x.eM12 = 0;\r
1576     x.eM21 = 0;\r
1577     x.eM22 = 1.0;\r
1578     x.eDx = 0;\r
1579     x.eDy = 0;\r
1580     SetWorldTransform( dst, &x );\r
1581 \r
1582     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1583 }\r
1584 \r
1585 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1586 enum {\r
1587     PM_WP = (int) WhitePawn, \r
1588     PM_WN = (int) WhiteKnight, \r
1589     PM_WB = (int) WhiteBishop, \r
1590     PM_WR = (int) WhiteRook, \r
1591     PM_WQ = (int) WhiteQueen, \r
1592     PM_WF = (int) WhiteFerz, \r
1593     PM_WW = (int) WhiteWazir, \r
1594     PM_WE = (int) WhiteAlfil, \r
1595     PM_WM = (int) WhiteMan, \r
1596     PM_WO = (int) WhiteCannon, \r
1597     PM_WU = (int) WhiteUnicorn, \r
1598     PM_WH = (int) WhiteNightrider, \r
1599     PM_WA = (int) WhiteAngel, \r
1600     PM_WC = (int) WhiteMarshall, \r
1601     PM_WAB = (int) WhiteCardinal, \r
1602     PM_WD = (int) WhiteDragon, \r
1603     PM_WL = (int) WhiteLance, \r
1604     PM_WS = (int) WhiteCobra, \r
1605     PM_WV = (int) WhiteFalcon, \r
1606     PM_WSG = (int) WhiteSilver, \r
1607     PM_WG = (int) WhiteGrasshopper, \r
1608     PM_WK = (int) WhiteKing,\r
1609     PM_BP = (int) BlackPawn, \r
1610     PM_BN = (int) BlackKnight, \r
1611     PM_BB = (int) BlackBishop, \r
1612     PM_BR = (int) BlackRook, \r
1613     PM_BQ = (int) BlackQueen, \r
1614     PM_BF = (int) BlackFerz, \r
1615     PM_BW = (int) BlackWazir, \r
1616     PM_BE = (int) BlackAlfil, \r
1617     PM_BM = (int) BlackMan,\r
1618     PM_BO = (int) BlackCannon, \r
1619     PM_BU = (int) BlackUnicorn, \r
1620     PM_BH = (int) BlackNightrider, \r
1621     PM_BA = (int) BlackAngel, \r
1622     PM_BC = (int) BlackMarshall, \r
1623     PM_BG = (int) BlackGrasshopper, \r
1624     PM_BAB = (int) BlackCardinal,\r
1625     PM_BD = (int) BlackDragon,\r
1626     PM_BL = (int) BlackLance,\r
1627     PM_BS = (int) BlackCobra,\r
1628     PM_BV = (int) BlackFalcon,\r
1629     PM_BSG = (int) BlackSilver,\r
1630     PM_BK = (int) BlackKing\r
1631 };\r
1632 \r
1633 static HFONT hPieceFont = NULL;\r
1634 static HBITMAP hPieceMask[(int) EmptySquare];\r
1635 static HBITMAP hPieceFace[(int) EmptySquare];\r
1636 static int fontBitmapSquareSize = 0;\r
1637 static char pieceToFontChar[(int) EmptySquare] =\r
1638                               { 'p', 'n', 'b', 'r', 'q', \r
1639                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1640                       'k', 'o', 'm', 'v', 't', 'w', \r
1641                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1642                                                               'l' };\r
1643 \r
1644 extern BOOL SetCharTable( char *table, const char * map );\r
1645 /* [HGM] moved to backend.c */\r
1646 \r
1647 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1648 {\r
1649     HBRUSH hbrush;\r
1650     BYTE r1 = GetRValue( color );\r
1651     BYTE g1 = GetGValue( color );\r
1652     BYTE b1 = GetBValue( color );\r
1653     BYTE r2 = r1 / 2;\r
1654     BYTE g2 = g1 / 2;\r
1655     BYTE b2 = b1 / 2;\r
1656     RECT rc;\r
1657 \r
1658     /* Create a uniform background first */\r
1659     hbrush = CreateSolidBrush( color );\r
1660     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1661     FillRect( hdc, &rc, hbrush );\r
1662     DeleteObject( hbrush );\r
1663     \r
1664     if( mode == 1 ) {\r
1665         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1666         int steps = squareSize / 2;\r
1667         int i;\r
1668 \r
1669         for( i=0; i<steps; i++ ) {\r
1670             BYTE r = r1 - (r1-r2) * i / steps;\r
1671             BYTE g = g1 - (g1-g2) * i / steps;\r
1672             BYTE b = b1 - (b1-b2) * i / steps;\r
1673 \r
1674             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1675             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1676             FillRect( hdc, &rc, hbrush );\r
1677             DeleteObject(hbrush);\r
1678         }\r
1679     }\r
1680     else if( mode == 2 ) {\r
1681         /* Diagonal gradient, good more or less for every piece */\r
1682         POINT triangle[3];\r
1683         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1684         HBRUSH hbrush_old;\r
1685         int steps = squareSize;\r
1686         int i;\r
1687 \r
1688         triangle[0].x = squareSize - steps;\r
1689         triangle[0].y = squareSize;\r
1690         triangle[1].x = squareSize;\r
1691         triangle[1].y = squareSize;\r
1692         triangle[2].x = squareSize;\r
1693         triangle[2].y = squareSize - steps;\r
1694 \r
1695         for( i=0; i<steps; i++ ) {\r
1696             BYTE r = r1 - (r1-r2) * i / steps;\r
1697             BYTE g = g1 - (g1-g2) * i / steps;\r
1698             BYTE b = b1 - (b1-b2) * i / steps;\r
1699 \r
1700             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1701             hbrush_old = SelectObject( hdc, hbrush );\r
1702             Polygon( hdc, triangle, 3 );\r
1703             SelectObject( hdc, hbrush_old );\r
1704             DeleteObject(hbrush);\r
1705             triangle[0].x++;\r
1706             triangle[2].y++;\r
1707         }\r
1708 \r
1709         SelectObject( hdc, hpen );\r
1710     }\r
1711 }\r
1712 \r
1713 /*\r
1714     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1715     seems to work ok. The main problem here is to find the "inside" of a chess\r
1716     piece: follow the steps as explained below.\r
1717 */\r
1718 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1719 {\r
1720     HBITMAP hbm;\r
1721     HBITMAP hbm_old;\r
1722     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1723     RECT rc;\r
1724     SIZE sz;\r
1725     POINT pt;\r
1726     int backColor = whitePieceColor; \r
1727     int foreColor = blackPieceColor;\r
1728     \r
1729     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1730         backColor = appData.fontBackColorWhite;\r
1731         foreColor = appData.fontForeColorWhite;\r
1732     }\r
1733     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1734         backColor = appData.fontBackColorBlack;\r
1735         foreColor = appData.fontForeColorBlack;\r
1736     }\r
1737 \r
1738     /* Mask */\r
1739     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1740 \r
1741     hbm_old = SelectObject( hdc, hbm );\r
1742 \r
1743     rc.left = 0;\r
1744     rc.top = 0;\r
1745     rc.right = squareSize;\r
1746     rc.bottom = squareSize;\r
1747 \r
1748     /* Step 1: background is now black */\r
1749     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1750 \r
1751     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1752 \r
1753     pt.x = (squareSize - sz.cx) / 2;\r
1754     pt.y = (squareSize - sz.cy) / 2;\r
1755 \r
1756     SetBkMode( hdc, TRANSPARENT );\r
1757     SetTextColor( hdc, chroma );\r
1758     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1759     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1760 \r
1761     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1762     /* Step 3: the area outside the piece is filled with white */\r
1763 //    FloodFill( hdc, 0, 0, chroma );\r
1764     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1765     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1766     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1767     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1768     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1769     /* \r
1770         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1771         but if the start point is not inside the piece we're lost!\r
1772         There should be a better way to do this... if we could create a region or path\r
1773         from the fill operation we would be fine for example.\r
1774     */\r
1775 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1776     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1777 \r
1778     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1779         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1780         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1781 \r
1782         SelectObject( dc2, bm2 );\r
1783         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1784         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1785         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1786         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1787         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1788 \r
1789         DeleteDC( dc2 );\r
1790         DeleteObject( bm2 );\r
1791     }\r
1792 \r
1793     SetTextColor( hdc, 0 );\r
1794     /* \r
1795         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1796         draw the piece again in black for safety.\r
1797     */\r
1798     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1799 \r
1800     SelectObject( hdc, hbm_old );\r
1801 \r
1802     if( hPieceMask[index] != NULL ) {\r
1803         DeleteObject( hPieceMask[index] );\r
1804     }\r
1805 \r
1806     hPieceMask[index] = hbm;\r
1807 \r
1808     /* Face */\r
1809     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1810 \r
1811     SelectObject( hdc, hbm );\r
1812 \r
1813     {\r
1814         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1815         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1816         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1817 \r
1818         SelectObject( dc1, hPieceMask[index] );\r
1819         SelectObject( dc2, bm2 );\r
1820         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1821         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1822         \r
1823         /* \r
1824             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1825             the piece background and deletes (makes transparent) the rest.\r
1826             Thanks to that mask, we are free to paint the background with the greates\r
1827             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1828             We use this, to make gradients and give the pieces a "roundish" look.\r
1829         */\r
1830         SetPieceBackground( hdc, backColor, 2 );\r
1831         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1832 \r
1833         DeleteDC( dc2 );\r
1834         DeleteDC( dc1 );\r
1835         DeleteObject( bm2 );\r
1836     }\r
1837 \r
1838     SetTextColor( hdc, foreColor );\r
1839     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1840 \r
1841     SelectObject( hdc, hbm_old );\r
1842 \r
1843     if( hPieceFace[index] != NULL ) {\r
1844         DeleteObject( hPieceFace[index] );\r
1845     }\r
1846 \r
1847     hPieceFace[index] = hbm;\r
1848 }\r
1849 \r
1850 static int TranslatePieceToFontPiece( int piece )\r
1851 {\r
1852     switch( piece ) {\r
1853     case BlackPawn:\r
1854         return PM_BP;\r
1855     case BlackKnight:\r
1856         return PM_BN;\r
1857     case BlackBishop:\r
1858         return PM_BB;\r
1859     case BlackRook:\r
1860         return PM_BR;\r
1861     case BlackQueen:\r
1862         return PM_BQ;\r
1863     case BlackKing:\r
1864         return PM_BK;\r
1865     case WhitePawn:\r
1866         return PM_WP;\r
1867     case WhiteKnight:\r
1868         return PM_WN;\r
1869     case WhiteBishop:\r
1870         return PM_WB;\r
1871     case WhiteRook:\r
1872         return PM_WR;\r
1873     case WhiteQueen:\r
1874         return PM_WQ;\r
1875     case WhiteKing:\r
1876         return PM_WK;\r
1877 \r
1878     case BlackAngel:\r
1879         return PM_BA;\r
1880     case BlackMarshall:\r
1881         return PM_BC;\r
1882     case BlackFerz:\r
1883         return PM_BF;\r
1884     case BlackNightrider:\r
1885         return PM_BH;\r
1886     case BlackAlfil:\r
1887         return PM_BE;\r
1888     case BlackWazir:\r
1889         return PM_BW;\r
1890     case BlackUnicorn:\r
1891         return PM_BU;\r
1892     case BlackCannon:\r
1893         return PM_BO;\r
1894     case BlackGrasshopper:\r
1895         return PM_BG;\r
1896     case BlackMan:\r
1897         return PM_BM;\r
1898     case BlackSilver:\r
1899         return PM_BSG;\r
1900     case BlackLance:\r
1901         return PM_BL;\r
1902     case BlackFalcon:\r
1903         return PM_BV;\r
1904     case BlackCobra:\r
1905         return PM_BS;\r
1906     case BlackCardinal:\r
1907         return PM_BAB;\r
1908     case BlackDragon:\r
1909         return PM_BD;\r
1910 \r
1911     case WhiteAngel:\r
1912         return PM_WA;\r
1913     case WhiteMarshall:\r
1914         return PM_WC;\r
1915     case WhiteFerz:\r
1916         return PM_WF;\r
1917     case WhiteNightrider:\r
1918         return PM_WH;\r
1919     case WhiteAlfil:\r
1920         return PM_WE;\r
1921     case WhiteWazir:\r
1922         return PM_WW;\r
1923     case WhiteUnicorn:\r
1924         return PM_WU;\r
1925     case WhiteCannon:\r
1926         return PM_WO;\r
1927     case WhiteGrasshopper:\r
1928         return PM_WG;\r
1929     case WhiteMan:\r
1930         return PM_WM;\r
1931     case WhiteSilver:\r
1932         return PM_WSG;\r
1933     case WhiteLance:\r
1934         return PM_WL;\r
1935     case WhiteFalcon:\r
1936         return PM_WV;\r
1937     case WhiteCobra:\r
1938         return PM_WS;\r
1939     case WhiteCardinal:\r
1940         return PM_WAB;\r
1941     case WhiteDragon:\r
1942         return PM_WD;\r
1943     }\r
1944 \r
1945     return 0;\r
1946 }\r
1947 \r
1948 void CreatePiecesFromFont()\r
1949 {\r
1950     LOGFONT lf;\r
1951     HDC hdc_window = NULL;\r
1952     HDC hdc = NULL;\r
1953     HFONT hfont_old;\r
1954     int fontHeight;\r
1955     int i;\r
1956 \r
1957     if( fontBitmapSquareSize < 0 ) {\r
1958         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1959         return;\r
1960     }\r
1961 \r
1962     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1963         fontBitmapSquareSize = -1;\r
1964         return;\r
1965     }\r
1966 \r
1967     if( fontBitmapSquareSize != squareSize ) {\r
1968         hdc_window = GetDC( hwndMain );\r
1969         hdc = CreateCompatibleDC( hdc_window );\r
1970 \r
1971         if( hPieceFont != NULL ) {\r
1972             DeleteObject( hPieceFont );\r
1973         }\r
1974         else {\r
1975             for( i=0; i<=(int)BlackKing; i++ ) {\r
1976                 hPieceMask[i] = NULL;\r
1977                 hPieceFace[i] = NULL;\r
1978             }\r
1979         }\r
1980 \r
1981         fontHeight = 75;\r
1982 \r
1983         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
1984             fontHeight = appData.fontPieceSize;\r
1985         }\r
1986 \r
1987         fontHeight = (fontHeight * squareSize) / 100;\r
1988 \r
1989         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
1990         lf.lfWidth = 0;\r
1991         lf.lfEscapement = 0;\r
1992         lf.lfOrientation = 0;\r
1993         lf.lfWeight = FW_NORMAL;\r
1994         lf.lfItalic = 0;\r
1995         lf.lfUnderline = 0;\r
1996         lf.lfStrikeOut = 0;\r
1997         lf.lfCharSet = DEFAULT_CHARSET;\r
1998         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1999         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2000         lf.lfQuality = PROOF_QUALITY;\r
2001         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2002         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2003         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2004 \r
2005         hPieceFont = CreateFontIndirect( &lf );\r
2006 \r
2007         if( hPieceFont == NULL ) {\r
2008             fontBitmapSquareSize = -2;\r
2009         }\r
2010         else {\r
2011             /* Setup font-to-piece character table */\r
2012             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2013                 /* No (or wrong) global settings, try to detect the font */\r
2014                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2015                     /* Alpha */\r
2016                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2017                 }\r
2018                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2019                     /* DiagramTT* family */\r
2020                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2021                 }\r
2022                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2023                     /* Fairy symbols */\r
2024                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2025                 }\r
2026                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2027                     /* Good Companion (Some characters get warped as literal :-( */\r
2028                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2029                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2030                     SetCharTable(pieceToFontChar, s);\r
2031                 }\r
2032                 else {\r
2033                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2034                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2035                 }\r
2036             }\r
2037 \r
2038             /* Create bitmaps */\r
2039             hfont_old = SelectObject( hdc, hPieceFont );\r
2040             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2041                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2042                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2043 \r
2044             SelectObject( hdc, hfont_old );\r
2045 \r
2046             fontBitmapSquareSize = squareSize;\r
2047         }\r
2048     }\r
2049 \r
2050     if( hdc != NULL ) {\r
2051         DeleteDC( hdc );\r
2052     }\r
2053 \r
2054     if( hdc_window != NULL ) {\r
2055         ReleaseDC( hwndMain, hdc_window );\r
2056     }\r
2057 }\r
2058 \r
2059 HBITMAP\r
2060 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2061 {\r
2062   char name[128];\r
2063 \r
2064     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2065   if (gameInfo.event &&\r
2066       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2067       strcmp(name, "k80s") == 0) {\r
2068     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2069   }\r
2070   return LoadBitmap(hinst, name);\r
2071 }\r
2072 \r
2073 \r
2074 /* Insert a color into the program's logical palette\r
2075    structure.  This code assumes the given color is\r
2076    the result of the RGB or PALETTERGB macro, and it\r
2077    knows how those macros work (which is documented).\r
2078 */\r
2079 VOID\r
2080 InsertInPalette(COLORREF color)\r
2081 {\r
2082   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2083 \r
2084   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2085     DisplayFatalError(_("Too many colors"), 0, 1);\r
2086     pLogPal->palNumEntries--;\r
2087     return;\r
2088   }\r
2089 \r
2090   pe->peFlags = (char) 0;\r
2091   pe->peRed = (char) (0xFF & color);\r
2092   pe->peGreen = (char) (0xFF & (color >> 8));\r
2093   pe->peBlue = (char) (0xFF & (color >> 16));\r
2094   return;\r
2095 }\r
2096 \r
2097 \r
2098 VOID\r
2099 InitDrawingColors()\r
2100 {\r
2101   if (pLogPal == NULL) {\r
2102     /* Allocate enough memory for a logical palette with\r
2103      * PALETTESIZE entries and set the size and version fields\r
2104      * of the logical palette structure.\r
2105      */\r
2106     pLogPal = (NPLOGPALETTE)\r
2107       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2108                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2109     pLogPal->palVersion    = 0x300;\r
2110   }\r
2111   pLogPal->palNumEntries = 0;\r
2112 \r
2113   InsertInPalette(lightSquareColor);\r
2114   InsertInPalette(darkSquareColor);\r
2115   InsertInPalette(whitePieceColor);\r
2116   InsertInPalette(blackPieceColor);\r
2117   InsertInPalette(highlightSquareColor);\r
2118   InsertInPalette(premoveHighlightColor);\r
2119 \r
2120   /*  create a logical color palette according the information\r
2121    *  in the LOGPALETTE structure.\r
2122    */\r
2123   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2124 \r
2125   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2126   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2127   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2128   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2129   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2130   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2131   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2132   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2133   /* [AS] Force rendering of the font-based pieces */\r
2134   if( fontBitmapSquareSize > 0 ) {\r
2135     fontBitmapSquareSize = 0;\r
2136   }\r
2137 }\r
2138 \r
2139 \r
2140 int\r
2141 BoardWidth(int boardSize, int n)\r
2142 { /* [HGM] argument n added to allow different width and height */\r
2143   int lineGap = sizeInfo[boardSize].lineGap;\r
2144 \r
2145   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2146       lineGap = appData.overrideLineGap;\r
2147   }\r
2148 \r
2149   return (n + 1) * lineGap +\r
2150           n * sizeInfo[boardSize].squareSize;\r
2151 }\r
2152 \r
2153 /* Respond to board resize by dragging edge */\r
2154 VOID\r
2155 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2156 {\r
2157   BoardSize newSize = NUM_SIZES - 1;\r
2158   static int recurse = 0;\r
2159   if (IsIconic(hwndMain)) return;\r
2160   if (recurse > 0) return;\r
2161   recurse++;\r
2162   while (newSize > 0) {\r
2163         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2164         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2165            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2166     newSize--;\r
2167   } \r
2168   boardSize = newSize;\r
2169   InitDrawingSizes(boardSize, flags);\r
2170   recurse--;\r
2171 }\r
2172 \r
2173 \r
2174 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2175 \r
2176 VOID\r
2177 InitDrawingSizes(BoardSize boardSize, int flags)\r
2178 {\r
2179   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2180   ChessSquare piece;\r
2181   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2182   HDC hdc;\r
2183   SIZE clockSize, messageSize;\r
2184   HFONT oldFont;\r
2185   char buf[MSG_SIZ];\r
2186   char *str;\r
2187   HMENU hmenu = GetMenu(hwndMain);\r
2188   RECT crect, wrect, oldRect;\r
2189   int offby;\r
2190   LOGBRUSH logbrush;\r
2191 \r
2192   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2193   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2194 \r
2195   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2196   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2197 \r
2198   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2199   oldRect.top = wpMain.y;\r
2200   oldRect.right = wpMain.x + wpMain.width;\r
2201   oldRect.bottom = wpMain.y + wpMain.height;\r
2202 \r
2203   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2204   smallLayout = sizeInfo[boardSize].smallLayout;\r
2205   squareSize = sizeInfo[boardSize].squareSize;\r
2206   lineGap = sizeInfo[boardSize].lineGap;\r
2207   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2208 \r
2209   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2210       lineGap = appData.overrideLineGap;\r
2211   }\r
2212 \r
2213   if (tinyLayout != oldTinyLayout) {\r
2214     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
2215     if (tinyLayout) {\r
2216       style &= ~WS_SYSMENU;\r
2217       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2218                  "&Minimize\tCtrl+F4");\r
2219     } else {\r
2220       style |= WS_SYSMENU;\r
2221       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2222     }\r
2223     SetWindowLong(hwndMain, GWL_STYLE, style);\r
2224 \r
2225     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2226       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2227         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2228     }\r
2229     DrawMenuBar(hwndMain);\r
2230   }\r
2231 \r
2232   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2233   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2234 \r
2235   /* Get text area sizes */\r
2236   hdc = GetDC(hwndMain);\r
2237   if (appData.clockMode) {\r
2238     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2239   } else {\r
2240     snprintf(buf, MSG_SIZ, _("White"));\r
2241   }\r
2242   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2243   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2244   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2245   str = _("We only care about the height here");\r
2246   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2247   SelectObject(hdc, oldFont);\r
2248   ReleaseDC(hwndMain, hdc);\r
2249 \r
2250   /* Compute where everything goes */\r
2251   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2252         /* [HGM] logo: if either logo is on, reserve space for it */\r
2253         logoHeight =  2*clockSize.cy;\r
2254         leftLogoRect.left   = OUTER_MARGIN;\r
2255         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2256         leftLogoRect.top    = OUTER_MARGIN;\r
2257         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2258 \r
2259         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2260         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2261         rightLogoRect.top    = OUTER_MARGIN;\r
2262         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2263 \r
2264 \r
2265     whiteRect.left = leftLogoRect.right;\r
2266     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2267     whiteRect.top = OUTER_MARGIN;\r
2268     whiteRect.bottom = whiteRect.top + logoHeight;\r
2269 \r
2270     blackRect.right = rightLogoRect.left;\r
2271     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2272     blackRect.top = whiteRect.top;\r
2273     blackRect.bottom = whiteRect.bottom;\r
2274   } else {\r
2275     whiteRect.left = OUTER_MARGIN;\r
2276     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2277     whiteRect.top = OUTER_MARGIN;\r
2278     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2279 \r
2280     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2281     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2282     blackRect.top = whiteRect.top;\r
2283     blackRect.bottom = whiteRect.bottom;\r
2284 \r
2285     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2286   }\r
2287 \r
2288   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2289   if (appData.showButtonBar) {\r
2290     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2291       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2292   } else {\r
2293     messageRect.right = OUTER_MARGIN + boardWidth;\r
2294   }\r
2295   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2296   messageRect.bottom = messageRect.top + messageSize.cy;\r
2297 \r
2298   boardRect.left = OUTER_MARGIN;\r
2299   boardRect.right = boardRect.left + boardWidth;\r
2300   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2301   boardRect.bottom = boardRect.top + boardHeight;\r
2302 \r
2303   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2304   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2305   oldBoardSize = boardSize;\r
2306   oldTinyLayout = tinyLayout;\r
2307   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2308   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2309     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2310   winW *= 1 + twoBoards;\r
2311   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2312   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2313   wpMain.height = winH; //       without disturbing window attachments\r
2314   GetWindowRect(hwndMain, &wrect);\r
2315   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2316                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2317 \r
2318   // [HGM] placement: let attached windows follow size change.\r
2319   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2320   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2321   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2322   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2323   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2324 \r
2325   /* compensate if menu bar wrapped */\r
2326   GetClientRect(hwndMain, &crect);\r
2327   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2328   wpMain.height += offby;\r
2329   switch (flags) {\r
2330   case WMSZ_TOPLEFT:\r
2331     SetWindowPos(hwndMain, NULL, \r
2332                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2333                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2334     break;\r
2335 \r
2336   case WMSZ_TOPRIGHT:\r
2337   case WMSZ_TOP:\r
2338     SetWindowPos(hwndMain, NULL, \r
2339                  wrect.left, wrect.bottom - wpMain.height, \r
2340                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2341     break;\r
2342 \r
2343   case WMSZ_BOTTOMLEFT:\r
2344   case WMSZ_LEFT:\r
2345     SetWindowPos(hwndMain, NULL, \r
2346                  wrect.right - wpMain.width, wrect.top, \r
2347                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2348     break;\r
2349 \r
2350   case WMSZ_BOTTOMRIGHT:\r
2351   case WMSZ_BOTTOM:\r
2352   case WMSZ_RIGHT:\r
2353   default:\r
2354     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2355                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2356     break;\r
2357   }\r
2358 \r
2359   hwndPause = NULL;\r
2360   for (i = 0; i < N_BUTTONS; i++) {\r
2361     if (buttonDesc[i].hwnd != NULL) {\r
2362       DestroyWindow(buttonDesc[i].hwnd);\r
2363       buttonDesc[i].hwnd = NULL;\r
2364     }\r
2365     if (appData.showButtonBar) {\r
2366       buttonDesc[i].hwnd =\r
2367         CreateWindow("BUTTON", buttonDesc[i].label,\r
2368                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2369                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2370                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2371                      (HMENU) buttonDesc[i].id,\r
2372                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
2373       if (tinyLayout) {\r
2374         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2375                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2376                     MAKELPARAM(FALSE, 0));\r
2377       }\r
2378       if (buttonDesc[i].id == IDM_Pause)\r
2379         hwndPause = buttonDesc[i].hwnd;\r
2380       buttonDesc[i].wndproc = (WNDPROC)\r
2381         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
2382     }\r
2383   }\r
2384   if (gridPen != NULL) DeleteObject(gridPen);\r
2385   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2386   if (premovePen != NULL) DeleteObject(premovePen);\r
2387   if (lineGap != 0) {\r
2388     logbrush.lbStyle = BS_SOLID;\r
2389     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2390     gridPen =\r
2391       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2392                    lineGap, &logbrush, 0, NULL);\r
2393     logbrush.lbColor = highlightSquareColor;\r
2394     highlightPen =\r
2395       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2396                    lineGap, &logbrush, 0, NULL);\r
2397 \r
2398     logbrush.lbColor = premoveHighlightColor; \r
2399     premovePen =\r
2400       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2401                    lineGap, &logbrush, 0, NULL);\r
2402 \r
2403     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2404     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2405       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2406       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2407         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2408       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2409         BOARD_WIDTH * (squareSize + lineGap);\r
2410       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2411     }\r
2412     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2413       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2414       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2415         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2416         lineGap / 2 + (i * (squareSize + lineGap));\r
2417       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2418         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2419       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2420     }\r
2421   }\r
2422 \r
2423   /* [HGM] Licensing requirement */\r
2424 #ifdef GOTHIC\r
2425   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2426 #endif\r
2427 #ifdef FALCON\r
2428   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2429 #endif\r
2430   GothicPopUp( "", VariantNormal);\r
2431 \r
2432 \r
2433 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2434 \r
2435   /* Load piece bitmaps for this board size */\r
2436   for (i=0; i<=2; i++) {\r
2437     for (piece = WhitePawn;\r
2438          (int) piece < (int) BlackPawn;\r
2439          piece = (ChessSquare) ((int) piece + 1)) {\r
2440       if (pieceBitmap[i][piece] != NULL)\r
2441         DeleteObject(pieceBitmap[i][piece]);\r
2442     }\r
2443   }\r
2444 \r
2445   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2446   // Orthodox Chess pieces\r
2447   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2448   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2449   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2450   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2451   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2452   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2453   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2454   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2455   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2456   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2457   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2458   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2459   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2460   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2461   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2462   if( gameInfo.variant == VariantShogi && (squareSize==72 || squareSize==49)) {\r
2463     // in Shogi, Hijack the unused Queen for Lance\r
2464     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2465     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2466     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2467   } else {\r
2468     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2469     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2470     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2471   }\r
2472 \r
2473   if(squareSize <= 72 && squareSize >= 33) { \r
2474     /* A & C are available in most sizes now */\r
2475     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2476       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2477       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2478       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2479       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2480       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2481       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2482       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2483       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2484       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2485       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2486       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2487       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2488     } else { // Smirf-like\r
2489       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2490       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2491       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2492     }\r
2493     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2494       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2495       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2496       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2497     } else { // WinBoard standard\r
2498       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2499       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2500       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2501     }\r
2502   }\r
2503 \r
2504 \r
2505   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2506     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2507     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2508     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2509     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2510     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2511     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2512     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2513     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2514     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2515     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2516     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2517     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2518     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2519     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2520     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2521     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2522     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2523     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2524     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2525     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2526     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2527     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2528     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2529     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2530     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2531     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2532     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2533     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2534     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2535     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2536 \r
2537     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2538       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2539       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2540       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2541       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2542       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2543       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2544       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2545       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2546       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2547       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2548       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2549       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2550     } else {\r
2551       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2552       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2553       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2554       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2555       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2556       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2557       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2558       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2559       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2560       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2561       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2562       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2563     }\r
2564 \r
2565   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2566     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2567     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2568     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2569     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2570     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2571     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2572     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2573     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2574     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2575     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2576     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2577     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2578     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2579     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2580   }\r
2581 \r
2582 \r
2583   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2584   /* special Shogi support in this size */\r
2585   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2586       for (piece = WhitePawn;\r
2587            (int) piece < (int) BlackPawn;\r
2588            piece = (ChessSquare) ((int) piece + 1)) {\r
2589         if (pieceBitmap[i][piece] != NULL)\r
2590           DeleteObject(pieceBitmap[i][piece]);\r
2591       }\r
2592     }\r
2593   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2594   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2595   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2596   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2597   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2598   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2599   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2600   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2601   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2602   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2603   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2604   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2605   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2606   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2607   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2608   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2609   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2610   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2611   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2612   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2613   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2614   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2615   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2616   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2617   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2618   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2619   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2620   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2621   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2622   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2623   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2624   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2625   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2626   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2627   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2628   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2629   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2630   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2631   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2632   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2633   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2634   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2635   minorSize = 0;\r
2636   }\r
2637 }\r
2638 \r
2639 HBITMAP\r
2640 PieceBitmap(ChessSquare p, int kind)\r
2641 {\r
2642   if ((int) p >= (int) BlackPawn)\r
2643     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2644 \r
2645   return pieceBitmap[kind][(int) p];\r
2646 }\r
2647 \r
2648 /***************************************************************/\r
2649 \r
2650 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2651 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2652 /*\r
2653 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2654 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2655 */\r
2656 \r
2657 VOID\r
2658 SquareToPos(int row, int column, int * x, int * y)\r
2659 {\r
2660   if (flipView) {\r
2661     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2662     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2663   } else {\r
2664     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2665     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2666   }\r
2667 }\r
2668 \r
2669 VOID\r
2670 DrawCoordsOnDC(HDC hdc)\r
2671 {\r
2672   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
2673   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
2674   char str[2] = { NULLCHAR, NULLCHAR };\r
2675   int oldMode, oldAlign, x, y, start, i;\r
2676   HFONT oldFont;\r
2677   HBRUSH oldBrush;\r
2678 \r
2679   if (!appData.showCoords)\r
2680     return;\r
2681 \r
2682   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2683 \r
2684   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2685   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2686   oldAlign = GetTextAlign(hdc);\r
2687   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2688 \r
2689   y = boardRect.top + lineGap;\r
2690   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2691 \r
2692   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2693   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2694     str[0] = files[start + i];\r
2695     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2696     y += squareSize + lineGap;\r
2697   }\r
2698 \r
2699   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2700 \r
2701   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2702   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2703     str[0] = ranks[start + i];\r
2704     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2705     x += squareSize + lineGap;\r
2706   }    \r
2707 \r
2708   SelectObject(hdc, oldBrush);\r
2709   SetBkMode(hdc, oldMode);\r
2710   SetTextAlign(hdc, oldAlign);\r
2711   SelectObject(hdc, oldFont);\r
2712 }\r
2713 \r
2714 VOID\r
2715 DrawGridOnDC(HDC hdc)\r
2716 {\r
2717   HPEN oldPen;\r
2718  \r
2719   if (lineGap != 0) {\r
2720     oldPen = SelectObject(hdc, gridPen);\r
2721     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2722     SelectObject(hdc, oldPen);\r
2723   }\r
2724 }\r
2725 \r
2726 #define HIGHLIGHT_PEN 0\r
2727 #define PREMOVE_PEN   1\r
2728 \r
2729 VOID\r
2730 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2731 {\r
2732   int x1, y1;\r
2733   HPEN oldPen, hPen;\r
2734   if (lineGap == 0) return;\r
2735   if (flipView) {\r
2736     x1 = boardRect.left +\r
2737       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2738     y1 = boardRect.top +\r
2739       lineGap/2 + y * (squareSize + lineGap);\r
2740   } else {\r
2741     x1 = boardRect.left +\r
2742       lineGap/2 + x * (squareSize + lineGap);\r
2743     y1 = boardRect.top +\r
2744       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2745   }\r
2746   hPen = pen ? premovePen : highlightPen;\r
2747   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2748   MoveToEx(hdc, x1, y1, NULL);\r
2749   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2750   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2751   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2752   LineTo(hdc, x1, y1);\r
2753   SelectObject(hdc, oldPen);\r
2754 }\r
2755 \r
2756 VOID\r
2757 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2758 {\r
2759   int i;\r
2760   for (i=0; i<2; i++) {\r
2761     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2762       DrawHighlightOnDC(hdc, TRUE,\r
2763                         h->sq[i].x, h->sq[i].y,\r
2764                         pen);\r
2765   }\r
2766 }\r
2767 \r
2768 /* Note: sqcolor is used only in monoMode */\r
2769 /* Note that this code is largely duplicated in woptions.c,\r
2770    function DrawSampleSquare, so that needs to be updated too */\r
2771 VOID\r
2772 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2773 {\r
2774   HBITMAP oldBitmap;\r
2775   HBRUSH oldBrush;\r
2776   int tmpSize;\r
2777 \r
2778   if (appData.blindfold) return;\r
2779 \r
2780   /* [AS] Use font-based pieces if needed */\r
2781   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2782     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2783     CreatePiecesFromFont();\r
2784 \r
2785     if( fontBitmapSquareSize == squareSize ) {\r
2786         int index = TranslatePieceToFontPiece(piece);\r
2787 \r
2788         SelectObject( tmphdc, hPieceMask[ index ] );\r
2789 \r
2790       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2791         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2792       else\r
2793         BitBlt( hdc,\r
2794             x, y,\r
2795             squareSize, squareSize,\r
2796             tmphdc,\r
2797             0, 0,\r
2798             SRCAND );\r
2799 \r
2800         SelectObject( tmphdc, hPieceFace[ index ] );\r
2801 \r
2802       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2803         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2804       else\r
2805         BitBlt( hdc,\r
2806             x, y,\r
2807             squareSize, squareSize,\r
2808             tmphdc,\r
2809             0, 0,\r
2810             SRCPAINT );\r
2811 \r
2812         return;\r
2813     }\r
2814   }\r
2815 \r
2816   if (appData.monoMode) {\r
2817     SelectObject(tmphdc, PieceBitmap(piece, \r
2818       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2819     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2820            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2821   } else {\r
2822     tmpSize = squareSize;\r
2823     if(minorSize &&\r
2824         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2825          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2826       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2827       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2828       x += (squareSize - minorSize)>>1;\r
2829       y += squareSize - minorSize - 2;\r
2830       tmpSize = minorSize;\r
2831     }\r
2832     if (color || appData.allWhite ) {\r
2833       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2834       if( color )\r
2835               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2836       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2837       if(appData.upsideDown && color==flipView)\r
2838         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2839       else\r
2840         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2841       /* Use black for outline of white pieces */\r
2842       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2843       if(appData.upsideDown && color==flipView)\r
2844         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2845       else\r
2846         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2847     } else {\r
2848       /* Use square color for details of black pieces */\r
2849       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2850       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2851       if(appData.upsideDown && !flipView)\r
2852         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2853       else\r
2854         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2855     }\r
2856     SelectObject(hdc, oldBrush);\r
2857     SelectObject(tmphdc, oldBitmap);\r
2858   }\r
2859 }\r
2860 \r
2861 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2862 int GetBackTextureMode( int algo )\r
2863 {\r
2864     int result = BACK_TEXTURE_MODE_DISABLED;\r
2865 \r
2866     switch( algo ) \r
2867     {\r
2868         case BACK_TEXTURE_MODE_PLAIN:\r
2869             result = 1; /* Always use identity map */\r
2870             break;\r
2871         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2872             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2873             break;\r
2874     }\r
2875 \r
2876     return result;\r
2877 }\r
2878 \r
2879 /* \r
2880     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2881     to handle redraws cleanly (as random numbers would always be different).\r
2882 */\r
2883 VOID RebuildTextureSquareInfo()\r
2884 {\r
2885     BITMAP bi;\r
2886     int lite_w = 0;\r
2887     int lite_h = 0;\r
2888     int dark_w = 0;\r
2889     int dark_h = 0;\r
2890     int row;\r
2891     int col;\r
2892 \r
2893     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2894 \r
2895     if( liteBackTexture != NULL ) {\r
2896         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2897             lite_w = bi.bmWidth;\r
2898             lite_h = bi.bmHeight;\r
2899         }\r
2900     }\r
2901 \r
2902     if( darkBackTexture != NULL ) {\r
2903         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2904             dark_w = bi.bmWidth;\r
2905             dark_h = bi.bmHeight;\r
2906         }\r
2907     }\r
2908 \r
2909     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2910         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2911             if( (col + row) & 1 ) {\r
2912                 /* Lite square */\r
2913                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2914                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2915                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2916                   else\r
2917                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2918                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2919                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2920                   else\r
2921                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2922                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2923                 }\r
2924             }\r
2925             else {\r
2926                 /* Dark square */\r
2927                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2928                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2929                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2930                   else\r
2931                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2932                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2933                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2934                   else\r
2935                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2936                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2937                 }\r
2938             }\r
2939         }\r
2940     }\r
2941 }\r
2942 \r
2943 /* [AS] Arrow highlighting support */\r
2944 \r
2945 static int A_WIDTH = 5; /* Width of arrow body */\r
2946 \r
2947 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2948 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2949 \r
2950 static double Sqr( double x )\r
2951 {\r
2952     return x*x;\r
2953 }\r
2954 \r
2955 static int Round( double x )\r
2956 {\r
2957     return (int) (x + 0.5);\r
2958 }\r
2959 \r
2960 /* Draw an arrow between two points using current settings */\r
2961 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2962 {\r
2963     POINT arrow[7];\r
2964     double dx, dy, j, k, x, y;\r
2965 \r
2966     if( d_x == s_x ) {\r
2967         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2968 \r
2969         arrow[0].x = s_x + A_WIDTH;\r
2970         arrow[0].y = s_y;\r
2971 \r
2972         arrow[1].x = s_x + A_WIDTH;\r
2973         arrow[1].y = d_y - h;\r
2974 \r
2975         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
2976         arrow[2].y = d_y - h;\r
2977 \r
2978         arrow[3].x = d_x;\r
2979         arrow[3].y = d_y;\r
2980 \r
2981         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
2982         arrow[4].y = d_y - h;\r
2983 \r
2984         arrow[5].x = s_x - A_WIDTH;\r
2985         arrow[5].y = d_y - h;\r
2986 \r
2987         arrow[6].x = s_x - A_WIDTH;\r
2988         arrow[6].y = s_y;\r
2989     }\r
2990     else if( d_y == s_y ) {\r
2991         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2992 \r
2993         arrow[0].x = s_x;\r
2994         arrow[0].y = s_y + A_WIDTH;\r
2995 \r
2996         arrow[1].x = d_x - w;\r
2997         arrow[1].y = s_y + A_WIDTH;\r
2998 \r
2999         arrow[2].x = d_x - w;\r
3000         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3001 \r
3002         arrow[3].x = d_x;\r
3003         arrow[3].y = d_y;\r
3004 \r
3005         arrow[4].x = d_x - w;\r
3006         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3007 \r
3008         arrow[5].x = d_x - w;\r
3009         arrow[5].y = s_y - A_WIDTH;\r
3010 \r
3011         arrow[6].x = s_x;\r
3012         arrow[6].y = s_y - A_WIDTH;\r
3013     }\r
3014     else {\r
3015         /* [AS] Needed a lot of paper for this! :-) */\r
3016         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3017         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3018   \r
3019         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3020 \r
3021         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3022 \r
3023         x = s_x;\r
3024         y = s_y;\r
3025 \r
3026         arrow[0].x = Round(x - j);\r
3027         arrow[0].y = Round(y + j*dx);\r
3028 \r
3029         arrow[1].x = Round(x + j);\r
3030         arrow[1].y = Round(y - j*dx);\r
3031 \r
3032         if( d_x > s_x ) {\r
3033             x = (double) d_x - k;\r
3034             y = (double) d_y - k*dy;\r
3035         }\r
3036         else {\r
3037             x = (double) d_x + k;\r
3038             y = (double) d_y + k*dy;\r
3039         }\r
3040 \r
3041         arrow[2].x = Round(x + j);\r
3042         arrow[2].y = Round(y - j*dx);\r
3043 \r
3044         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3045         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3046 \r
3047         arrow[4].x = d_x;\r
3048         arrow[4].y = d_y;\r
3049 \r
3050         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3051         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3052 \r
3053         arrow[6].x = Round(x - j);\r
3054         arrow[6].y = Round(y + j*dx);\r
3055     }\r
3056 \r
3057     Polygon( hdc, arrow, 7 );\r
3058 }\r
3059 \r
3060 /* [AS] Draw an arrow between two squares */\r
3061 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3062 {\r
3063     int s_x, s_y, d_x, d_y;\r
3064     HPEN hpen;\r
3065     HPEN holdpen;\r
3066     HBRUSH hbrush;\r
3067     HBRUSH holdbrush;\r
3068     LOGBRUSH stLB;\r
3069 \r
3070     if( s_col == d_col && s_row == d_row ) {\r
3071         return;\r
3072     }\r
3073 \r
3074     /* Get source and destination points */\r
3075     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3076     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3077 \r
3078     if( d_y > s_y ) {\r
3079         d_y += squareSize / 4;\r
3080     }\r
3081     else if( d_y < s_y ) {\r
3082         d_y += 3 * squareSize / 4;\r
3083     }\r
3084     else {\r
3085         d_y += squareSize / 2;\r
3086     }\r
3087 \r
3088     if( d_x > s_x ) {\r
3089         d_x += squareSize / 4;\r
3090     }\r
3091     else if( d_x < s_x ) {\r
3092         d_x += 3 * squareSize / 4;\r
3093     }\r
3094     else {\r
3095         d_x += squareSize / 2;\r
3096     }\r
3097 \r
3098     s_x += squareSize / 2;\r
3099     s_y += squareSize / 2;\r
3100 \r
3101     /* Adjust width */\r
3102     A_WIDTH = squareSize / 14;\r
3103 \r
3104     /* Draw */\r
3105     stLB.lbStyle = BS_SOLID;\r
3106     stLB.lbColor = appData.highlightArrowColor;\r
3107     stLB.lbHatch = 0;\r
3108 \r
3109     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3110     holdpen = SelectObject( hdc, hpen );\r
3111     hbrush = CreateBrushIndirect( &stLB );\r
3112     holdbrush = SelectObject( hdc, hbrush );\r
3113 \r
3114     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3115 \r
3116     SelectObject( hdc, holdpen );\r
3117     SelectObject( hdc, holdbrush );\r
3118     DeleteObject( hpen );\r
3119     DeleteObject( hbrush );\r
3120 }\r
3121 \r
3122 BOOL HasHighlightInfo()\r
3123 {\r
3124     BOOL result = FALSE;\r
3125 \r
3126     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3127         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3128     {\r
3129         result = TRUE;\r
3130     }\r
3131 \r
3132     return result;\r
3133 }\r
3134 \r
3135 BOOL IsDrawArrowEnabled()\r
3136 {\r
3137     BOOL result = FALSE;\r
3138 \r
3139     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3140         result = TRUE;\r
3141     }\r
3142 \r
3143     return result;\r
3144 }\r
3145 \r
3146 VOID DrawArrowHighlight( HDC hdc )\r
3147 {\r
3148     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3149         DrawArrowBetweenSquares( hdc,\r
3150             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3151             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3152     }\r
3153 }\r
3154 \r
3155 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3156 {\r
3157     HRGN result = NULL;\r
3158 \r
3159     if( HasHighlightInfo() ) {\r
3160         int x1, y1, x2, y2;\r
3161         int sx, sy, dx, dy;\r
3162 \r
3163         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3164         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3165 \r
3166         sx = MIN( x1, x2 );\r
3167         sy = MIN( y1, y2 );\r
3168         dx = MAX( x1, x2 ) + squareSize;\r
3169         dy = MAX( y1, y2 ) + squareSize;\r
3170 \r
3171         result = CreateRectRgn( sx, sy, dx, dy );\r
3172     }\r
3173 \r
3174     return result;\r
3175 }\r
3176 \r
3177 /*\r
3178     Warning: this function modifies the behavior of several other functions. \r
3179     \r
3180     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3181     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3182     repaint is scattered all over the place, which is not good for features such as\r
3183     "arrow highlighting" that require a full repaint of the board.\r
3184 \r
3185     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3186     user interaction, when speed is not so important) but especially to avoid errors\r
3187     in the displayed graphics.\r
3188 \r
3189     In such patched places, I always try refer to this function so there is a single\r
3190     place to maintain knowledge.\r
3191     \r
3192     To restore the original behavior, just return FALSE unconditionally.\r
3193 */\r
3194 BOOL IsFullRepaintPreferrable()\r
3195 {\r
3196     BOOL result = FALSE;\r
3197 \r
3198     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3199         /* Arrow may appear on the board */\r
3200         result = TRUE;\r
3201     }\r
3202 \r
3203     return result;\r
3204 }\r
3205 \r
3206 /* \r
3207     This function is called by DrawPosition to know whether a full repaint must\r
3208     be forced or not.\r
3209 \r
3210     Only DrawPosition may directly call this function, which makes use of \r
3211     some state information. Other function should call DrawPosition specifying \r
3212     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3213 */\r
3214 BOOL DrawPositionNeedsFullRepaint()\r
3215 {\r
3216     BOOL result = FALSE;\r
3217 \r
3218     /* \r
3219         Probably a slightly better policy would be to trigger a full repaint\r
3220         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3221         but animation is fast enough that it's difficult to notice.\r
3222     */\r
3223     if( animInfo.piece == EmptySquare ) {\r
3224         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3225             result = TRUE;\r
3226         }\r
3227     }\r
3228 \r
3229     return result;\r
3230 }\r
3231 \r
3232 VOID\r
3233 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3234 {\r
3235   int row, column, x, y, square_color, piece_color;\r
3236   ChessSquare piece;\r
3237   HBRUSH oldBrush;\r
3238   HDC texture_hdc = NULL;\r
3239 \r
3240   /* [AS] Initialize background textures if needed */\r
3241   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3242       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3243       if( backTextureSquareSize != squareSize \r
3244        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3245           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3246           backTextureSquareSize = squareSize;\r
3247           RebuildTextureSquareInfo();\r
3248       }\r
3249 \r
3250       texture_hdc = CreateCompatibleDC( hdc );\r
3251   }\r
3252 \r
3253   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3254     for (column = 0; column < BOARD_WIDTH; column++) {\r
3255   \r
3256       SquareToPos(row, column, &x, &y);\r
3257 \r
3258       piece = board[row][column];\r
3259 \r
3260       square_color = ((column + row) % 2) == 1;\r
3261       if( gameInfo.variant == VariantXiangqi ) {\r
3262           square_color = !InPalace(row, column);\r
3263           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3264           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3265       }\r
3266       piece_color = (int) piece < (int) BlackPawn;\r
3267 \r
3268 \r
3269       /* [HGM] holdings file: light square or black */\r
3270       if(column == BOARD_LEFT-2) {\r
3271             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3272                 square_color = 1;\r
3273             else {\r
3274                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3275                 continue;\r
3276             }\r
3277       } else\r
3278       if(column == BOARD_RGHT + 1 ) {\r
3279             if( row < gameInfo.holdingsSize )\r
3280                 square_color = 1;\r
3281             else {\r
3282                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3283                 continue;\r
3284             }\r
3285       }\r
3286       if(column == BOARD_LEFT-1 ) /* left align */\r
3287             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3288       else if( column == BOARD_RGHT) /* right align */\r
3289             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3290       else\r
3291       if (appData.monoMode) {\r
3292         if (piece == EmptySquare) {\r
3293           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3294                  square_color ? WHITENESS : BLACKNESS);\r
3295         } else {\r
3296           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3297         }\r
3298       } \r
3299       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3300           /* [AS] Draw the square using a texture bitmap */\r
3301           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3302           int r = row, c = column; // [HGM] do not flip board in flipView\r
3303           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3304 \r
3305           DrawTile( x, y, \r
3306               squareSize, squareSize, \r
3307               hdc, \r
3308               texture_hdc,\r
3309               backTextureSquareInfo[r][c].mode,\r
3310               backTextureSquareInfo[r][c].x,\r
3311               backTextureSquareInfo[r][c].y );\r
3312 \r
3313           SelectObject( texture_hdc, hbm );\r
3314 \r
3315           if (piece != EmptySquare) {\r
3316               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3317           }\r
3318       }\r
3319       else {\r
3320         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3321 \r
3322         oldBrush = SelectObject(hdc, brush );\r
3323         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3324         SelectObject(hdc, oldBrush);\r
3325         if (piece != EmptySquare)\r
3326           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3327       }\r
3328     }\r
3329   }\r
3330 \r
3331   if( texture_hdc != NULL ) {\r
3332     DeleteDC( texture_hdc );\r
3333   }\r
3334 }\r
3335 \r
3336 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3337 void fputDW(FILE *f, int x)\r
3338 {\r
3339         fputc(x     & 255, f);\r
3340         fputc(x>>8  & 255, f);\r
3341         fputc(x>>16 & 255, f);\r
3342         fputc(x>>24 & 255, f);\r
3343 }\r
3344 \r
3345 #define MAX_CLIPS 200   /* more than enough */\r
3346 \r
3347 VOID\r
3348 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3349 {\r
3350 //  HBITMAP bufferBitmap;\r
3351   BITMAP bi;\r
3352 //  RECT Rect;\r
3353   HDC tmphdc;\r
3354   HBITMAP hbm;\r
3355   int w = 100, h = 50;\r
3356 \r
3357   if(logo == NULL) return;\r
3358 //  GetClientRect(hwndMain, &Rect);\r
3359 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3360 //                                      Rect.bottom-Rect.top+1);\r
3361   tmphdc = CreateCompatibleDC(hdc);\r
3362   hbm = SelectObject(tmphdc, logo);\r
3363   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3364             w = bi.bmWidth;\r
3365             h = bi.bmHeight;\r
3366   }\r
3367   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3368                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3369   SelectObject(tmphdc, hbm);\r
3370   DeleteDC(tmphdc);\r
3371 }\r
3372 \r
3373 static HDC hdcSeek;\r
3374 \r
3375 // [HGM] seekgraph\r
3376 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3377 {\r
3378     POINT stPt;\r
3379     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3380     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3381     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3382     SelectObject( hdcSeek, hp );\r
3383 }\r
3384 \r
3385 // front-end wrapper for drawing functions to do rectangles\r
3386 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3387 {\r
3388     HPEN hp;\r
3389     RECT rc;\r
3390 \r
3391     if (hdcSeek == NULL) {\r
3392     hdcSeek = GetDC(hwndMain);\r
3393       if (!appData.monoMode) {\r
3394         SelectPalette(hdcSeek, hPal, FALSE);\r
3395         RealizePalette(hdcSeek);\r
3396       }\r
3397     }\r
3398     hp = SelectObject( hdcSeek, gridPen );\r
3399     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3400     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3401     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3402     SelectObject( hdcSeek, hp );\r
3403 }\r
3404 \r
3405 // front-end wrapper for putting text in graph\r
3406 void DrawSeekText(char *buf, int x, int y)\r
3407 {\r
3408         SIZE stSize;\r
3409         SetBkMode( hdcSeek, TRANSPARENT );\r
3410         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3411         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3412 }\r
3413 \r
3414 void DrawSeekDot(int x, int y, int color)\r
3415 {\r
3416         int square = color & 0x80;\r
3417         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3418                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3419         color &= 0x7F;\r
3420         if(square)\r
3421             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3422                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3423         else\r
3424             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3425                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3426             SelectObject(hdcSeek, oldBrush);\r
3427 }\r
3428 \r
3429 VOID\r
3430 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3431 {\r
3432   static Board lastReq[2], lastDrawn[2];\r
3433   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3434   static int lastDrawnFlipView = 0;\r
3435   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3436   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3437   HDC tmphdc;\r
3438   HDC hdcmem;\r
3439   HBITMAP bufferBitmap;\r
3440   HBITMAP oldBitmap;\r
3441   RECT Rect;\r
3442   HRGN clips[MAX_CLIPS];\r
3443   ChessSquare dragged_piece = EmptySquare;\r
3444   int nr = twoBoards*partnerUp;\r
3445 \r
3446   /* I'm undecided on this - this function figures out whether a full\r
3447    * repaint is necessary on its own, so there's no real reason to have the\r
3448    * caller tell it that.  I think this can safely be set to FALSE - but\r
3449    * if we trust the callers not to request full repaints unnessesarily, then\r
3450    * we could skip some clipping work.  In other words, only request a full\r
3451    * redraw when the majority of pieces have changed positions (ie. flip, \r
3452    * gamestart and similar)  --Hawk\r
3453    */\r
3454   Boolean fullrepaint = repaint;\r
3455 \r
3456   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3457 \r
3458   if( DrawPositionNeedsFullRepaint() ) {\r
3459       fullrepaint = TRUE;\r
3460   }\r
3461 \r
3462   if (board == NULL) {\r
3463     if (!lastReqValid[nr]) {\r
3464       return;\r
3465     }\r
3466     board = lastReq[nr];\r
3467   } else {\r
3468     CopyBoard(lastReq[nr], board);\r
3469     lastReqValid[nr] = 1;\r
3470   }\r
3471 \r
3472   if (doingSizing) {\r
3473     return;\r
3474   }\r
3475 \r
3476   if (IsIconic(hwndMain)) {\r
3477     return;\r
3478   }\r
3479 \r
3480   if (hdc == NULL) {\r
3481     hdc = GetDC(hwndMain);\r
3482     if (!appData.monoMode) {\r
3483       SelectPalette(hdc, hPal, FALSE);\r
3484       RealizePalette(hdc);\r
3485     }\r
3486     releaseDC = TRUE;\r
3487   } else {\r
3488     releaseDC = FALSE;\r
3489   }\r
3490 \r
3491   /* Create some work-DCs */\r
3492   hdcmem = CreateCompatibleDC(hdc);\r
3493   tmphdc = CreateCompatibleDC(hdc);\r
3494 \r
3495   /* If dragging is in progress, we temporarely remove the piece */\r
3496   /* [HGM] or temporarily decrease count if stacked              */\r
3497   /*       !! Moved to before board compare !!                   */\r
3498   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3499     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3500     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3501             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3502         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3503     } else \r
3504     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3505             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3506         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3507     } else \r
3508         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3509   }\r
3510 \r
3511   /* Figure out which squares need updating by comparing the \r
3512    * newest board with the last drawn board and checking if\r
3513    * flipping has changed.\r
3514    */\r
3515   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3516     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3517       for (column = 0; column < BOARD_WIDTH; column++) {\r
3518         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3519           SquareToPos(row, column, &x, &y);\r
3520           clips[num_clips++] =\r
3521             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3522         }\r
3523       }\r
3524     }\r
3525    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3526     for (i=0; i<2; i++) {\r
3527       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3528           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3529         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3530             lastDrawnHighlight.sq[i].y >= 0) {\r
3531           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3532                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3533           clips[num_clips++] =\r
3534             CreateRectRgn(x - lineGap, y - lineGap, \r
3535                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3536         }\r
3537         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3538           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3539           clips[num_clips++] =\r
3540             CreateRectRgn(x - lineGap, y - lineGap, \r
3541                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3542         }\r
3543       }\r
3544     }\r
3545     for (i=0; i<2; i++) {\r
3546       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3547           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3548         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3549             lastDrawnPremove.sq[i].y >= 0) {\r
3550           SquareToPos(lastDrawnPremove.sq[i].y,\r
3551                       lastDrawnPremove.sq[i].x, &x, &y);\r
3552           clips[num_clips++] =\r
3553             CreateRectRgn(x - lineGap, y - lineGap, \r
3554                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3555         }\r
3556         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3557             premoveHighlightInfo.sq[i].y >= 0) {\r
3558           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3559                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3560           clips[num_clips++] =\r
3561             CreateRectRgn(x - lineGap, y - lineGap, \r
3562                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3563         }\r
3564       }\r
3565     }\r
3566    } else { // nr == 1\r
3567         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3568         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3569         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3570         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3571       for (i=0; i<2; i++) {\r
3572         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3573             partnerHighlightInfo.sq[i].y >= 0) {\r
3574           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3575                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3576           clips[num_clips++] =\r
3577             CreateRectRgn(x - lineGap, y - lineGap, \r
3578                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3579         }\r
3580         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3581             oldPartnerHighlight.sq[i].y >= 0) {\r
3582           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3583                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3584           clips[num_clips++] =\r
3585             CreateRectRgn(x - lineGap, y - lineGap, \r
3586                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3587         }\r
3588       }\r
3589    }\r
3590   } else {\r
3591     fullrepaint = TRUE;\r
3592   }\r
3593 \r
3594   /* Create a buffer bitmap - this is the actual bitmap\r
3595    * being written to.  When all the work is done, we can\r
3596    * copy it to the real DC (the screen).  This avoids\r
3597    * the problems with flickering.\r
3598    */\r
3599   GetClientRect(hwndMain, &Rect);\r
3600   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3601                                         Rect.bottom-Rect.top+1);\r
3602   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3603   if (!appData.monoMode) {\r
3604     SelectPalette(hdcmem, hPal, FALSE);\r
3605   }\r
3606 \r
3607   /* Create clips for dragging */\r
3608   if (!fullrepaint) {\r
3609     if (dragInfo.from.x >= 0) {\r
3610       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3611       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3612     }\r
3613     if (dragInfo.start.x >= 0) {\r
3614       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3615       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3616     }\r
3617     if (dragInfo.pos.x >= 0) {\r
3618       x = dragInfo.pos.x - squareSize / 2;\r
3619       y = dragInfo.pos.y - squareSize / 2;\r
3620       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3621     }\r
3622     if (dragInfo.lastpos.x >= 0) {\r
3623       x = dragInfo.lastpos.x - squareSize / 2;\r
3624       y = dragInfo.lastpos.y - squareSize / 2;\r
3625       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3626     }\r
3627   }\r
3628 \r
3629   /* Are we animating a move?  \r
3630    * If so, \r
3631    *   - remove the piece from the board (temporarely)\r
3632    *   - calculate the clipping region\r
3633    */\r
3634   if (!fullrepaint) {\r
3635     if (animInfo.piece != EmptySquare) {\r
3636       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3637       x = boardRect.left + animInfo.lastpos.x;\r
3638       y = boardRect.top + animInfo.lastpos.y;\r
3639       x2 = boardRect.left + animInfo.pos.x;\r
3640       y2 = boardRect.top + animInfo.pos.y;\r
3641       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3642       /* Slight kludge.  The real problem is that after AnimateMove is\r
3643          done, the position on the screen does not match lastDrawn.\r
3644          This currently causes trouble only on e.p. captures in\r
3645          atomic, where the piece moves to an empty square and then\r
3646          explodes.  The old and new positions both had an empty square\r
3647          at the destination, but animation has drawn a piece there and\r
3648          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3649       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3650     }\r
3651   }\r
3652 \r
3653   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3654   if (num_clips == 0)\r
3655     fullrepaint = TRUE;\r
3656 \r
3657   /* Set clipping on the memory DC */\r
3658   if (!fullrepaint) {\r
3659     SelectClipRgn(hdcmem, clips[0]);\r
3660     for (x = 1; x < num_clips; x++) {\r
3661       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3662         abort();  // this should never ever happen!\r
3663     }\r
3664   }\r
3665 \r
3666   /* Do all the drawing to the memory DC */\r
3667   if(explodeInfo.radius) { // [HGM] atomic\r
3668         HBRUSH oldBrush;\r
3669         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3670         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3671         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3672         x += squareSize/2;\r
3673         y += squareSize/2;\r
3674         if(!fullrepaint) {\r
3675           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3676           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3677         }\r
3678         DrawGridOnDC(hdcmem);\r
3679         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3680         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3681         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3682         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3683         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3684         SelectObject(hdcmem, oldBrush);\r
3685   } else {\r
3686     DrawGridOnDC(hdcmem);\r
3687     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3688         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3689         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3690     } else {\r
3691         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3692         oldPartnerHighlight = partnerHighlightInfo;\r
3693     }\r
3694     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3695   }\r
3696   if(nr == 0) // [HGM] dual: markers only on left board\r
3697   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3698     for (column = 0; column < BOARD_WIDTH; column++) {\r
3699         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3700             HBRUSH oldBrush = SelectObject(hdcmem, \r
3701                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3702             SquareToPos(row, column, &x, &y);\r
3703             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3704                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3705             SelectObject(hdcmem, oldBrush);\r
3706         }\r
3707     }\r
3708   }\r
3709   if(logoHeight) {\r
3710         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3711         if(appData.autoLogo) {\r
3712           \r
3713           switch(gameMode) { // pick logos based on game mode\r
3714             case IcsObserving:\r
3715                 whiteLogo = second.programLogo; // ICS logo\r
3716                 blackLogo = second.programLogo;\r
3717             default:\r
3718                 break;\r
3719             case IcsPlayingWhite:\r
3720                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3721                 blackLogo = second.programLogo; // ICS logo\r
3722                 break;\r
3723             case IcsPlayingBlack:\r
3724                 whiteLogo = second.programLogo; // ICS logo\r
3725                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3726                 break;\r
3727             case TwoMachinesPlay:\r
3728                 if(first.twoMachinesColor[0] == 'b') {\r
3729                     whiteLogo = second.programLogo;\r
3730                     blackLogo = first.programLogo;\r
3731                 }\r
3732                 break;\r
3733             case MachinePlaysWhite:\r
3734                 blackLogo = userLogo;\r
3735                 break;\r
3736             case MachinePlaysBlack:\r
3737                 whiteLogo = userLogo;\r
3738                 blackLogo = first.programLogo;\r
3739           }\r
3740         }\r
3741         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3742         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3743   }\r
3744 \r
3745   if( appData.highlightMoveWithArrow ) {\r
3746     DrawArrowHighlight(hdcmem);\r
3747   }\r
3748 \r
3749   DrawCoordsOnDC(hdcmem);\r
3750 \r
3751   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3752                  /* to make sure lastDrawn contains what is actually drawn */\r
3753 \r
3754   /* Put the dragged piece back into place and draw it (out of place!) */\r
3755     if (dragged_piece != EmptySquare) {\r
3756     /* [HGM] or restack */\r
3757     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3758                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3759     else\r
3760     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3761                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3762     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3763     x = dragInfo.pos.x - squareSize / 2;\r
3764     y = dragInfo.pos.y - squareSize / 2;\r
3765     DrawPieceOnDC(hdcmem, dragged_piece,\r
3766                   ((int) dragged_piece < (int) BlackPawn), \r
3767                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3768   }   \r
3769   \r
3770   /* Put the animated piece back into place and draw it */\r
3771   if (animInfo.piece != EmptySquare) {\r
3772     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3773     x = boardRect.left + animInfo.pos.x;\r
3774     y = boardRect.top + animInfo.pos.y;\r
3775     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3776                   ((int) animInfo.piece < (int) BlackPawn),\r
3777                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3778   }\r
3779 \r
3780   /* Release the bufferBitmap by selecting in the old bitmap \r
3781    * and delete the memory DC\r
3782    */\r
3783   SelectObject(hdcmem, oldBitmap);\r
3784   DeleteDC(hdcmem);\r
3785 \r
3786   /* Set clipping on the target DC */\r
3787   if (!fullrepaint) {\r
3788     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3789         RECT rect;\r
3790         GetRgnBox(clips[x], &rect);\r
3791         DeleteObject(clips[x]);\r
3792         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3793                           rect.right + wpMain.width/2, rect.bottom);\r
3794     }\r
3795     SelectClipRgn(hdc, clips[0]);\r
3796     for (x = 1; x < num_clips; x++) {\r
3797       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3798         abort();   // this should never ever happen!\r
3799     } \r
3800   }\r
3801 \r
3802   /* Copy the new bitmap onto the screen in one go.\r
3803    * This way we avoid any flickering\r
3804    */\r
3805   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3806   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3807          boardRect.right - boardRect.left,\r
3808          boardRect.bottom - boardRect.top,\r
3809          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3810   if(saveDiagFlag) { \r
3811     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
3812     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3813 \r
3814     GetObject(bufferBitmap, sizeof(b), &b);\r
3815     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
3816         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3817         bih.biWidth = b.bmWidth;\r
3818         bih.biHeight = b.bmHeight;\r
3819         bih.biPlanes = 1;\r
3820         bih.biBitCount = b.bmBitsPixel;\r
3821         bih.biCompression = 0;\r
3822         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3823         bih.biXPelsPerMeter = 0;\r
3824         bih.biYPelsPerMeter = 0;\r
3825         bih.biClrUsed = 0;\r
3826         bih.biClrImportant = 0;\r
3827 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3828 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3829         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3830 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3831 \r
3832         wb = b.bmWidthBytes;\r
3833         // count colors\r
3834         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3835                 int k = ((int*) pData)[i];\r
3836                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3837                 if(j >= 16) break;\r
3838                 color[j] = k;\r
3839                 if(j >= nrColors) nrColors = j+1;\r
3840         }\r
3841         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3842                 INT p = 0;\r
3843                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3844                     for(w=0; w<(wb>>2); w+=2) {\r
3845                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3846                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3847                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3848                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3849                         pData[p++] = m | j<<4;\r
3850                     }\r
3851                     while(p&3) pData[p++] = 0;\r
3852                 }\r
3853                 fac = 3;\r
3854                 wb = ((wb+31)>>5)<<2;\r
3855         }\r
3856         // write BITMAPFILEHEADER\r
3857         fprintf(diagFile, "BM");\r
3858         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3859         fputDW(diagFile, 0);\r
3860         fputDW(diagFile, 0x36 + (fac?64:0));\r
3861         // write BITMAPINFOHEADER\r
3862         fputDW(diagFile, 40);\r
3863         fputDW(diagFile, b.bmWidth);\r
3864         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3865         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3866         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3867         fputDW(diagFile, 0);\r
3868         fputDW(diagFile, 0);\r
3869         fputDW(diagFile, 0);\r
3870         fputDW(diagFile, 0);\r
3871         fputDW(diagFile, 0);\r
3872         fputDW(diagFile, 0);\r
3873         // write color table\r
3874         if(fac)\r
3875         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3876         // write bitmap data\r
3877         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3878                 fputc(pData[i], diagFile);\r
3879      }\r
3880   }\r
3881 \r
3882   SelectObject(tmphdc, oldBitmap);\r
3883 \r
3884   /* Massive cleanup */\r
3885   for (x = 0; x < num_clips; x++)\r
3886     DeleteObject(clips[x]);\r
3887 \r
3888   DeleteDC(tmphdc);\r
3889   DeleteObject(bufferBitmap);\r
3890 \r
3891   if (releaseDC) \r
3892     ReleaseDC(hwndMain, hdc);\r
3893   \r
3894   if (lastDrawnFlipView != flipView && nr == 0) {\r
3895     if (flipView)\r
3896       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3897     else\r
3898       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3899   }\r
3900 \r
3901 /*  CopyBoard(lastDrawn, board);*/\r
3902   lastDrawnHighlight = highlightInfo;\r
3903   lastDrawnPremove   = premoveHighlightInfo;\r
3904   lastDrawnFlipView = flipView;\r
3905   lastDrawnValid[nr] = 1;\r
3906 }\r
3907 \r
3908 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3909 int\r
3910 SaveDiagram(f)\r
3911      FILE *f;\r
3912 {\r
3913     saveDiagFlag = 1; diagFile = f;\r
3914     HDCDrawPosition(NULL, TRUE, NULL);\r
3915 \r
3916     saveDiagFlag = 0;\r
3917 \r
3918 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
3919     \r
3920     fclose(f);\r
3921     return TRUE;\r
3922 }\r
3923 \r
3924 \r
3925 /*---------------------------------------------------------------------------*\\r
3926 | CLIENT PAINT PROCEDURE\r
3927 |   This is the main event-handler for the WM_PAINT message.\r
3928 |\r
3929 \*---------------------------------------------------------------------------*/\r
3930 VOID\r
3931 PaintProc(HWND hwnd)\r
3932 {\r
3933   HDC         hdc;\r
3934   PAINTSTRUCT ps;\r
3935   HFONT       oldFont;\r
3936 \r
3937   if((hdc = BeginPaint(hwnd, &ps))) {\r
3938     if (IsIconic(hwnd)) {\r
3939       DrawIcon(hdc, 2, 2, iconCurrent);\r
3940     } else {\r
3941       if (!appData.monoMode) {\r
3942         SelectPalette(hdc, hPal, FALSE);\r
3943         RealizePalette(hdc);\r
3944       }\r
3945       HDCDrawPosition(hdc, 1, NULL);\r
3946       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3947         flipView = !flipView; partnerUp = !partnerUp;\r
3948         HDCDrawPosition(hdc, 1, NULL);\r
3949         flipView = !flipView; partnerUp = !partnerUp;\r
3950       }\r
3951       oldFont =\r
3952         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3953       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3954                  ETO_CLIPPED|ETO_OPAQUE,\r
3955                  &messageRect, messageText, strlen(messageText), NULL);\r
3956       SelectObject(hdc, oldFont);\r
3957       DisplayBothClocks();\r
3958     }\r
3959     EndPaint(hwnd,&ps);\r
3960   }\r
3961 \r
3962   return;\r
3963 }\r
3964 \r
3965 \r
3966 /*\r
3967  * If the user selects on a border boundary, return -1; if off the board,\r
3968  *   return -2.  Otherwise map the event coordinate to the square.\r
3969  * The offset boardRect.left or boardRect.top must already have been\r
3970  *   subtracted from x.\r
3971  */\r
3972 int EventToSquare(x, limit)\r
3973      int x, limit;\r
3974 {\r
3975   if (x <= 0)\r
3976     return -2;\r
3977   if (x < lineGap)\r
3978     return -1;\r
3979   x -= lineGap;\r
3980   if ((x % (squareSize + lineGap)) >= squareSize)\r
3981     return -1;\r
3982   x /= (squareSize + lineGap);\r
3983     if (x >= limit)\r
3984     return -2;\r
3985   return x;\r
3986 }\r
3987 \r
3988 typedef struct {\r
3989   char piece;\r
3990   int command;\r
3991   char* name;\r
3992 } DropEnable;\r
3993 \r
3994 DropEnable dropEnables[] = {\r
3995   { 'P', DP_Pawn, N_("Pawn") },\r
3996   { 'N', DP_Knight, N_("Knight") },\r
3997   { 'B', DP_Bishop, N_("Bishop") },\r
3998   { 'R', DP_Rook, N_("Rook") },\r
3999   { 'Q', DP_Queen, N_("Queen") },\r
4000 };\r
4001 \r
4002 VOID\r
4003 SetupDropMenu(HMENU hmenu)\r
4004 {\r
4005   int i, count, enable;\r
4006   char *p;\r
4007   extern char white_holding[], black_holding[];\r
4008   char item[MSG_SIZ];\r
4009 \r
4010   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4011     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4012                dropEnables[i].piece);\r
4013     count = 0;\r
4014     while (p && *p++ == dropEnables[i].piece) count++;\r
4015       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4016     enable = count > 0 || !appData.testLegality\r
4017       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4018                       && !appData.icsActive);\r
4019     ModifyMenu(hmenu, dropEnables[i].command,\r
4020                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4021                dropEnables[i].command, item);\r
4022   }\r
4023 }\r
4024 \r
4025 void DragPieceBegin(int x, int y)\r
4026 {\r
4027       dragInfo.lastpos.x = boardRect.left + x;\r
4028       dragInfo.lastpos.y = boardRect.top + y;\r
4029       dragInfo.from.x = fromX;\r
4030       dragInfo.from.y = fromY;\r
4031       dragInfo.start = dragInfo.from;\r
4032       SetCapture(hwndMain);\r
4033 }\r
4034 \r
4035 void DragPieceEnd(int x, int y)\r
4036 {\r
4037     ReleaseCapture();\r
4038     dragInfo.start.x = dragInfo.start.y = -1;\r
4039     dragInfo.from = dragInfo.start;\r
4040     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4041 }\r
4042 \r
4043 /* Event handler for mouse messages */\r
4044 VOID\r
4045 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4046 {\r
4047   int x, y, menuNr;\r
4048   POINT pt;\r
4049   static int recursive = 0;\r
4050   HMENU hmenu;\r
4051   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4052 \r
4053   if (recursive) {\r
4054     if (message == WM_MBUTTONUP) {\r
4055       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4056          to the middle button: we simulate pressing the left button too!\r
4057          */\r
4058       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4059       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4060     }\r
4061     return;\r
4062   }\r
4063   recursive++;\r
4064   \r
4065   pt.x = LOWORD(lParam);\r
4066   pt.y = HIWORD(lParam);\r
4067   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4068   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4069   if (!flipView && y >= 0) {\r
4070     y = BOARD_HEIGHT - 1 - y;\r
4071   }\r
4072   if (flipView && x >= 0) {\r
4073     x = BOARD_WIDTH - 1 - x;\r
4074   }\r
4075 \r
4076   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4077 \r
4078   switch (message) {\r
4079   case WM_LBUTTONDOWN:\r
4080       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4081         if (gameMode == EditPosition) {\r
4082           SetWhiteToPlayEvent();\r
4083         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4084           AdjustClock(flipClock, -1);\r
4085         } else if (gameMode == IcsPlayingBlack ||\r
4086                    gameMode == MachinePlaysWhite) {\r
4087           CallFlagEvent();\r
4088         }\r
4089       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4090         if (gameMode == EditPosition) {\r
4091           SetBlackToPlayEvent();\r
4092         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4093           AdjustClock(!flipClock, -1);\r
4094         } else if (gameMode == IcsPlayingWhite ||\r
4095                    gameMode == MachinePlaysBlack) {\r
4096           CallFlagEvent();\r
4097         }\r
4098       }\r
4099       dragInfo.start.x = dragInfo.start.y = -1;\r
4100       dragInfo.from = dragInfo.start;\r
4101     if(fromX == -1 && frozen) { // not sure where this is for\r
4102                 fromX = fromY = -1; \r
4103       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4104       break;\r
4105     }\r
4106       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4107       DrawPosition(TRUE, NULL);\r
4108     break;\r
4109 \r
4110   case WM_LBUTTONUP:\r
4111       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4112       DrawPosition(TRUE, NULL);\r
4113     break;\r
4114 \r
4115   case WM_MOUSEMOVE:\r
4116     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4117     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4118     if ((appData.animateDragging || appData.highlightDragging)\r
4119         && (wParam & MK_LBUTTON)\r
4120         && dragInfo.from.x >= 0) \r
4121     {\r
4122       BOOL full_repaint = FALSE;\r
4123 \r
4124       if (appData.animateDragging) {\r
4125         dragInfo.pos = pt;\r
4126       }\r
4127       if (appData.highlightDragging) {\r
4128         SetHighlights(fromX, fromY, x, y);\r
4129         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4130             full_repaint = TRUE;\r
4131         }\r
4132       }\r
4133       \r
4134       DrawPosition( full_repaint, NULL);\r
4135       \r
4136       dragInfo.lastpos = dragInfo.pos;\r
4137     }\r
4138     break;\r
4139 \r
4140   case WM_MOUSEWHEEL: // [DM]\r
4141     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4142        /* Mouse Wheel is being rolled forward\r
4143         * Play moves forward\r
4144         */\r
4145        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4146                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4147        /* Mouse Wheel is being rolled backward\r
4148         * Play moves backward\r
4149         */\r
4150        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4151                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4152     }\r
4153     break;\r
4154 \r
4155   case WM_MBUTTONUP:\r
4156   case WM_RBUTTONUP:\r
4157     ReleaseCapture();\r
4158     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4159     break;\r
4160  \r
4161   case WM_MBUTTONDOWN:\r
4162   case WM_RBUTTONDOWN:\r
4163     ErrorPopDown();\r
4164     ReleaseCapture();\r
4165     fromX = fromY = -1;\r
4166     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4167     dragInfo.start.x = dragInfo.start.y = -1;\r
4168     dragInfo.from = dragInfo.start;\r
4169     dragInfo.lastpos = dragInfo.pos;\r
4170     if (appData.highlightDragging) {\r
4171       ClearHighlights();\r
4172     }\r
4173     if(y == -2) {\r
4174       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4175       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4176           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4177       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4178           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4179       }\r
4180       break;\r
4181     }\r
4182     DrawPosition(TRUE, NULL);\r
4183 \r
4184     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4185     switch (menuNr) {\r
4186     case 0:\r
4187       if (message == WM_MBUTTONDOWN) {\r
4188         buttonCount = 3;  /* even if system didn't think so */\r
4189         if (wParam & MK_SHIFT) \r
4190           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4191         else\r
4192           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4193       } else { /* message == WM_RBUTTONDOWN */\r
4194         /* Just have one menu, on the right button.  Windows users don't\r
4195            think to try the middle one, and sometimes other software steals\r
4196            it, or it doesn't really exist. */\r
4197         if(gameInfo.variant != VariantShogi)\r
4198             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4199         else\r
4200             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4201       }\r
4202       break;\r
4203     case 2:\r
4204       SetCapture(hwndMain);
4205       break;\r
4206     case 1:\r
4207       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4208       SetupDropMenu(hmenu);\r
4209       MenuPopup(hwnd, pt, hmenu, -1);\r
4210     default:\r
4211       break;\r
4212     }\r
4213     break;\r
4214   }\r
4215 \r
4216   recursive--;\r
4217 }\r
4218 \r
4219 /* Preprocess messages for buttons in main window */\r
4220 LRESULT CALLBACK\r
4221 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4222 {\r
4223   int id = GetWindowLong(hwnd, GWL_ID);\r
4224   int i, dir;\r
4225 \r
4226   for (i=0; i<N_BUTTONS; i++) {\r
4227     if (buttonDesc[i].id == id) break;\r
4228   }\r
4229   if (i == N_BUTTONS) return 0;\r
4230   switch (message) {\r
4231   case WM_KEYDOWN:\r
4232     switch (wParam) {\r
4233     case VK_LEFT:\r
4234     case VK_RIGHT:\r
4235       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4236       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4237       return TRUE;\r
4238     }\r
4239     break;\r
4240   case WM_CHAR:\r
4241     switch (wParam) {\r
4242     case '\r':\r
4243       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4244       return TRUE;\r
4245     default:\r
4246       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4247         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4248         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4249         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4250         SetFocus(h);\r
4251         SendMessage(h, WM_CHAR, wParam, lParam);\r
4252         return TRUE;\r
4253       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4254         PopUpMoveDialog((char)wParam);\r
4255       }\r
4256       break;\r
4257     }\r
4258     break;\r
4259   }\r
4260   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4261 }\r
4262 \r
4263 /* Process messages for Promotion dialog box */\r
4264 LRESULT CALLBACK\r
4265 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4266 {\r
4267   char promoChar;\r
4268 \r
4269   switch (message) {\r
4270   case WM_INITDIALOG: /* message: initialize dialog box */\r
4271     /* Center the dialog over the application window */\r
4272     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4273     Translate(hDlg, DLG_PromotionKing);\r
4274     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4275       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4276        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4277                SW_SHOW : SW_HIDE);\r
4278     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4279     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4280        ((PieceToChar(WhiteAngel) >= 'A' &&\r
4281          PieceToChar(WhiteAngel) != '~') ||\r
4282         (PieceToChar(BlackAngel) >= 'A' &&\r
4283          PieceToChar(BlackAngel) != '~')   ) ?\r
4284                SW_SHOW : SW_HIDE);\r
4285     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4286        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
4287          PieceToChar(WhiteMarshall) != '~') ||\r
4288         (PieceToChar(BlackMarshall) >= 'A' &&\r
4289          PieceToChar(BlackMarshall) != '~')   ) ?\r
4290                SW_SHOW : SW_HIDE);\r
4291     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4292     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4293        gameInfo.variant != VariantShogi ?\r
4294                SW_SHOW : SW_HIDE);\r
4295     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4296        gameInfo.variant != VariantShogi ?\r
4297                SW_SHOW : SW_HIDE);\r
4298     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
4299        gameInfo.variant == VariantShogi ?\r
4300                SW_SHOW : SW_HIDE);\r
4301     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
4302        gameInfo.variant == VariantShogi ?\r
4303                SW_SHOW : SW_HIDE);\r
4304     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4305        gameInfo.variant == VariantSuper ?\r
4306                SW_SHOW : SW_HIDE);\r
4307     return TRUE;\r
4308 \r
4309   case WM_COMMAND: /* message: received a command */\r
4310     switch (LOWORD(wParam)) {\r
4311     case IDCANCEL:\r
4312       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4313       ClearHighlights();\r
4314       DrawPosition(FALSE, NULL);\r
4315       return TRUE;\r
4316     case PB_King:\r
4317       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4318       break;\r
4319     case PB_Queen:\r
4320       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
4321       break;\r
4322     case PB_Rook:\r
4323       promoChar = PieceToChar(BlackRook);\r
4324       break;\r
4325     case PB_Bishop:\r
4326       promoChar = PieceToChar(BlackBishop);\r
4327       break;\r
4328     case PB_Chancellor:\r
4329       promoChar = PieceToChar(BlackMarshall);\r
4330       break;\r
4331     case PB_Archbishop:\r
4332       promoChar = PieceToChar(BlackAngel);\r
4333       break;\r
4334     case PB_Knight:\r
4335       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
4336       break;\r
4337     default:\r
4338       return FALSE;\r
4339     }\r
4340     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4341     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
4342        only show the popup when we are already sure the move is valid or\r
4343        legal. We pass a faulty move type, but the kludge is that FinishMove\r
4344        will figure out it is a promotion from the promoChar. */\r
4345     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4346     fromX = fromY = -1;\r
4347     if (!appData.highlightLastMove) {\r
4348       ClearHighlights();\r
4349       DrawPosition(FALSE, NULL);\r
4350     }\r
4351     return TRUE;\r
4352   }\r
4353   return FALSE;\r
4354 }\r
4355 \r
4356 /* Pop up promotion dialog */\r
4357 VOID\r
4358 PromotionPopup(HWND hwnd)\r
4359 {\r
4360   FARPROC lpProc;\r
4361 \r
4362   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4363   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4364     hwnd, (DLGPROC)lpProc);\r
4365   FreeProcInstance(lpProc);\r
4366 }\r
4367 \r
4368 void\r
4369 PromotionPopUp()\r
4370 {\r
4371   DrawPosition(TRUE, NULL);\r
4372   PromotionPopup(hwndMain);\r
4373 }\r
4374 \r
4375 /* Toggle ShowThinking */\r
4376 VOID\r
4377 ToggleShowThinking()\r
4378 {\r
4379   appData.showThinking = !appData.showThinking;\r
4380   ShowThinkingEvent();\r
4381 }\r
4382 \r
4383 VOID\r
4384 LoadGameDialog(HWND hwnd, char* title)\r
4385 {\r
4386   UINT number = 0;\r
4387   FILE *f;\r
4388   char fileTitle[MSG_SIZ];\r
4389   f = OpenFileDialog(hwnd, "rb", "",\r
4390                      appData.oldSaveStyle ? "gam" : "pgn",\r
4391                      GAME_FILT,\r
4392                      title, &number, fileTitle, NULL);\r
4393   if (f != NULL) {\r
4394     cmailMsgLoaded = FALSE;\r
4395     if (number == 0) {\r
4396       int error = GameListBuild(f);\r
4397       if (error) {\r
4398         DisplayError(_("Cannot build game list"), error);\r
4399       } else if (!ListEmpty(&gameList) &&\r
4400                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4401         GameListPopUp(f, fileTitle);\r
4402         return;\r
4403       }\r
4404       GameListDestroy();\r
4405       number = 1;\r
4406     }\r
4407     LoadGame(f, number, fileTitle, FALSE);\r
4408   }\r
4409 }\r
4410 \r
4411 int get_term_width()\r
4412 {\r
4413     HDC hdc;\r
4414     TEXTMETRIC tm;\r
4415     RECT rc;\r
4416     HFONT hfont, hold_font;\r
4417     LOGFONT lf;\r
4418     HWND hText;\r
4419 \r
4420     if (hwndConsole)\r
4421         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4422     else\r
4423         return 79;\r
4424 \r
4425     // get the text metrics\r
4426     hdc = GetDC(hText);\r
4427     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4428     if (consoleCF.dwEffects & CFE_BOLD)\r
4429         lf.lfWeight = FW_BOLD;\r
4430     if (consoleCF.dwEffects & CFE_ITALIC)\r
4431         lf.lfItalic = TRUE;\r
4432     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4433         lf.lfStrikeOut = TRUE;\r
4434     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4435         lf.lfUnderline = TRUE;\r
4436     hfont = CreateFontIndirect(&lf);\r
4437     hold_font = SelectObject(hdc, hfont);\r
4438     GetTextMetrics(hdc, &tm);\r
4439     SelectObject(hdc, hold_font);\r
4440     DeleteObject(hfont);\r
4441     ReleaseDC(hText, hdc);\r
4442 \r
4443     // get the rectangle\r
4444     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4445 \r
4446     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4447 }\r
4448 \r
4449 void UpdateICSWidth(HWND hText)\r
4450 {\r
4451     LONG old_width, new_width;\r
4452 \r
4453     new_width = get_term_width(hText, FALSE);\r
4454     old_width = GetWindowLong(hText, GWL_USERDATA);\r
4455     if (new_width != old_width)\r
4456     {\r
4457         ics_update_width(new_width);\r
4458         SetWindowLong(hText, GWL_USERDATA, new_width);\r
4459     }\r
4460 }\r
4461 \r
4462 VOID\r
4463 ChangedConsoleFont()\r
4464 {\r
4465   CHARFORMAT cfmt;\r
4466   CHARRANGE tmpsel, sel;\r
4467   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4468   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4469   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4470   PARAFORMAT paraf;\r
4471 \r
4472   cfmt.cbSize = sizeof(CHARFORMAT);\r
4473   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4474     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4475                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4476   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4477    * size.  This was undocumented in the version of MSVC++ that I had\r
4478    * when I wrote the code, but is apparently documented now.\r
4479    */\r
4480   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4481   cfmt.bCharSet = f->lf.lfCharSet;\r
4482   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4483   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4484   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4485   /* Why are the following seemingly needed too? */\r
4486   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4487   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4488   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4489   tmpsel.cpMin = 0;\r
4490   tmpsel.cpMax = -1; /*999999?*/\r
4491   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4492   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4493   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4494    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4495    */\r
4496   paraf.cbSize = sizeof(paraf);\r
4497   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4498   paraf.dxStartIndent = 0;\r
4499   paraf.dxOffset = WRAP_INDENT;\r
4500   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4501   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4502   UpdateICSWidth(hText);\r
4503 }\r
4504 \r
4505 /*---------------------------------------------------------------------------*\\r
4506  *\r
4507  * Window Proc for main window\r
4508  *\r
4509 \*---------------------------------------------------------------------------*/\r
4510 \r
4511 /* Process messages for main window, etc. */\r
4512 LRESULT CALLBACK\r
4513 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4514 {\r
4515   FARPROC lpProc;\r
4516   int wmId, wmEvent;\r
4517   char *defName;\r
4518   FILE *f;\r
4519   UINT number;\r
4520   char fileTitle[MSG_SIZ];\r
4521   char buf[MSG_SIZ];\r
4522   static SnapData sd;\r
4523 \r
4524   switch (message) {\r
4525 \r
4526   case WM_PAINT: /* message: repaint portion of window */\r
4527     PaintProc(hwnd);\r
4528     break;\r
4529 \r
4530   case WM_ERASEBKGND:\r
4531     if (IsIconic(hwnd)) {\r
4532       /* Cheat; change the message */\r
4533       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4534     } else {\r
4535       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4536     }\r
4537     break;\r
4538 \r
4539   case WM_LBUTTONDOWN:\r
4540   case WM_MBUTTONDOWN:\r
4541   case WM_RBUTTONDOWN:\r
4542   case WM_LBUTTONUP:\r
4543   case WM_MBUTTONUP:\r
4544   case WM_RBUTTONUP:\r
4545   case WM_MOUSEMOVE:\r
4546   case WM_MOUSEWHEEL:\r
4547     MouseEvent(hwnd, message, wParam, lParam);\r
4548     break;\r
4549 \r
4550   JAWS_KB_NAVIGATION\r
4551 \r
4552   case WM_CHAR:\r
4553     \r
4554     JAWS_ALT_INTERCEPT\r
4555 \r
4556     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4557         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4558         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4559         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4560         SetFocus(h);\r
4561         SendMessage(h, message, wParam, lParam);\r
4562     } else if(lParam != KF_REPEAT) {\r
4563         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4564                 PopUpMoveDialog((char)wParam);\r
4565         } else if((char)wParam == 003) CopyGameToClipboard();\r
4566          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4567     }\r
4568 \r
4569     break;\r
4570 \r
4571   case WM_PALETTECHANGED:\r
4572     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4573       int nnew;\r
4574       HDC hdc = GetDC(hwndMain);\r
4575       SelectPalette(hdc, hPal, TRUE);\r
4576       nnew = RealizePalette(hdc);\r
4577       if (nnew > 0) {\r
4578         paletteChanged = TRUE;\r
4579         InvalidateRect(hwnd, &boardRect, FALSE);\r
4580       }\r
4581       ReleaseDC(hwnd, hdc);\r
4582     }\r
4583     break;\r
4584 \r
4585   case WM_QUERYNEWPALETTE:\r
4586     if (!appData.monoMode /*&& paletteChanged*/) {\r
4587       int nnew;\r
4588       HDC hdc = GetDC(hwndMain);\r
4589       paletteChanged = FALSE;\r
4590       SelectPalette(hdc, hPal, FALSE);\r
4591       nnew = RealizePalette(hdc);\r
4592       if (nnew > 0) {\r
4593         InvalidateRect(hwnd, &boardRect, FALSE);\r
4594       }\r
4595       ReleaseDC(hwnd, hdc);\r
4596       return TRUE;\r
4597     }\r
4598     return FALSE;\r
4599 \r
4600   case WM_COMMAND: /* message: command from application menu */\r
4601     wmId    = LOWORD(wParam);\r
4602     wmEvent = HIWORD(wParam);\r
4603 \r
4604     switch (wmId) {\r
4605     case IDM_NewGame:\r
4606       ResetGameEvent();\r
4607       SAY("new game enter a move to play against the computer with white");\r
4608       break;\r
4609 \r
4610     case IDM_NewGameFRC:\r
4611       if( NewGameFRC() == 0 ) {\r
4612         ResetGameEvent();\r
4613       }\r
4614       break;\r
4615 \r
4616     case IDM_NewVariant:\r
4617       NewVariantPopup(hwnd);\r
4618       break;\r
4619 \r
4620     case IDM_LoadGame:\r
4621       LoadGameDialog(hwnd, _("Load Game from File"));\r
4622       break;\r
4623 \r
4624     case IDM_LoadNextGame:\r
4625       ReloadGame(1);\r
4626       break;\r
4627 \r
4628     case IDM_LoadPrevGame:\r
4629       ReloadGame(-1);\r
4630       break;\r
4631 \r
4632     case IDM_ReloadGame:\r
4633       ReloadGame(0);\r
4634       break;\r
4635 \r
4636     case IDM_LoadPosition:\r
4637       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4638         Reset(FALSE, TRUE);\r
4639       }\r
4640       number = 1;\r
4641       f = OpenFileDialog(hwnd, "rb", "",\r
4642                          appData.oldSaveStyle ? "pos" : "fen",\r
4643                          POSITION_FILT,\r
4644                          _("Load Position from File"), &number, fileTitle, NULL);\r
4645       if (f != NULL) {\r
4646         LoadPosition(f, number, fileTitle);\r
4647       }\r
4648       break;\r
4649 \r
4650     case IDM_LoadNextPosition:\r
4651       ReloadPosition(1);\r
4652       break;\r
4653 \r
4654     case IDM_LoadPrevPosition:\r
4655       ReloadPosition(-1);\r
4656       break;\r
4657 \r
4658     case IDM_ReloadPosition:\r
4659       ReloadPosition(0);\r
4660       break;\r
4661 \r
4662     case IDM_SaveGame:\r
4663       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4664       f = OpenFileDialog(hwnd, "a", defName,\r
4665                          appData.oldSaveStyle ? "gam" : "pgn",\r
4666                          GAME_FILT,\r
4667                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4668       if (f != NULL) {\r
4669         SaveGame(f, 0, "");\r
4670       }\r
4671       break;\r
4672 \r
4673     case IDM_SavePosition:\r
4674       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4675       f = OpenFileDialog(hwnd, "a", defName,\r
4676                          appData.oldSaveStyle ? "pos" : "fen",\r
4677                          POSITION_FILT,\r
4678                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4679       if (f != NULL) {\r
4680         SavePosition(f, 0, "");\r
4681       }\r
4682       break;\r
4683 \r
4684     case IDM_SaveDiagram:\r
4685       defName = "diagram";\r
4686       f = OpenFileDialog(hwnd, "wb", defName,\r
4687                          "bmp",\r
4688                          DIAGRAM_FILT,\r
4689                          "Save Diagram to File", NULL, fileTitle, NULL);\r
4690       if (f != NULL) {\r
4691         SaveDiagram(f);\r
4692       }\r
4693       break;\r
4694 \r
4695     case IDM_CopyGame:\r
4696       CopyGameToClipboard();\r
4697       break;\r
4698 \r
4699     case IDM_PasteGame:\r
4700       PasteGameFromClipboard();\r
4701       break;\r
4702 \r
4703     case IDM_CopyGameListToClipboard:\r
4704       CopyGameListToClipboard();\r
4705       break;\r
4706 \r
4707     /* [AS] Autodetect FEN or PGN data */\r
4708     case IDM_PasteAny:\r
4709       PasteGameOrFENFromClipboard();\r
4710       break;\r
4711 \r
4712     /* [AS] Move history */\r
4713     case IDM_ShowMoveHistory:\r
4714         if( MoveHistoryIsUp() ) {\r
4715             MoveHistoryPopDown();\r
4716         }\r
4717         else {\r
4718             MoveHistoryPopUp();\r
4719         }\r
4720         break;\r
4721 \r
4722     /* [AS] Eval graph */\r
4723     case IDM_ShowEvalGraph:\r
4724         if( EvalGraphIsUp() ) {\r
4725             EvalGraphPopDown();\r
4726         }\r
4727         else {\r
4728             EvalGraphPopUp();\r
4729             SetFocus(hwndMain);\r
4730         }\r
4731         break;\r
4732 \r
4733     /* [AS] Engine output */\r
4734     case IDM_ShowEngineOutput:\r
4735         if( EngineOutputIsUp() ) {\r
4736             EngineOutputPopDown();\r
4737         }\r
4738         else {\r
4739             EngineOutputPopUp();\r
4740         }\r
4741         break;\r
4742 \r
4743     /* [AS] User adjudication */\r
4744     case IDM_UserAdjudication_White:\r
4745         UserAdjudicationEvent( +1 );\r
4746         break;\r
4747 \r
4748     case IDM_UserAdjudication_Black:\r
4749         UserAdjudicationEvent( -1 );\r
4750         break;\r
4751 \r
4752     case IDM_UserAdjudication_Draw:\r
4753         UserAdjudicationEvent( 0 );\r
4754         break;\r
4755 \r
4756     /* [AS] Game list options dialog */\r
4757     case IDM_GameListOptions:\r
4758       GameListOptions();\r
4759       break;\r
4760 \r
4761     case IDM_NewChat:\r
4762       ChatPopUp(NULL);\r
4763       break;\r
4764 \r
4765     case IDM_CopyPosition:\r
4766       CopyFENToClipboard();\r
4767       break;\r
4768 \r
4769     case IDM_PastePosition:\r
4770       PasteFENFromClipboard();\r
4771       break;\r
4772 \r
4773     case IDM_MailMove:\r
4774       MailMoveEvent();\r
4775       break;\r
4776 \r
4777     case IDM_ReloadCMailMsg:\r
4778       Reset(TRUE, TRUE);\r
4779       ReloadCmailMsgEvent(FALSE);\r
4780       break;\r
4781 \r
4782     case IDM_Minimize:\r
4783       ShowWindow(hwnd, SW_MINIMIZE);\r
4784       break;\r
4785 \r
4786     case IDM_Exit:\r
4787       ExitEvent(0);\r
4788       break;\r
4789 \r
4790     case IDM_MachineWhite:\r
4791       MachineWhiteEvent();\r
4792       /*\r
4793        * refresh the tags dialog only if it's visible\r
4794        */\r
4795       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4796           char *tags;\r
4797           tags = PGNTags(&gameInfo);\r
4798           TagsPopUp(tags, CmailMsg());\r
4799           free(tags);\r
4800       }\r
4801       SAY("computer starts playing white");\r
4802       break;\r
4803 \r
4804     case IDM_MachineBlack:\r
4805       MachineBlackEvent();\r
4806       /*\r
4807        * refresh the tags dialog only if it's visible\r
4808        */\r
4809       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4810           char *tags;\r
4811           tags = PGNTags(&gameInfo);\r
4812           TagsPopUp(tags, CmailMsg());\r
4813           free(tags);\r
4814       }\r
4815       SAY("computer starts playing black");\r
4816       break;\r
4817 \r
4818     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4819       if(gameMode != BeginningOfGame) break; // allow menu item to remain enabled for better mode highligting\r
4820       matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)\r
4821       appData.matchGames = appData.defaultMatchGames;\r
4822       matchGame = 1;\r
4823 \r
4824     case IDM_TwoMachines:\r
4825       TwoMachinesEvent();\r
4826       /*\r
4827        * refresh the tags dialog only if it's visible\r
4828        */\r
4829       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4830           char *tags;\r
4831           tags = PGNTags(&gameInfo);\r
4832           TagsPopUp(tags, CmailMsg());\r
4833           free(tags);\r
4834       }\r
4835       SAY("computer starts playing both sides");\r
4836       break;\r
4837 \r
4838     case IDM_AnalysisMode:\r
4839       if (!first.analysisSupport) {\r
4840         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4841         DisplayError(buf, 0);\r
4842       } else {\r
4843         SAY("analyzing current position");\r
4844         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4845         if (appData.icsActive) {\r
4846                if (gameMode != IcsObserving) {\r
4847                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4848                        DisplayError(buf, 0);\r
4849                        /* secure check */\r
4850                        if (appData.icsEngineAnalyze) {\r
4851                                if (appData.debugMode) \r
4852                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4853                                ExitAnalyzeMode();\r
4854                                ModeHighlight();\r
4855                                break;\r
4856                        }\r
4857                        break;\r
4858                } else {\r
4859                        /* if enable, user want disable icsEngineAnalyze */\r
4860                        if (appData.icsEngineAnalyze) {\r
4861                                ExitAnalyzeMode();\r
4862                                ModeHighlight();\r
4863                                break;\r
4864                        }\r
4865                        appData.icsEngineAnalyze = TRUE;\r
4866                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4867                }\r
4868         } \r
4869         if (!appData.showThinking) ToggleShowThinking();\r
4870         AnalyzeModeEvent();\r
4871       }\r
4872       break;\r
4873 \r
4874     case IDM_AnalyzeFile:\r
4875       if (!first.analysisSupport) {\r
4876         char buf[MSG_SIZ];\r
4877           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4878         DisplayError(buf, 0);\r
4879       } else {\r
4880         if (!appData.showThinking) ToggleShowThinking();\r
4881         AnalyzeFileEvent();\r
4882         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4883         AnalysisPeriodicEvent(1);\r
4884       }\r
4885       break;\r
4886 \r
4887     case IDM_IcsClient:\r
4888       IcsClientEvent();\r
4889       break;\r
4890 \r
4891     case IDM_EditGame:\r
4892       EditGameEvent();\r
4893       SAY("edit game");\r
4894       break;\r
4895 \r
4896     case IDM_EditPosition:\r
4897       EditPositionEvent();\r
4898       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4899       break;\r
4900 \r
4901     case IDM_Training:\r
4902       TrainingEvent();\r
4903       break;\r
4904 \r
4905     case IDM_ShowGameList:\r
4906       ShowGameListProc();\r
4907       break;\r
4908 \r
4909     case IDM_EditTags:\r
4910       EditTagsProc();\r
4911       break;\r
4912 \r
4913     case IDM_EditComment:\r
4914       if (commentUp && editComment) {\r
4915         CommentPopDown();\r
4916       } else {\r
4917         EditCommentEvent();\r
4918       }\r
4919       break;\r
4920 \r
4921     case IDM_Pause:\r
4922       PauseEvent();\r
4923       break;\r
4924 \r
4925     case IDM_Accept:\r
4926       AcceptEvent();\r
4927       break;\r
4928 \r
4929     case IDM_Decline:\r
4930       DeclineEvent();\r
4931       break;\r
4932 \r
4933     case IDM_Rematch:\r
4934       RematchEvent();\r
4935       break;\r
4936 \r
4937     case IDM_CallFlag:\r
4938       CallFlagEvent();\r
4939       break;\r
4940 \r
4941     case IDM_Draw:\r
4942       DrawEvent();\r
4943       break;\r
4944 \r
4945     case IDM_Adjourn:\r
4946       AdjournEvent();\r
4947       break;\r
4948 \r
4949     case IDM_Abort:\r
4950       AbortEvent();\r
4951       break;\r
4952 \r
4953     case IDM_Resign:\r
4954       ResignEvent();\r
4955       break;\r
4956 \r
4957     case IDM_StopObserving:\r
4958       StopObservingEvent();\r
4959       break;\r
4960 \r
4961     case IDM_StopExamining:\r
4962       StopExaminingEvent();\r
4963       break;\r
4964 \r
4965     case IDM_Upload:\r
4966       UploadGameEvent();\r
4967       break;\r
4968 \r
4969     case IDM_TypeInMove:\r
4970       PopUpMoveDialog('\000');\r
4971       break;\r
4972 \r
4973     case IDM_TypeInName:\r
4974       PopUpNameDialog('\000');\r
4975       break;\r
4976 \r
4977     case IDM_Backward:\r
4978       BackwardEvent();\r
4979       SetFocus(hwndMain);\r
4980       break;\r
4981 \r
4982     JAWS_MENU_ITEMS\r
4983 \r
4984     case IDM_Forward:\r
4985       ForwardEvent();\r
4986       SetFocus(hwndMain);\r
4987       break;\r
4988 \r
4989     case IDM_ToStart:\r
4990       ToStartEvent();\r
4991       SetFocus(hwndMain);\r
4992       break;\r
4993 \r
4994     case IDM_ToEnd:\r
4995       ToEndEvent();\r
4996       SetFocus(hwndMain);\r
4997       break;\r
4998 \r
4999     case IDM_Revert:\r
5000       RevertEvent(FALSE);\r
5001       break;\r
5002 \r
5003     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5004       RevertEvent(TRUE);\r
5005       break;\r
5006 \r
5007     case IDM_TruncateGame:\r
5008       TruncateGameEvent();\r
5009       break;\r
5010 \r
5011     case IDM_MoveNow:\r
5012       MoveNowEvent();\r
5013       break;\r
5014 \r
5015     case IDM_RetractMove:\r
5016       RetractMoveEvent();\r
5017       break;\r
5018 \r
5019     case IDM_FlipView:\r
5020       flipView = !flipView;\r
5021       DrawPosition(FALSE, NULL);\r
5022       break;\r
5023 \r
5024     case IDM_FlipClock:\r
5025       flipClock = !flipClock;\r
5026       DisplayBothClocks();\r
5027       DrawPosition(FALSE, NULL);\r
5028       break;\r
5029 \r
5030     case IDM_MuteSounds:\r
5031       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5032       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5033                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5034       break;\r
5035 \r
5036     case IDM_GeneralOptions:\r
5037       GeneralOptionsPopup(hwnd);\r
5038       DrawPosition(TRUE, NULL);\r
5039       break;\r
5040 \r
5041     case IDM_BoardOptions:\r
5042       BoardOptionsPopup(hwnd);\r
5043       break;\r
5044 \r
5045     case IDM_EnginePlayOptions:\r
5046       EnginePlayOptionsPopup(hwnd);\r
5047       break;\r
5048 \r
5049     case IDM_Engine1Options:\r
5050       EngineOptionsPopup(hwnd, &first);\r
5051       break;\r
5052 \r
5053     case IDM_Engine2Options:\r
5054       EngineOptionsPopup(hwnd, &second);\r
5055       break;\r
5056 \r
5057     case IDM_OptionsUCI:\r
5058       UciOptionsPopup(hwnd);\r
5059       break;\r
5060 \r
5061     case IDM_IcsOptions:\r
5062       IcsOptionsPopup(hwnd);\r
5063       break;\r
5064 \r
5065     case IDM_Fonts:\r
5066       FontsOptionsPopup(hwnd);\r
5067       break;\r
5068 \r
5069     case IDM_Sounds:\r
5070       SoundOptionsPopup(hwnd);\r
5071       break;\r
5072 \r
5073     case IDM_CommPort:\r
5074       CommPortOptionsPopup(hwnd);\r
5075       break;\r
5076 \r
5077     case IDM_LoadOptions:\r
5078       LoadOptionsPopup(hwnd);\r
5079       break;\r
5080 \r
5081     case IDM_SaveOptions:\r
5082       SaveOptionsPopup(hwnd);\r
5083       break;\r
5084 \r
5085     case IDM_TimeControl:\r
5086       TimeControlOptionsPopup(hwnd);\r
5087       break;\r
5088 \r
5089     case IDM_SaveSettings:\r
5090       SaveSettings(settingsFileName);\r
5091       break;\r
5092 \r
5093     case IDM_SaveSettingsOnExit:\r
5094       saveSettingsOnExit = !saveSettingsOnExit;\r
5095       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5096                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5097                                          MF_CHECKED : MF_UNCHECKED));\r
5098       break;\r
5099 \r
5100     case IDM_Hint:\r
5101       HintEvent();\r
5102       break;\r
5103 \r
5104     case IDM_Book:\r
5105       BookEvent();\r
5106       break;\r
5107 \r
5108     case IDM_AboutGame:\r
5109       AboutGameEvent();\r
5110       break;\r
5111 \r
5112     case IDM_Debug:\r
5113       appData.debugMode = !appData.debugMode;\r
5114       if (appData.debugMode) {\r
5115         char dir[MSG_SIZ];\r
5116         GetCurrentDirectory(MSG_SIZ, dir);\r
5117         SetCurrentDirectory(installDir);\r
5118         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5119         SetCurrentDirectory(dir);\r
5120         setbuf(debugFP, NULL);\r
5121       } else {\r
5122         fclose(debugFP);\r
5123         debugFP = NULL;\r
5124       }\r
5125       break;\r
5126 \r
5127     case IDM_HELPCONTENTS:\r
5128       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5129           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5130           MessageBox (GetFocus(),\r
5131                     _("Unable to activate help"),\r
5132                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5133       }\r
5134       break;\r
5135 \r
5136     case IDM_HELPSEARCH:\r
5137         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5138             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5139         MessageBox (GetFocus(),\r
5140                     _("Unable to activate help"),\r
5141                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5142       }\r
5143       break;\r
5144 \r
5145     case IDM_HELPHELP:\r
5146       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5147         MessageBox (GetFocus(),\r
5148                     _("Unable to activate help"),\r
5149                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5150       }\r
5151       break;\r
5152 \r
5153     case IDM_ABOUT:\r
5154       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5155       DialogBox(hInst, \r
5156         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5157         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5158       FreeProcInstance(lpProc);\r
5159       break;\r
5160 \r
5161     case IDM_DirectCommand1:\r
5162       AskQuestionEvent(_("Direct Command"),\r
5163                        _("Send to chess program:"), "", "1");\r
5164       break;\r
5165     case IDM_DirectCommand2:\r
5166       AskQuestionEvent(_("Direct Command"),\r
5167                        _("Send to second chess program:"), "", "2");\r
5168       break;\r
5169 \r
5170     case EP_WhitePawn:\r
5171       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5172       fromX = fromY = -1;\r
5173       break;\r
5174 \r
5175     case EP_WhiteKnight:\r
5176       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5177       fromX = fromY = -1;\r
5178       break;\r
5179 \r
5180     case EP_WhiteBishop:\r
5181       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5182       fromX = fromY = -1;\r
5183       break;\r
5184 \r
5185     case EP_WhiteRook:\r
5186       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5187       fromX = fromY = -1;\r
5188       break;\r
5189 \r
5190     case EP_WhiteQueen:\r
5191       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5192       fromX = fromY = -1;\r
5193       break;\r
5194 \r
5195     case EP_WhiteFerz:\r
5196       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5197       fromX = fromY = -1;\r
5198       break;\r
5199 \r
5200     case EP_WhiteWazir:\r
5201       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5202       fromX = fromY = -1;\r
5203       break;\r
5204 \r
5205     case EP_WhiteAlfil:\r
5206       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5207       fromX = fromY = -1;\r
5208       break;\r
5209 \r
5210     case EP_WhiteCannon:\r
5211       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5212       fromX = fromY = -1;\r
5213       break;\r
5214 \r
5215     case EP_WhiteCardinal:\r
5216       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5217       fromX = fromY = -1;\r
5218       break;\r
5219 \r
5220     case EP_WhiteMarshall:\r
5221       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5222       fromX = fromY = -1;\r
5223       break;\r
5224 \r
5225     case EP_WhiteKing:\r
5226       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5227       fromX = fromY = -1;\r
5228       break;\r
5229 \r
5230     case EP_BlackPawn:\r
5231       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5232       fromX = fromY = -1;\r
5233       break;\r
5234 \r
5235     case EP_BlackKnight:\r
5236       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5237       fromX = fromY = -1;\r
5238       break;\r
5239 \r
5240     case EP_BlackBishop:\r
5241       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5242       fromX = fromY = -1;\r
5243       break;\r
5244 \r
5245     case EP_BlackRook:\r
5246       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5247       fromX = fromY = -1;\r
5248       break;\r
5249 \r
5250     case EP_BlackQueen:\r
5251       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5252       fromX = fromY = -1;\r
5253       break;\r
5254 \r
5255     case EP_BlackFerz:\r
5256       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5257       fromX = fromY = -1;\r
5258       break;\r
5259 \r
5260     case EP_BlackWazir:\r
5261       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5262       fromX = fromY = -1;\r
5263       break;\r
5264 \r
5265     case EP_BlackAlfil:\r
5266       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5267       fromX = fromY = -1;\r
5268       break;\r
5269 \r
5270     case EP_BlackCannon:\r
5271       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5272       fromX = fromY = -1;\r
5273       break;\r
5274 \r
5275     case EP_BlackCardinal:\r
5276       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5277       fromX = fromY = -1;\r
5278       break;\r
5279 \r
5280     case EP_BlackMarshall:\r
5281       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5282       fromX = fromY = -1;\r
5283       break;\r
5284 \r
5285     case EP_BlackKing:\r
5286       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5287       fromX = fromY = -1;\r
5288       break;\r
5289 \r
5290     case EP_EmptySquare:\r
5291       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5292       fromX = fromY = -1;\r
5293       break;\r
5294 \r
5295     case EP_ClearBoard:\r
5296       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5297       fromX = fromY = -1;\r
5298       break;\r
5299 \r
5300     case EP_White:\r
5301       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5302       fromX = fromY = -1;\r
5303       break;\r
5304 \r
5305     case EP_Black:\r
5306       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5307       fromX = fromY = -1;\r
5308       break;\r
5309 \r
5310     case EP_Promote:\r
5311       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5312       fromX = fromY = -1;\r
5313       break;\r
5314 \r
5315     case EP_Demote:\r
5316       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5317       fromX = fromY = -1;\r
5318       break;\r
5319 \r
5320     case DP_Pawn:\r
5321       DropMenuEvent(WhitePawn, fromX, fromY);\r
5322       fromX = fromY = -1;\r
5323       break;\r
5324 \r
5325     case DP_Knight:\r
5326       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5327       fromX = fromY = -1;\r
5328       break;\r
5329 \r
5330     case DP_Bishop:\r
5331       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5332       fromX = fromY = -1;\r
5333       break;\r
5334 \r
5335     case DP_Rook:\r
5336       DropMenuEvent(WhiteRook, fromX, fromY);\r
5337       fromX = fromY = -1;\r
5338       break;\r
5339 \r
5340     case DP_Queen:\r
5341       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5342       fromX = fromY = -1;\r
5343       break;\r
5344 \r
5345     case IDM_English:\r
5346       barbaric = 0;\r
5347       TranslateMenus(0);\r
5348       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5349       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5350       lastChecked = wmId;\r
5351       break;\r
5352 \r
5353     default:\r
5354       if(wmId > IDM_English && wmId < IDM_English+5) {\r
5355           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5356           TranslateMenus(0);\r
5357           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5358           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5359           lastChecked = wmId;\r
5360           break;\r
5361       }\r
5362       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5363     }\r
5364     break;\r
5365 \r
5366   case WM_TIMER:\r
5367     switch (wParam) {\r
5368     case CLOCK_TIMER_ID:\r
5369       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5370       clockTimerEvent = 0;\r
5371       DecrementClocks(); /* call into back end */\r
5372       break;\r
5373     case LOAD_GAME_TIMER_ID:\r
5374       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5375       loadGameTimerEvent = 0;\r
5376       AutoPlayGameLoop(); /* call into back end */\r
5377       break;\r
5378     case ANALYSIS_TIMER_ID:\r
5379       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5380                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5381         AnalysisPeriodicEvent(0);\r
5382       } else {\r
5383         KillTimer(hwnd, analysisTimerEvent);\r
5384         analysisTimerEvent = 0;\r
5385       }\r
5386       break;\r
5387     case DELAYED_TIMER_ID:\r
5388       KillTimer(hwnd, delayedTimerEvent);\r
5389       delayedTimerEvent = 0;\r
5390       delayedTimerCallback();\r
5391       break;\r
5392     }\r
5393     break;\r
5394 \r
5395   case WM_USER_Input:\r
5396     InputEvent(hwnd, message, wParam, lParam);\r
5397     break;\r
5398 \r
5399   /* [AS] Also move "attached" child windows */\r
5400   case WM_WINDOWPOSCHANGING:\r
5401 \r
5402     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5403         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5404 \r
5405         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5406             /* Window is moving */\r
5407             RECT rcMain;\r
5408 \r
5409 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5410             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5411             rcMain.right  = wpMain.x + wpMain.width;\r
5412             rcMain.top    = wpMain.y;\r
5413             rcMain.bottom = wpMain.y + wpMain.height;\r
5414             \r
5415             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5416             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5417             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5418             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5419             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5420             wpMain.x = lpwp->x;\r
5421             wpMain.y = lpwp->y;\r
5422         }\r
5423     }\r
5424     break;\r
5425 \r
5426   /* [AS] Snapping */\r
5427   case WM_ENTERSIZEMOVE:\r
5428     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5429     if (hwnd == hwndMain) {\r
5430       doingSizing = TRUE;\r
5431       lastSizing = 0;\r
5432     }\r
5433     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5434     break;\r
5435 \r
5436   case WM_SIZING:\r
5437     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5438     if (hwnd == hwndMain) {\r
5439       lastSizing = wParam;\r
5440     }\r
5441     break;\r
5442 \r
5443   case WM_MOVING:\r
5444     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5445       return OnMoving( &sd, hwnd, wParam, lParam );\r
5446 \r
5447   case WM_EXITSIZEMOVE:\r
5448     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5449     if (hwnd == hwndMain) {\r
5450       RECT client;\r
5451       doingSizing = FALSE;\r
5452       InvalidateRect(hwnd, &boardRect, FALSE);\r
5453       GetClientRect(hwnd, &client);\r
5454       ResizeBoard(client.right, client.bottom, lastSizing);\r
5455       lastSizing = 0;\r
5456       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5457     }\r
5458     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5459     break;\r
5460 \r
5461   case WM_DESTROY: /* message: window being destroyed */\r
5462     PostQuitMessage(0);\r
5463     break;\r
5464 \r
5465   case WM_CLOSE:\r
5466     if (hwnd == hwndMain) {\r
5467       ExitEvent(0);\r
5468     }\r
5469     break;\r
5470 \r
5471   default:      /* Passes it on if unprocessed */\r
5472     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5473   }\r
5474   return 0;\r
5475 }\r
5476 \r
5477 /*---------------------------------------------------------------------------*\\r
5478  *\r
5479  * Misc utility routines\r
5480  *\r
5481 \*---------------------------------------------------------------------------*/\r
5482 \r
5483 /*\r
5484  * Decent random number generator, at least not as bad as Windows\r
5485  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5486  */\r
5487 unsigned int randstate;\r
5488 \r
5489 int\r
5490 myrandom(void)\r
5491 {\r
5492   randstate = randstate * 1664525 + 1013904223;\r
5493   return (int) randstate & 0x7fffffff;\r
5494 }\r
5495 \r
5496 void\r
5497 mysrandom(unsigned int seed)\r
5498 {\r
5499   randstate = seed;\r
5500 }\r
5501 \r
5502 \r
5503 /* \r
5504  * returns TRUE if user selects a different color, FALSE otherwise \r
5505  */\r
5506 \r
5507 BOOL\r
5508 ChangeColor(HWND hwnd, COLORREF *which)\r
5509 {\r
5510   static BOOL firstTime = TRUE;\r
5511   static DWORD customColors[16];\r
5512   CHOOSECOLOR cc;\r
5513   COLORREF newcolor;\r
5514   int i;\r
5515   ColorClass ccl;\r
5516 \r
5517   if (firstTime) {\r
5518     /* Make initial colors in use available as custom colors */\r
5519     /* Should we put the compiled-in defaults here instead? */\r
5520     i = 0;\r
5521     customColors[i++] = lightSquareColor & 0xffffff;\r
5522     customColors[i++] = darkSquareColor & 0xffffff;\r
5523     customColors[i++] = whitePieceColor & 0xffffff;\r
5524     customColors[i++] = blackPieceColor & 0xffffff;\r
5525     customColors[i++] = highlightSquareColor & 0xffffff;\r
5526     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5527 \r
5528     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5529       customColors[i++] = textAttribs[ccl].color;\r
5530     }\r
5531     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5532     firstTime = FALSE;\r
5533   }\r
5534 \r
5535   cc.lStructSize = sizeof(cc);\r
5536   cc.hwndOwner = hwnd;\r
5537   cc.hInstance = NULL;\r
5538   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5539   cc.lpCustColors = (LPDWORD) customColors;\r
5540   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5541 \r
5542   if (!ChooseColor(&cc)) return FALSE;\r
5543 \r
5544   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5545   if (newcolor == *which) return FALSE;\r
5546   *which = newcolor;\r
5547   return TRUE;\r
5548 \r
5549   /*\r
5550   InitDrawingColors();\r
5551   InvalidateRect(hwnd, &boardRect, FALSE);\r
5552   */\r
5553 }\r
5554 \r
5555 BOOLEAN\r
5556 MyLoadSound(MySound *ms)\r
5557 {\r
5558   BOOL ok = FALSE;\r
5559   struct stat st;\r
5560   FILE *f;\r
5561 \r
5562   if (ms->data) free(ms->data);\r
5563   ms->data = NULL;\r
5564 \r
5565   switch (ms->name[0]) {\r
5566   case NULLCHAR:\r
5567     /* Silence */\r
5568     ok = TRUE;\r
5569     break;\r
5570   case '$':\r
5571     /* System sound from Control Panel.  Don't preload here. */\r
5572     ok = TRUE;\r
5573     break;\r
5574   case '!':\r
5575     if (ms->name[1] == NULLCHAR) {\r
5576       /* "!" alone = silence */\r
5577       ok = TRUE;\r
5578     } else {\r
5579       /* Builtin wave resource.  Error if not found. */\r
5580       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5581       if (h == NULL) break;\r
5582       ms->data = (void *)LoadResource(hInst, h);\r
5583       if (h == NULL) break;\r
5584       ok = TRUE;\r
5585     }\r
5586     break;\r
5587   default:\r
5588     /* .wav file.  Error if not found. */\r
5589     f = fopen(ms->name, "rb");\r
5590     if (f == NULL) break;\r
5591     if (fstat(fileno(f), &st) < 0) break;\r
5592     ms->data = malloc(st.st_size);\r
5593     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5594     fclose(f);\r
5595     ok = TRUE;\r
5596     break;\r
5597   }\r
5598   if (!ok) {\r
5599     char buf[MSG_SIZ];\r
5600       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5601     DisplayError(buf, GetLastError());\r
5602   }\r
5603   return ok;\r
5604 }\r
5605 \r
5606 BOOLEAN\r
5607 MyPlaySound(MySound *ms)\r
5608 {\r
5609   BOOLEAN ok = FALSE;\r
5610 \r
5611   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5612   switch (ms->name[0]) {\r
5613   case NULLCHAR:\r
5614         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5615     /* Silence */\r
5616     ok = TRUE;\r
5617     break;\r
5618   case '$':\r
5619     /* System sound from Control Panel (deprecated feature).\r
5620        "$" alone or an unset sound name gets default beep (still in use). */\r
5621     if (ms->name[1]) {\r
5622       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5623     }\r
5624     if (!ok) ok = MessageBeep(MB_OK);\r
5625     break; \r
5626   case '!':\r
5627     /* Builtin wave resource, or "!" alone for silence */\r
5628     if (ms->name[1]) {\r
5629       if (ms->data == NULL) return FALSE;\r
5630       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5631     } else {\r
5632       ok = TRUE;\r
5633     }\r
5634     break;\r
5635   default:\r
5636     /* .wav file.  Error if not found. */\r
5637     if (ms->data == NULL) return FALSE;\r
5638     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5639     break;\r
5640   }\r
5641   /* Don't print an error: this can happen innocently if the sound driver\r
5642      is busy; for instance, if another instance of WinBoard is playing\r
5643      a sound at about the same time. */\r
5644   return ok;\r
5645 }\r
5646 \r
5647 \r
5648 LRESULT CALLBACK\r
5649 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5650 {\r
5651   BOOL ok;\r
5652   OPENFILENAME *ofn;\r
5653   static UINT *number; /* gross that this is static */\r
5654 \r
5655   switch (message) {\r
5656   case WM_INITDIALOG: /* message: initialize dialog box */\r
5657     /* Center the dialog over the application window */\r
5658     ofn = (OPENFILENAME *) lParam;\r
5659     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5660       number = (UINT *) ofn->lCustData;\r
5661       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5662     } else {\r
5663       number = NULL;\r
5664     }\r
5665     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5666     Translate(hDlg, 1536);\r
5667     return FALSE;  /* Allow for further processing */\r
5668 \r
5669   case WM_COMMAND:\r
5670     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5671       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5672     }\r
5673     return FALSE;  /* Allow for further processing */\r
5674   }\r
5675   return FALSE;\r
5676 }\r
5677 \r
5678 UINT APIENTRY\r
5679 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5680 {\r
5681   static UINT *number;\r
5682   OPENFILENAME *ofname;\r
5683   OFNOTIFY *ofnot;\r
5684   switch (uiMsg) {\r
5685   case WM_INITDIALOG:\r
5686     Translate(hdlg, DLG_IndexNumber);\r
5687     ofname = (OPENFILENAME *)lParam;\r
5688     number = (UINT *)(ofname->lCustData);\r
5689     break;\r
5690   case WM_NOTIFY:\r
5691     ofnot = (OFNOTIFY *)lParam;\r
5692     if (ofnot->hdr.code == CDN_FILEOK) {\r
5693       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5694     }\r
5695     break;\r
5696   }\r
5697   return 0;\r
5698 }\r
5699 \r
5700 \r
5701 FILE *\r
5702 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5703                char *nameFilt, char *dlgTitle, UINT *number,\r
5704                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5705 {\r
5706   OPENFILENAME openFileName;\r
5707   char buf1[MSG_SIZ];\r
5708   FILE *f;\r
5709 \r
5710   if (fileName == NULL) fileName = buf1;\r
5711   if (defName == NULL) {\r
5712     safeStrCpy(fileName, "*.", sizeof(fileName)/sizeof(fileName[0]) );\r
5713     strcat(fileName, defExt);\r
5714   } else {\r
5715     safeStrCpy(fileName, defName, sizeof(fileName)/sizeof(fileName[0]) );\r
5716   }\r
5717     if (fileTitle) safeStrCpy(fileTitle, "", sizeof(fileTitle)/sizeof(fileTitle[0]) );\r
5718   if (number) *number = 0;\r
5719 \r
5720   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5721   openFileName.hwndOwner         = hwnd;\r
5722   openFileName.hInstance         = (HANDLE) hInst;\r
5723   openFileName.lpstrFilter       = nameFilt;\r
5724   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5725   openFileName.nMaxCustFilter    = 0L;\r
5726   openFileName.nFilterIndex      = 1L;\r
5727   openFileName.lpstrFile         = fileName;\r
5728   openFileName.nMaxFile          = MSG_SIZ;\r
5729   openFileName.lpstrFileTitle    = fileTitle;\r
5730   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5731   openFileName.lpstrInitialDir   = NULL;\r
5732   openFileName.lpstrTitle        = dlgTitle;\r
5733   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5734     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5735     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5736     | (oldDialog ? 0 : OFN_EXPLORER);\r
5737   openFileName.nFileOffset       = 0;\r
5738   openFileName.nFileExtension    = 0;\r
5739   openFileName.lpstrDefExt       = defExt;\r
5740   openFileName.lCustData         = (LONG) number;\r
5741   openFileName.lpfnHook          = oldDialog ?\r
5742     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5743   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5744 \r
5745   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5746                         GetOpenFileName(&openFileName)) {\r
5747     /* open the file */\r
5748     f = fopen(openFileName.lpstrFile, write);\r
5749     if (f == NULL) {\r
5750       MessageBox(hwnd, _("File open failed"), NULL,\r
5751                  MB_OK|MB_ICONEXCLAMATION);\r
5752       return NULL;\r
5753     }\r
5754   } else {\r
5755     int err = CommDlgExtendedError();\r
5756     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5757     return FALSE;\r
5758   }\r
5759   return f;\r
5760 }\r
5761 \r
5762 \r
5763 \r
5764 VOID APIENTRY\r
5765 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5766 {\r
5767   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5768 \r
5769   /*\r
5770    * Get the first pop-up menu in the menu template. This is the\r
5771    * menu that TrackPopupMenu displays.\r
5772    */\r
5773   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5774 \r
5775   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5776 \r
5777   /*\r
5778    * TrackPopup uses screen coordinates, so convert the\r
5779    * coordinates of the mouse click to screen coordinates.\r
5780    */\r
5781   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5782 \r
5783   /* Draw and track the floating pop-up menu. */\r
5784   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5785                  pt.x, pt.y, 0, hwnd, NULL);\r
5786 \r
5787   /* Destroy the menu.*/\r
5788   DestroyMenu(hmenu);\r
5789 }\r
5790    \r
5791 typedef struct {\r
5792   HWND hDlg, hText;\r
5793   int sizeX, sizeY, newSizeX, newSizeY;\r
5794   HDWP hdwp;\r
5795 } ResizeEditPlusButtonsClosure;\r
5796 \r
5797 BOOL CALLBACK\r
5798 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5799 {\r
5800   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5801   RECT rect;\r
5802   POINT pt;\r
5803 \r
5804   if (hChild == cl->hText) return TRUE;\r
5805   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5806   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5807   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5808   ScreenToClient(cl->hDlg, &pt);\r
5809   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5810     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5811   return TRUE;\r
5812 }\r
5813 \r
5814 /* Resize a dialog that has a (rich) edit field filling most of\r
5815    the top, with a row of buttons below */\r
5816 VOID\r
5817 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5818 {\r
5819   RECT rectText;\r
5820   int newTextHeight, newTextWidth;\r
5821   ResizeEditPlusButtonsClosure cl;\r
5822   \r
5823   /*if (IsIconic(hDlg)) return;*/\r
5824   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5825   \r
5826   cl.hdwp = BeginDeferWindowPos(8);\r
5827 \r
5828   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5829   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5830   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5831   if (newTextHeight < 0) {\r
5832     newSizeY += -newTextHeight;\r
5833     newTextHeight = 0;\r
5834   }\r
5835   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5836     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5837 \r
5838   cl.hDlg = hDlg;\r
5839   cl.hText = hText;\r
5840   cl.sizeX = sizeX;\r
5841   cl.sizeY = sizeY;\r
5842   cl.newSizeX = newSizeX;\r
5843   cl.newSizeY = newSizeY;\r
5844   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5845 \r
5846   EndDeferWindowPos(cl.hdwp);\r
5847 }\r
5848 \r
5849 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5850 {\r
5851     RECT    rChild, rParent;\r
5852     int     wChild, hChild, wParent, hParent;\r
5853     int     wScreen, hScreen, xNew, yNew;\r
5854     HDC     hdc;\r
5855 \r
5856     /* Get the Height and Width of the child window */\r
5857     GetWindowRect (hwndChild, &rChild);\r
5858     wChild = rChild.right - rChild.left;\r
5859     hChild = rChild.bottom - rChild.top;\r
5860 \r
5861     /* Get the Height and Width of the parent window */\r
5862     GetWindowRect (hwndParent, &rParent);\r
5863     wParent = rParent.right - rParent.left;\r
5864     hParent = rParent.bottom - rParent.top;\r
5865 \r
5866     /* Get the display limits */\r
5867     hdc = GetDC (hwndChild);\r
5868     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5869     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5870     ReleaseDC(hwndChild, hdc);\r
5871 \r
5872     /* Calculate new X position, then adjust for screen */\r
5873     xNew = rParent.left + ((wParent - wChild) /2);\r
5874     if (xNew < 0) {\r
5875         xNew = 0;\r
5876     } else if ((xNew+wChild) > wScreen) {\r
5877         xNew = wScreen - wChild;\r
5878     }\r
5879 \r
5880     /* Calculate new Y position, then adjust for screen */\r
5881     if( mode == 0 ) {\r
5882         yNew = rParent.top  + ((hParent - hChild) /2);\r
5883     }\r
5884     else {\r
5885         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5886     }\r
5887 \r
5888     if (yNew < 0) {\r
5889         yNew = 0;\r
5890     } else if ((yNew+hChild) > hScreen) {\r
5891         yNew = hScreen - hChild;\r
5892     }\r
5893 \r
5894     /* Set it, and return */\r
5895     return SetWindowPos (hwndChild, NULL,\r
5896                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5897 }\r
5898 \r
5899 /* Center one window over another */\r
5900 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5901 {\r
5902     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5903 }\r
5904 \r
5905 /*---------------------------------------------------------------------------*\\r
5906  *\r
5907  * Startup Dialog functions\r
5908  *\r
5909 \*---------------------------------------------------------------------------*/\r
5910 void\r
5911 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5912 {\r
5913   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5914 \r
5915   while (*cd != NULL) {\r
5916     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5917     cd++;\r
5918   }\r
5919 }\r
5920 \r
5921 void\r
5922 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5923 {\r
5924   char buf1[MAX_ARG_LEN];\r
5925   int len;\r
5926 \r
5927   if (str[0] == '@') {\r
5928     FILE* f = fopen(str + 1, "r");\r
5929     if (f == NULL) {\r
5930       DisplayFatalError(str + 1, errno, 2);\r
5931       return;\r
5932     }\r
5933     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5934     fclose(f);\r
5935     buf1[len] = NULLCHAR;\r
5936     str = buf1;\r
5937   }\r
5938 \r
5939   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5940 \r
5941   for (;;) {\r
5942     char buf[MSG_SIZ];\r
5943     char *end = strchr(str, '\n');\r
5944     if (end == NULL) return;\r
5945     memcpy(buf, str, end - str);\r
5946     buf[end - str] = NULLCHAR;\r
5947     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5948     str = end + 1;\r
5949   }\r
5950 }\r
5951 \r
5952 void\r
5953 SetStartupDialogEnables(HWND hDlg)\r
5954 {\r
5955   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
5956     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5957     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
5958   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5959     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
5960   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
5961     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
5962   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
5963     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
5964   EnableWindow(GetDlgItem(hDlg, IDOK),\r
5965     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5966     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
5967     IsDlgButtonChecked(hDlg, OPT_View));\r
5968 }\r
5969 \r
5970 char *\r
5971 QuoteForFilename(char *filename)\r
5972 {\r
5973   int dquote, space;\r
5974   dquote = strchr(filename, '"') != NULL;\r
5975   space = strchr(filename, ' ') != NULL;\r
5976   if (dquote || space) {\r
5977     if (dquote) {\r
5978       return "'";\r
5979     } else {\r
5980       return "\"";\r
5981     }\r
5982   } else {\r
5983     return "";\r
5984   }\r
5985 }\r
5986 \r
5987 VOID\r
5988 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
5989 {\r
5990   char buf[MSG_SIZ];\r
5991   char *q;\r
5992 \r
5993   InitComboStringsFromOption(hwndCombo, nthnames);\r
5994   q = QuoteForFilename(nthcp);\r
5995     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
5996   if (*nthdir != NULLCHAR) {\r
5997     q = QuoteForFilename(nthdir);\r
5998       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
5999   }\r
6000   if (*nthcp == NULLCHAR) {\r
6001     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6002   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6003     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6004     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6005   }\r
6006 }\r
6007 \r
6008 LRESULT CALLBACK\r
6009 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6010 {\r
6011   char buf[MSG_SIZ];\r
6012   HANDLE hwndCombo;\r
6013   char *p;\r
6014 \r
6015   switch (message) {\r
6016   case WM_INITDIALOG:\r
6017     /* Center the dialog */\r
6018     CenterWindow (hDlg, GetDesktopWindow());\r
6019     Translate(hDlg, DLG_Startup);\r
6020     /* Initialize the dialog items */\r
6021     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6022                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6023                   firstChessProgramNames);\r
6024     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6025                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6026                   secondChessProgramNames);\r
6027     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6028     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6029       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6030     if (*appData.icsHelper != NULLCHAR) {\r
6031       char *q = QuoteForFilename(appData.icsHelper);\r
6032       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6033     }\r
6034     if (*appData.icsHost == NULLCHAR) {\r
6035       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6036       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6037     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6038       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6039       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6040     }\r
6041 \r
6042     if (appData.icsActive) {\r
6043       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6044     }\r
6045     else if (appData.noChessProgram) {\r
6046       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6047     }\r
6048     else {\r
6049       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6050     }\r
6051 \r
6052     SetStartupDialogEnables(hDlg);\r
6053     return TRUE;\r
6054 \r
6055   case WM_COMMAND:\r
6056     switch (LOWORD(wParam)) {\r
6057     case IDOK:\r
6058       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6059         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6060         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6061         p = buf;\r
6062         ParseArgs(StringGet, &p);\r
6063         safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6064         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6065         p = buf;\r
6066         ParseArgs(StringGet, &p);\r
6067         appData.noChessProgram = FALSE;\r
6068         appData.icsActive = FALSE;\r
6069       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6070         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6071         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6072         p = buf;\r
6073         ParseArgs(StringGet, &p);\r
6074         if (appData.zippyPlay) {\r
6075           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6076           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6077           p = buf;\r
6078           ParseArgs(StringGet, &p);\r
6079         }\r
6080       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6081         appData.noChessProgram = TRUE;\r
6082         appData.icsActive = FALSE;\r
6083       } else {\r
6084         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6085                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6086         return TRUE;\r
6087       }\r
6088       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6089         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6090         p = buf;\r
6091         ParseArgs(StringGet, &p);\r
6092       }\r
6093       EndDialog(hDlg, TRUE);\r
6094       return TRUE;\r
6095 \r
6096     case IDCANCEL:\r
6097       ExitEvent(0);\r
6098       return TRUE;\r
6099 \r
6100     case IDM_HELPCONTENTS:\r
6101       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6102         MessageBox (GetFocus(),\r
6103                     _("Unable to activate help"),\r
6104                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6105       }\r
6106       break;\r
6107 \r
6108     default:\r
6109       SetStartupDialogEnables(hDlg);\r
6110       break;\r
6111     }\r
6112     break;\r
6113   }\r
6114   return FALSE;\r
6115 }\r
6116 \r
6117 /*---------------------------------------------------------------------------*\\r
6118  *\r
6119  * About box dialog functions\r
6120  *\r
6121 \*---------------------------------------------------------------------------*/\r
6122 \r
6123 /* Process messages for "About" dialog box */\r
6124 LRESULT CALLBACK\r
6125 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6126 {\r
6127   switch (message) {\r
6128   case WM_INITDIALOG: /* message: initialize dialog box */\r
6129     /* Center the dialog over the application window */\r
6130     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6131     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6132     Translate(hDlg, ABOUTBOX);\r
6133     JAWS_COPYRIGHT\r
6134     return (TRUE);\r
6135 \r
6136   case WM_COMMAND: /* message: received a command */\r
6137     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6138         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6139       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6140       return (TRUE);\r
6141     }\r
6142     break;\r
6143   }\r
6144   return (FALSE);\r
6145 }\r
6146 \r
6147 /*---------------------------------------------------------------------------*\\r
6148  *\r
6149  * Comment Dialog functions\r
6150  *\r
6151 \*---------------------------------------------------------------------------*/\r
6152 \r
6153 LRESULT CALLBACK\r
6154 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6155 {\r
6156   static HANDLE hwndText = NULL;\r
6157   int len, newSizeX, newSizeY, flags;\r
6158   static int sizeX, sizeY;\r
6159   char *str;\r
6160   RECT rect;\r
6161   MINMAXINFO *mmi;\r
6162 \r
6163   switch (message) {\r
6164   case WM_INITDIALOG: /* message: initialize dialog box */\r
6165     /* Initialize the dialog items */\r
6166     Translate(hDlg, DLG_EditComment);\r
6167     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6168     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6169     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6170     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6171     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6172     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6173     SetWindowText(hDlg, commentTitle);\r
6174     if (editComment) {\r
6175       SetFocus(hwndText);\r
6176     } else {\r
6177       SetFocus(GetDlgItem(hDlg, IDOK));\r
6178     }\r
6179     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6180                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6181                 MAKELPARAM(FALSE, 0));\r
6182     /* Size and position the dialog */\r
6183     if (!commentDialog) {\r
6184       commentDialog = hDlg;\r
6185       flags = SWP_NOZORDER;\r
6186       GetClientRect(hDlg, &rect);\r
6187       sizeX = rect.right;\r
6188       sizeY = rect.bottom;\r
6189       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6190           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6191         WINDOWPLACEMENT wp;\r
6192         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6193         wp.length = sizeof(WINDOWPLACEMENT);\r
6194         wp.flags = 0;\r
6195         wp.showCmd = SW_SHOW;\r
6196         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6197         wp.rcNormalPosition.left = wpComment.x;\r
6198         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6199         wp.rcNormalPosition.top = wpComment.y;\r
6200         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6201         SetWindowPlacement(hDlg, &wp);\r
6202 \r
6203         GetClientRect(hDlg, &rect);\r
6204         newSizeX = rect.right;\r
6205         newSizeY = rect.bottom;\r
6206         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6207                               newSizeX, newSizeY);\r
6208         sizeX = newSizeX;\r
6209         sizeY = newSizeY;\r
6210       }\r
6211     }\r
6212     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );\r
6213     return FALSE;\r
6214 \r
6215   case WM_COMMAND: /* message: received a command */\r
6216     switch (LOWORD(wParam)) {\r
6217     case IDOK:\r
6218       if (editComment) {\r
6219         char *p, *q;\r
6220         /* Read changed options from the dialog box */\r
6221         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6222         len = GetWindowTextLength(hwndText);\r
6223         str = (char *) malloc(len + 1);\r
6224         GetWindowText(hwndText, str, len + 1);\r
6225         p = q = str;\r
6226         while (*q) {\r
6227           if (*q == '\r')\r
6228             q++;\r
6229           else\r
6230             *p++ = *q++;\r
6231         }\r
6232         *p = NULLCHAR;\r
6233         ReplaceComment(commentIndex, str);\r
6234         free(str);\r
6235       }\r
6236       CommentPopDown();\r
6237       return TRUE;\r
6238 \r
6239     case IDCANCEL:\r
6240     case OPT_CancelComment:\r
6241       CommentPopDown();\r
6242       return TRUE;\r
6243 \r
6244     case OPT_ClearComment:\r
6245       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6246       break;\r
6247 \r
6248     case OPT_EditComment:\r
6249       EditCommentEvent();\r
6250       return TRUE;\r
6251 \r
6252     default:\r
6253       break;\r
6254     }\r
6255     break;\r
6256 \r
6257   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6258         if( wParam == OPT_CommentText ) {\r
6259             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6260 \r
6261             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {\r
6262                 POINTL pt;\r
6263                 LRESULT index;\r
6264 \r
6265                 pt.x = LOWORD( lpMF->lParam );\r
6266                 pt.y = HIWORD( lpMF->lParam );\r
6267 \r
6268                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6269 \r
6270                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6271                 len = GetWindowTextLength(hwndText);\r
6272                 str = (char *) malloc(len + 1);\r
6273                 GetWindowText(hwndText, str, len + 1);\r
6274                 ReplaceComment(commentIndex, str);\r
6275                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6276                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6277                 free(str);\r
6278 \r
6279                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6280                 lpMF->msg = WM_USER;\r
6281 \r
6282                 return TRUE;\r
6283             }\r
6284         }\r
6285         break;\r
6286 \r
6287   case WM_SIZE:\r
6288     newSizeX = LOWORD(lParam);\r
6289     newSizeY = HIWORD(lParam);\r
6290     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6291     sizeX = newSizeX;\r
6292     sizeY = newSizeY;\r
6293     break;\r
6294 \r
6295   case WM_GETMINMAXINFO:\r
6296     /* Prevent resizing window too small */\r
6297     mmi = (MINMAXINFO *) lParam;\r
6298     mmi->ptMinTrackSize.x = 100;\r
6299     mmi->ptMinTrackSize.y = 100;\r
6300     break;\r
6301   }\r
6302   return FALSE;\r
6303 }\r
6304 \r
6305 VOID\r
6306 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6307 {\r
6308   FARPROC lpProc;\r
6309   char *p, *q;\r
6310 \r
6311   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6312 \r
6313   if (str == NULL) str = "";\r
6314   p = (char *) malloc(2 * strlen(str) + 2);\r
6315   q = p;\r
6316   while (*str) {\r
6317     if (*str == '\n') *q++ = '\r';\r
6318     *q++ = *str++;\r
6319   }\r
6320   *q = NULLCHAR;\r
6321   if (commentText != NULL) free(commentText);\r
6322 \r
6323   commentIndex = index;\r
6324   commentTitle = title;\r
6325   commentText = p;\r
6326   editComment = edit;\r
6327 \r
6328   if (commentDialog) {\r
6329     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6330     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6331   } else {\r
6332     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6333     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6334                  hwndMain, (DLGPROC)lpProc);\r
6335     FreeProcInstance(lpProc);\r
6336   }\r
6337   commentUp = TRUE;\r
6338 }\r
6339 \r
6340 \r
6341 /*---------------------------------------------------------------------------*\\r
6342  *\r
6343  * Type-in move dialog functions\r
6344  * \r
6345 \*---------------------------------------------------------------------------*/\r
6346 \r
6347 LRESULT CALLBACK\r
6348 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6349 {\r
6350   char move[MSG_SIZ];\r
6351   HWND hInput;\r
6352   ChessMove moveType;\r
6353   int fromX, fromY, toX, toY;\r
6354   char promoChar;\r
6355 \r
6356   switch (message) {\r
6357   case WM_INITDIALOG:\r
6358     move[0] = (char) lParam;\r
6359     move[1] = NULLCHAR;\r
6360     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6361     Translate(hDlg, DLG_TypeInMove);\r
6362     hInput = GetDlgItem(hDlg, OPT_Move);\r
6363     SetWindowText(hInput, move);\r
6364     SetFocus(hInput);\r
6365     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6366     return FALSE;\r
6367 \r
6368   case WM_COMMAND:\r
6369     switch (LOWORD(wParam)) {\r
6370     case IDOK:
6371       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6372       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6373       { int n; Board board;\r
6374         // [HGM] FENedit\r
6375         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
6376                 EditPositionPasteFEN(move);\r
6377                 EndDialog(hDlg, TRUE);\r
6378                 return TRUE;\r
6379         }\r
6380         // [HGM] movenum: allow move number to be typed in any mode\r
6381         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
6382           ToNrEvent(2*n-1);\r
6383           EndDialog(hDlg, TRUE);\r
6384           return TRUE;\r
6385         }\r
6386       }\r
6387       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6388         gameMode != Training) {\r
6389         DisplayMoveError(_("Displayed move is not current"));\r
6390       } else {\r
6391 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
6392         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6393           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
6394         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
6395         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6396           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6397           if (gameMode != Training)\r
6398               forwardMostMove = currentMove;\r
6399           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6400         } else {\r
6401           DisplayMoveError(_("Could not parse move"));\r
6402         }\r
6403       }\r
6404       EndDialog(hDlg, TRUE);\r
6405       return TRUE;\r
6406     case IDCANCEL:\r
6407       EndDialog(hDlg, FALSE);\r
6408       return TRUE;\r
6409     default:\r
6410       break;\r
6411     }\r
6412     break;\r
6413   }\r
6414   return FALSE;\r
6415 }\r
6416 \r
6417 VOID\r
6418 PopUpMoveDialog(char firstchar)\r
6419 {\r
6420     FARPROC lpProc;\r
6421     \r
6422     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6423         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6424         gameMode == AnalyzeMode || gameMode == EditGame || \r
6425         gameMode == EditPosition || gameMode == IcsExamining ||\r
6426         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6427         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
6428                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
6429                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
6430         gameMode == Training) {\r
6431       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6432       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6433         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6434       FreeProcInstance(lpProc);\r
6435     }\r
6436 }\r
6437 \r
6438 /*---------------------------------------------------------------------------*\\r
6439  *\r
6440  * Type-in name dialog functions\r
6441  * \r
6442 \*---------------------------------------------------------------------------*/\r
6443 \r
6444 LRESULT CALLBACK\r
6445 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6446 {\r
6447   char move[MSG_SIZ];\r
6448   HWND hInput;\r
6449 \r
6450   switch (message) {\r
6451   case WM_INITDIALOG:\r
6452     move[0] = (char) lParam;\r
6453     move[1] = NULLCHAR;\r
6454     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6455     Translate(hDlg, DLG_TypeInName);\r
6456     hInput = GetDlgItem(hDlg, OPT_Name);\r
6457     SetWindowText(hInput, move);\r
6458     SetFocus(hInput);\r
6459     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6460     return FALSE;\r
6461 \r
6462   case WM_COMMAND:\r
6463     switch (LOWORD(wParam)) {\r
6464     case IDOK:\r
6465       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6466       appData.userName = strdup(move);\r
6467       SetUserLogo();\r
6468       SetGameInfo();\r
6469       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6470         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6471         DisplayTitle(move);\r
6472       }\r
6473 \r
6474 \r
6475       EndDialog(hDlg, TRUE);\r
6476       return TRUE;\r
6477     case IDCANCEL:\r
6478       EndDialog(hDlg, FALSE);\r
6479       return TRUE;\r
6480     default:\r
6481       break;\r
6482     }\r
6483     break;\r
6484   }\r
6485   return FALSE;\r
6486 }\r
6487 \r
6488 VOID\r
6489 PopUpNameDialog(char firstchar)\r
6490 {\r
6491     FARPROC lpProc;\r
6492     \r
6493       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6494       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6495         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6496       FreeProcInstance(lpProc);\r
6497 }\r
6498 \r
6499 /*---------------------------------------------------------------------------*\\r
6500  *\r
6501  *  Error dialogs\r
6502  * \r
6503 \*---------------------------------------------------------------------------*/\r
6504 \r
6505 /* Nonmodal error box */\r
6506 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6507                              WPARAM wParam, LPARAM lParam);\r
6508 \r
6509 VOID\r
6510 ErrorPopUp(char *title, char *content)\r
6511 {\r
6512   FARPROC lpProc;\r
6513   char *p, *q;\r
6514   BOOLEAN modal = hwndMain == NULL;\r
6515 \r
6516   p = content;\r
6517   q = errorMessage;\r
6518   while (*p) {\r
6519     if (*p == '\n') {\r
6520       if (modal) {\r
6521         *q++ = ' ';\r
6522         p++;\r
6523       } else {\r
6524         *q++ = '\r';\r
6525         *q++ = *p++;\r
6526       }\r
6527     } else {\r
6528       *q++ = *p++;\r
6529     }\r
6530   }\r
6531   *q = NULLCHAR;\r
6532   strncpy(errorTitle, title, sizeof(errorTitle));\r
6533   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6534   \r
6535   if (modal) {\r
6536     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6537   } else {\r
6538     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6539     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6540                  hwndMain, (DLGPROC)lpProc);\r
6541     FreeProcInstance(lpProc);\r
6542   }\r
6543 }\r
6544 \r
6545 VOID\r
6546 ErrorPopDown()\r
6547 {\r
6548   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6549   if (errorDialog == NULL) return;\r
6550   DestroyWindow(errorDialog);\r
6551   errorDialog = NULL;\r
6552   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6553 }\r
6554 \r
6555 LRESULT CALLBACK\r
6556 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6557 {\r
6558   HANDLE hwndText;\r
6559   RECT rChild;\r
6560 \r
6561   switch (message) {\r
6562   case WM_INITDIALOG:\r
6563     GetWindowRect(hDlg, &rChild);\r
6564 \r
6565     /*\r
6566     SetWindowPos(hDlg, NULL, rChild.left,\r
6567       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6568       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6569     */\r
6570 \r
6571     /* \r
6572         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6573         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6574         and it doesn't work when you resize the dialog.\r
6575         For now, just give it a default position.\r
6576     */\r
6577     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6578     Translate(hDlg, DLG_Error);\r
6579 \r
6580     errorDialog = hDlg;\r
6581     SetWindowText(hDlg, errorTitle);\r
6582     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6583     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6584     return FALSE;\r
6585 \r
6586   case WM_COMMAND:\r
6587     switch (LOWORD(wParam)) {\r
6588     case IDOK:\r
6589     case IDCANCEL:\r
6590       if (errorDialog == hDlg) errorDialog = NULL;\r
6591       DestroyWindow(hDlg);\r
6592       return TRUE;\r
6593 \r
6594     default:\r
6595       break;\r
6596     }\r
6597     break;\r
6598   }\r
6599   return FALSE;\r
6600 }\r
6601 \r
6602 #ifdef GOTHIC\r
6603 HWND gothicDialog = NULL;\r
6604 \r
6605 LRESULT CALLBACK\r
6606 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6607 {\r
6608   HANDLE hwndText;\r
6609   RECT rChild;\r
6610   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6611 \r
6612   switch (message) {\r
6613   case WM_INITDIALOG:\r
6614     GetWindowRect(hDlg, &rChild);\r
6615 \r
6616     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6617                                                              SWP_NOZORDER);\r
6618 \r
6619     /* \r
6620         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6621         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6622         and it doesn't work when you resize the dialog.\r
6623         For now, just give it a default position.\r
6624     */\r
6625     gothicDialog = hDlg;\r
6626     SetWindowText(hDlg, errorTitle);\r
6627     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6628     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6629     return FALSE;\r
6630 \r
6631   case WM_COMMAND:\r
6632     switch (LOWORD(wParam)) {\r
6633     case IDOK:\r
6634     case IDCANCEL:\r
6635       if (errorDialog == hDlg) errorDialog = NULL;\r
6636       DestroyWindow(hDlg);\r
6637       return TRUE;\r
6638 \r
6639     default:\r
6640       break;\r
6641     }\r
6642     break;\r
6643   }\r
6644   return FALSE;\r
6645 }\r
6646 \r
6647 VOID\r
6648 GothicPopUp(char *title, VariantClass variant)\r
6649 {\r
6650   FARPROC lpProc;\r
6651   static char *lastTitle;\r
6652 \r
6653   strncpy(errorTitle, title, sizeof(errorTitle));\r
6654   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6655 \r
6656   if(lastTitle != title && gothicDialog != NULL) {\r
6657     DestroyWindow(gothicDialog);\r
6658     gothicDialog = NULL;\r
6659   }\r
6660   if(variant != VariantNormal && gothicDialog == NULL) {\r
6661     title = lastTitle;\r
6662     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6663     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6664                  hwndMain, (DLGPROC)lpProc);\r
6665     FreeProcInstance(lpProc);\r
6666   }\r
6667 }\r
6668 #endif\r
6669 \r
6670 /*---------------------------------------------------------------------------*\\r
6671  *\r
6672  *  Ics Interaction console functions\r
6673  *\r
6674 \*---------------------------------------------------------------------------*/\r
6675 \r
6676 #define HISTORY_SIZE 64\r
6677 static char *history[HISTORY_SIZE];\r
6678 int histIn = 0, histP = 0;\r
6679 \r
6680 VOID\r
6681 SaveInHistory(char *cmd)\r
6682 {\r
6683   if (history[histIn] != NULL) {\r
6684     free(history[histIn]);\r
6685     history[histIn] = NULL;\r
6686   }\r
6687   if (*cmd == NULLCHAR) return;\r
6688   history[histIn] = StrSave(cmd);\r
6689   histIn = (histIn + 1) % HISTORY_SIZE;\r
6690   if (history[histIn] != NULL) {\r
6691     free(history[histIn]);\r
6692     history[histIn] = NULL;\r
6693   }\r
6694   histP = histIn;\r
6695 }\r
6696 \r
6697 char *\r
6698 PrevInHistory(char *cmd)\r
6699 {\r
6700   int newhp;\r
6701   if (histP == histIn) {\r
6702     if (history[histIn] != NULL) free(history[histIn]);\r
6703     history[histIn] = StrSave(cmd);\r
6704   }\r
6705   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6706   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6707   histP = newhp;\r
6708   return history[histP];\r
6709 }\r
6710 \r
6711 char *\r
6712 NextInHistory()\r
6713 {\r
6714   if (histP == histIn) return NULL;\r
6715   histP = (histP + 1) % HISTORY_SIZE;\r
6716   return history[histP];   \r
6717 }\r
6718 \r
6719 HMENU\r
6720 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6721 {\r
6722   HMENU hmenu, h;\r
6723   int i = 0;\r
6724   hmenu = LoadMenu(hInst, "TextMenu");\r
6725   h = GetSubMenu(hmenu, 0);\r
6726   while (e->item) {\r
6727     if (strcmp(e->item, "-") == 0) {\r
6728       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6729     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6730       int flags = MF_STRING, j = 0;\r
6731       if (e->item[0] == '|') {\r
6732         flags |= MF_MENUBARBREAK;\r
6733         j++;\r
6734       }\r
6735       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6736       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6737     }\r
6738     e++;\r
6739     i++;\r
6740   } \r
6741   return hmenu;\r
6742 }\r
6743 \r
6744 WNDPROC consoleTextWindowProc;\r
6745 \r
6746 void\r
6747 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6748 {\r
6749   char buf[MSG_SIZ], name[MSG_SIZ];\r
6750   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6751   CHARRANGE sel;\r
6752 \r
6753   if (!getname) {\r
6754     SetWindowText(hInput, command);\r
6755     if (immediate) {\r
6756       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6757     } else {\r
6758       sel.cpMin = 999999;\r
6759       sel.cpMax = 999999;\r
6760       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6761       SetFocus(hInput);\r
6762     }\r
6763     return;\r
6764   }    \r
6765   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6766   if (sel.cpMin == sel.cpMax) {\r
6767     /* Expand to surrounding word */\r
6768     TEXTRANGE tr;\r
6769     do {\r
6770       tr.chrg.cpMax = sel.cpMin;\r
6771       tr.chrg.cpMin = --sel.cpMin;\r
6772       if (sel.cpMin < 0) break;\r
6773       tr.lpstrText = name;\r
6774       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6775     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6776     sel.cpMin++;\r
6777 \r
6778     do {\r
6779       tr.chrg.cpMin = sel.cpMax;\r
6780       tr.chrg.cpMax = ++sel.cpMax;\r
6781       tr.lpstrText = name;\r
6782       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6783     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6784     sel.cpMax--;\r
6785 \r
6786     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6787       MessageBeep(MB_ICONEXCLAMATION);\r
6788       return;\r
6789     }\r
6790     tr.chrg = sel;\r
6791     tr.lpstrText = name;\r
6792     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6793   } else {\r
6794     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6795       MessageBeep(MB_ICONEXCLAMATION);\r
6796       return;\r
6797     }\r
6798     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6799   }\r
6800   if (immediate) {\r
6801     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6802     SetWindowText(hInput, buf);\r
6803     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6804   } else {\r
6805     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6806       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6807     SetWindowText(hInput, buf);\r
6808     sel.cpMin = 999999;\r
6809     sel.cpMax = 999999;\r
6810     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6811     SetFocus(hInput);\r
6812   }\r
6813 }\r
6814 \r
6815 LRESULT CALLBACK \r
6816 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6817 {\r
6818   HWND hInput;\r
6819   CHARRANGE sel;\r
6820 \r
6821   switch (message) {\r
6822   case WM_KEYDOWN:\r
6823     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6824     switch (wParam) {\r
6825     case VK_PRIOR:\r
6826       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6827       return 0;\r
6828     case VK_NEXT:\r
6829       sel.cpMin = 999999;\r
6830       sel.cpMax = 999999;\r
6831       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6832       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6833       return 0;\r
6834     }\r
6835     break;\r
6836   case WM_CHAR:\r
6837    if(wParam != '\022') {\r
6838     if (wParam == '\t') {\r
6839       if (GetKeyState(VK_SHIFT) < 0) {\r
6840         /* shifted */\r
6841         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6842         if (buttonDesc[0].hwnd) {\r
6843           SetFocus(buttonDesc[0].hwnd);\r
6844         } else {\r
6845           SetFocus(hwndMain);\r
6846         }\r
6847       } else {\r
6848         /* unshifted */\r
6849         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6850       }\r
6851     } else {\r
6852       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6853       JAWS_DELETE( SetFocus(hInput); )\r
6854       SendMessage(hInput, message, wParam, lParam);\r
6855     }\r
6856     return 0;\r
6857    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
6858   case WM_RBUTTONDOWN:\r
6859     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6860       /* Move selection here if it was empty */\r
6861       POINT pt;\r
6862       pt.x = LOWORD(lParam);\r
6863       pt.y = HIWORD(lParam);\r
6864       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6865       if (sel.cpMin == sel.cpMax) {\r
6866         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6867         sel.cpMax = sel.cpMin;\r
6868         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6869       }\r
6870       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6871 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6872       POINT pt;\r
6873       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6874       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6875       if (sel.cpMin == sel.cpMax) {\r
6876         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6877         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6878       }\r
6879       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6880         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6881       }\r
6882       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6883       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6884       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6885       MenuPopup(hwnd, pt, hmenu, -1);\r
6886 }\r
6887     }\r
6888     return 0;\r
6889   case WM_RBUTTONUP:\r
6890     if (GetKeyState(VK_SHIFT) & ~1) {\r
6891       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6892         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6893     }\r
6894     return 0;\r
6895   case WM_PASTE:\r
6896     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6897     SetFocus(hInput);\r
6898     return SendMessage(hInput, message, wParam, lParam);\r
6899   case WM_MBUTTONDOWN:\r
6900     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6901   case WM_COMMAND:\r
6902     switch (LOWORD(wParam)) {\r
6903     case IDM_QuickPaste:\r
6904       {\r
6905         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6906         if (sel.cpMin == sel.cpMax) {\r
6907           MessageBeep(MB_ICONEXCLAMATION);\r
6908           return 0;\r
6909         }\r
6910         SendMessage(hwnd, WM_COPY, 0, 0);\r
6911         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6912         SendMessage(hInput, WM_PASTE, 0, 0);\r
6913         SetFocus(hInput);\r
6914         return 0;\r
6915       }\r
6916     case IDM_Cut:\r
6917       SendMessage(hwnd, WM_CUT, 0, 0);\r
6918       return 0;\r
6919     case IDM_Paste:\r
6920       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6921       return 0;\r
6922     case IDM_Copy:\r
6923       SendMessage(hwnd, WM_COPY, 0, 0);\r
6924       return 0;\r
6925     default:\r
6926       {\r
6927         int i = LOWORD(wParam) - IDM_CommandX;\r
6928         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6929             icsTextMenuEntry[i].command != NULL) {\r
6930           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6931                    icsTextMenuEntry[i].getname,\r
6932                    icsTextMenuEntry[i].immediate);\r
6933           return 0;\r
6934         }\r
6935       }\r
6936       break;\r
6937     }\r
6938     break;\r
6939   }\r
6940   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6941 }\r
6942 \r
6943 WNDPROC consoleInputWindowProc;\r
6944 \r
6945 LRESULT CALLBACK\r
6946 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6947 {\r
6948   char buf[MSG_SIZ];\r
6949   char *p;\r
6950   static BOOL sendNextChar = FALSE;\r
6951   static BOOL quoteNextChar = FALSE;\r
6952   InputSource *is = consoleInputSource;\r
6953   CHARFORMAT cf;\r
6954   CHARRANGE sel;\r
6955 \r
6956   switch (message) {\r
6957   case WM_CHAR:\r
6958     if (!appData.localLineEditing || sendNextChar) {\r
6959       is->buf[0] = (CHAR) wParam;\r
6960       is->count = 1;\r
6961       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6962       sendNextChar = FALSE;\r
6963       return 0;\r
6964     }\r
6965     if (quoteNextChar) {\r
6966       buf[0] = (char) wParam;\r
6967       buf[1] = NULLCHAR;\r
6968       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
6969       quoteNextChar = FALSE;\r
6970       return 0;\r
6971     }\r
6972     switch (wParam) {\r
6973     case '\r':   /* Enter key */\r
6974       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
6975       if (consoleEcho) SaveInHistory(is->buf);\r
6976       is->buf[is->count++] = '\n';\r
6977       is->buf[is->count] = NULLCHAR;\r
6978       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6979       if (consoleEcho) {\r
6980         ConsoleOutput(is->buf, is->count, TRUE);\r
6981       } else if (appData.localLineEditing) {\r
6982         ConsoleOutput("\n", 1, TRUE);\r
6983       }\r
6984       /* fall thru */\r
6985     case '\033': /* Escape key */\r
6986       SetWindowText(hwnd, "");\r
6987       cf.cbSize = sizeof(CHARFORMAT);\r
6988       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
6989       if (consoleEcho) {\r
6990         cf.crTextColor = textAttribs[ColorNormal].color;\r
6991       } else {\r
6992         cf.crTextColor = COLOR_ECHOOFF;\r
6993       }\r
6994       cf.dwEffects = textAttribs[ColorNormal].effects;\r
6995       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
6996       return 0;\r
6997     case '\t':   /* Tab key */\r
6998       if (GetKeyState(VK_SHIFT) < 0) {\r
6999         /* shifted */\r
7000         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7001       } else {\r
7002         /* unshifted */\r
7003         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7004         if (buttonDesc[0].hwnd) {\r
7005           SetFocus(buttonDesc[0].hwnd);\r
7006         } else {\r
7007           SetFocus(hwndMain);\r
7008         }\r
7009       }\r
7010       return 0;\r
7011     case '\023': /* Ctrl+S */\r
7012       sendNextChar = TRUE;\r
7013       return 0;\r
7014     case '\021': /* Ctrl+Q */\r
7015       quoteNextChar = TRUE;\r
7016       return 0;\r
7017     JAWS_REPLAY\r
7018     default:\r
7019       break;\r
7020     }\r
7021     break;\r
7022   case WM_KEYDOWN:\r
7023     switch (wParam) {\r
7024     case VK_UP:\r
7025       GetWindowText(hwnd, buf, MSG_SIZ);\r
7026       p = PrevInHistory(buf);\r
7027       if (p != NULL) {\r
7028         SetWindowText(hwnd, p);\r
7029         sel.cpMin = 999999;\r
7030         sel.cpMax = 999999;\r
7031         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7032         return 0;\r
7033       }\r
7034       break;\r
7035     case VK_DOWN:\r
7036       p = NextInHistory();\r
7037       if (p != NULL) {\r
7038         SetWindowText(hwnd, p);\r
7039         sel.cpMin = 999999;\r
7040         sel.cpMax = 999999;\r
7041         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7042         return 0;\r
7043       }\r
7044       break;\r
7045     case VK_HOME:\r
7046     case VK_END:\r
7047       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7048       /* fall thru */\r
7049     case VK_PRIOR:\r
7050     case VK_NEXT:\r
7051       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7052       return 0;\r
7053     }\r
7054     break;\r
7055   case WM_MBUTTONDOWN:\r
7056     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7057       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7058     break;\r
7059   case WM_RBUTTONUP:\r
7060     if (GetKeyState(VK_SHIFT) & ~1) {\r
7061       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7062         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7063     } else {\r
7064       POINT pt;\r
7065       HMENU hmenu;\r
7066       hmenu = LoadMenu(hInst, "InputMenu");\r
7067       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7068       if (sel.cpMin == sel.cpMax) {\r
7069         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7070         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7071       }\r
7072       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7073         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7074       }\r
7075       pt.x = LOWORD(lParam);\r
7076       pt.y = HIWORD(lParam);\r
7077       MenuPopup(hwnd, pt, hmenu, -1);\r
7078     }\r
7079     return 0;\r
7080   case WM_COMMAND:\r
7081     switch (LOWORD(wParam)) { \r
7082     case IDM_Undo:\r
7083       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7084       return 0;\r
7085     case IDM_SelectAll:\r
7086       sel.cpMin = 0;\r
7087       sel.cpMax = -1; /*999999?*/\r
7088       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7089       return 0;\r
7090     case IDM_Cut:\r
7091       SendMessage(hwnd, WM_CUT, 0, 0);\r
7092       return 0;\r
7093     case IDM_Paste:\r
7094       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7095       return 0;\r
7096     case IDM_Copy:\r
7097       SendMessage(hwnd, WM_COPY, 0, 0);\r
7098       return 0;\r
7099     }\r
7100     break;\r
7101   }\r
7102   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7103 }\r
7104 \r
7105 #define CO_MAX  100000\r
7106 #define CO_TRIM   1000\r
7107 \r
7108 LRESULT CALLBACK\r
7109 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7110 {\r
7111   static SnapData sd;\r
7112   HWND hText, hInput;\r
7113   RECT rect;\r
7114   static int sizeX, sizeY;\r
7115   int newSizeX, newSizeY;\r
7116   MINMAXINFO *mmi;\r
7117   WORD wMask;\r
7118 \r
7119   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7120   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7121 \r
7122   switch (message) {\r
7123   case WM_NOTIFY:\r
7124     if (((NMHDR*)lParam)->code == EN_LINK)\r
7125     {\r
7126       ENLINK *pLink = (ENLINK*)lParam;\r
7127       if (pLink->msg == WM_LBUTTONUP)\r
7128       {\r
7129         TEXTRANGE tr;\r
7130 \r
7131         tr.chrg = pLink->chrg;\r
7132         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7133         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7134         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7135         free(tr.lpstrText);\r
7136       }\r
7137     }\r
7138     break;\r
7139   case WM_INITDIALOG: /* message: initialize dialog box */\r
7140     hwndConsole = hDlg;\r
7141     SetFocus(hInput);\r
7142     consoleTextWindowProc = (WNDPROC)\r
7143       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7144     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7145     consoleInputWindowProc = (WNDPROC)\r
7146       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7147     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7148     Colorize(ColorNormal, TRUE);\r
7149     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7150     ChangedConsoleFont();\r
7151     GetClientRect(hDlg, &rect);\r
7152     sizeX = rect.right;\r
7153     sizeY = rect.bottom;\r
7154     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7155         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7156       WINDOWPLACEMENT wp;\r
7157       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7158       wp.length = sizeof(WINDOWPLACEMENT);\r
7159       wp.flags = 0;\r
7160       wp.showCmd = SW_SHOW;\r
7161       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7162       wp.rcNormalPosition.left = wpConsole.x;\r
7163       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7164       wp.rcNormalPosition.top = wpConsole.y;\r
7165       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7166       SetWindowPlacement(hDlg, &wp);\r
7167     }\r
7168 \r
7169    // [HGM] Chessknight's change 2004-07-13\r
7170    else { /* Determine Defaults */\r
7171        WINDOWPLACEMENT wp;\r
7172        wpConsole.x = wpMain.width + 1;\r
7173        wpConsole.y = wpMain.y;\r
7174        wpConsole.width = screenWidth -  wpMain.width;\r
7175        wpConsole.height = wpMain.height;\r
7176        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7177        wp.length = sizeof(WINDOWPLACEMENT);\r
7178        wp.flags = 0;\r
7179        wp.showCmd = SW_SHOW;\r
7180        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7181        wp.rcNormalPosition.left = wpConsole.x;\r
7182        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7183        wp.rcNormalPosition.top = wpConsole.y;\r
7184        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7185        SetWindowPlacement(hDlg, &wp);\r
7186     }\r
7187 \r
7188    // Allow hText to highlight URLs and send notifications on them\r
7189    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7190    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7191    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7192    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
7193 \r
7194     return FALSE;\r
7195 \r
7196   case WM_SETFOCUS:\r
7197     SetFocus(hInput);\r
7198     return 0;\r
7199 \r
7200   case WM_CLOSE:\r
7201     ExitEvent(0);\r
7202     /* not reached */\r
7203     break;\r
7204 \r
7205   case WM_SIZE:\r
7206     if (IsIconic(hDlg)) break;\r
7207     newSizeX = LOWORD(lParam);\r
7208     newSizeY = HIWORD(lParam);\r
7209     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7210       RECT rectText, rectInput;\r
7211       POINT pt;\r
7212       int newTextHeight, newTextWidth;\r
7213       GetWindowRect(hText, &rectText);\r
7214       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7215       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7216       if (newTextHeight < 0) {\r
7217         newSizeY += -newTextHeight;\r
7218         newTextHeight = 0;\r
7219       }\r
7220       SetWindowPos(hText, NULL, 0, 0,\r
7221         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7222       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7223       pt.x = rectInput.left;\r
7224       pt.y = rectInput.top + newSizeY - sizeY;\r
7225       ScreenToClient(hDlg, &pt);\r
7226       SetWindowPos(hInput, NULL, \r
7227         pt.x, pt.y, /* needs client coords */   \r
7228         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7229         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7230     }\r
7231     sizeX = newSizeX;\r
7232     sizeY = newSizeY;\r
7233     break;\r
7234 \r
7235   case WM_GETMINMAXINFO:\r
7236     /* Prevent resizing window too small */\r
7237     mmi = (MINMAXINFO *) lParam;\r
7238     mmi->ptMinTrackSize.x = 100;\r
7239     mmi->ptMinTrackSize.y = 100;\r
7240     break;\r
7241 \r
7242   /* [AS] Snapping */\r
7243   case WM_ENTERSIZEMOVE:\r
7244     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7245 \r
7246   case WM_SIZING:\r
7247     return OnSizing( &sd, hDlg, wParam, lParam );\r
7248 \r
7249   case WM_MOVING:\r
7250     return OnMoving( &sd, hDlg, wParam, lParam );\r
7251 \r
7252   case WM_EXITSIZEMOVE:\r
7253         UpdateICSWidth(hText);\r
7254     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7255   }\r
7256 \r
7257   return DefWindowProc(hDlg, message, wParam, lParam);\r
7258 }\r
7259 \r
7260 \r
7261 VOID\r
7262 ConsoleCreate()\r
7263 {\r
7264   HWND hCons;\r
7265   if (hwndConsole) return;\r
7266   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7267   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7268 }\r
7269 \r
7270 \r
7271 VOID\r
7272 ConsoleOutput(char* data, int length, int forceVisible)\r
7273 {\r
7274   HWND hText;\r
7275   int trim, exlen;\r
7276   char *p, *q;\r
7277   char buf[CO_MAX+1];\r
7278   POINT pEnd;\r
7279   RECT rect;\r
7280   static int delayLF = 0;\r
7281   CHARRANGE savesel, sel;\r
7282 \r
7283   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7284   p = data;\r
7285   q = buf;\r
7286   if (delayLF) {\r
7287     *q++ = '\r';\r
7288     *q++ = '\n';\r
7289     delayLF = 0;\r
7290   }\r
7291   while (length--) {\r
7292     if (*p == '\n') {\r
7293       if (*++p) {\r
7294         *q++ = '\r';\r
7295         *q++ = '\n';\r
7296       } else {\r
7297         delayLF = 1;\r
7298       }\r
7299     } else if (*p == '\007') {\r
7300        MyPlaySound(&sounds[(int)SoundBell]);\r
7301        p++;\r
7302     } else {\r
7303       *q++ = *p++;\r
7304     }\r
7305   }\r
7306   *q = NULLCHAR;\r
7307   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7308   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7309   /* Save current selection */\r
7310   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7311   exlen = GetWindowTextLength(hText);\r
7312   /* Find out whether current end of text is visible */\r
7313   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7314   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7315   /* Trim existing text if it's too long */\r
7316   if (exlen + (q - buf) > CO_MAX) {\r
7317     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7318     sel.cpMin = 0;\r
7319     sel.cpMax = trim;\r
7320     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7321     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7322     exlen -= trim;\r
7323     savesel.cpMin -= trim;\r
7324     savesel.cpMax -= trim;\r
7325     if (exlen < 0) exlen = 0;\r
7326     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7327     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7328   }\r
7329   /* Append the new text */\r
7330   sel.cpMin = exlen;\r
7331   sel.cpMax = exlen;\r
7332   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7333   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7334   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7335   if (forceVisible || exlen == 0 ||\r
7336       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7337        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7338     /* Scroll to make new end of text visible if old end of text\r
7339        was visible or new text is an echo of user typein */\r
7340     sel.cpMin = 9999999;\r
7341     sel.cpMax = 9999999;\r
7342     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7343     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7344     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7345     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7346   }\r
7347   if (savesel.cpMax == exlen || forceVisible) {\r
7348     /* Move insert point to new end of text if it was at the old\r
7349        end of text or if the new text is an echo of user typein */\r
7350     sel.cpMin = 9999999;\r
7351     sel.cpMax = 9999999;\r
7352     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7353   } else {\r
7354     /* Restore previous selection */\r
7355     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7356   }\r
7357   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7358 }\r
7359 \r
7360 /*---------*/\r
7361 \r
7362 \r
7363 void\r
7364 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7365 {\r
7366   char buf[100];\r
7367   char *str;\r
7368   COLORREF oldFg, oldBg;\r
7369   HFONT oldFont;\r
7370   RECT rect;\r
7371 \r
7372   if(copyNumber > 1)
7373     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7374 \r
7375   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7376   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7377   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7378 \r
7379   rect.left = x;\r
7380   rect.right = x + squareSize;\r
7381   rect.top  = y;\r
7382   rect.bottom = y + squareSize;\r
7383   str = buf;\r
7384 \r
7385   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7386                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7387              y, ETO_CLIPPED|ETO_OPAQUE,\r
7388              &rect, str, strlen(str), NULL);\r
7389 \r
7390   (void) SetTextColor(hdc, oldFg);\r
7391   (void) SetBkColor(hdc, oldBg);\r
7392   (void) SelectObject(hdc, oldFont);\r
7393 }\r
7394 \r
7395 void\r
7396 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7397               RECT *rect, char *color, char *flagFell)\r
7398 {\r
7399   char buf[100];\r
7400   char *str;\r
7401   COLORREF oldFg, oldBg;\r
7402   HFONT oldFont;\r
7403 \r
7404   if (appData.clockMode) {\r
7405     if (tinyLayout)\r
7406       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7407     else\r
7408       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7409     str = buf;\r
7410   } else {\r
7411     str = color;\r
7412   }\r
7413 \r
7414   if (highlight) {\r
7415     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7416     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7417   } else {\r
7418     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7419     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7420   }\r
7421   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7422 \r
7423   JAWS_SILENCE\r
7424 \r
7425   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7426              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7427              rect, str, strlen(str), NULL);\r
7428   if(logoHeight > 0 && appData.clockMode) {\r
7429       RECT r;\r
7430       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s %s", buf+7, flagFell);\r
7431       r.top = rect->top + logoHeight/2;\r
7432       r.left = rect->left;\r
7433       r.right = rect->right;\r
7434       r.bottom = rect->bottom;\r
7435       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7436                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7437                  &r, str, strlen(str), NULL);\r
7438   }\r
7439   (void) SetTextColor(hdc, oldFg);\r
7440   (void) SetBkColor(hdc, oldBg);\r
7441   (void) SelectObject(hdc, oldFont);\r
7442 }\r
7443 \r
7444 \r
7445 int\r
7446 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7447            OVERLAPPED *ovl)\r
7448 {\r
7449   int ok, err;\r
7450 \r
7451   /* [AS]  */\r
7452   if( count <= 0 ) {\r
7453     if (appData.debugMode) {\r
7454       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7455     }\r
7456 \r
7457     return ERROR_INVALID_USER_BUFFER;\r
7458   }\r
7459 \r
7460   ResetEvent(ovl->hEvent);\r
7461   ovl->Offset = ovl->OffsetHigh = 0;\r
7462   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7463   if (ok) {\r
7464     err = NO_ERROR;\r
7465   } else {\r
7466     err = GetLastError();\r
7467     if (err == ERROR_IO_PENDING) {\r
7468       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7469       if (ok)\r
7470         err = NO_ERROR;\r
7471       else\r
7472         err = GetLastError();\r
7473     }\r
7474   }\r
7475   return err;\r
7476 }\r
7477 \r
7478 int\r
7479 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7480             OVERLAPPED *ovl)\r
7481 {\r
7482   int ok, err;\r
7483 \r
7484   ResetEvent(ovl->hEvent);\r
7485   ovl->Offset = ovl->OffsetHigh = 0;\r
7486   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7487   if (ok) {\r
7488     err = NO_ERROR;\r
7489   } else {\r
7490     err = GetLastError();\r
7491     if (err == ERROR_IO_PENDING) {\r
7492       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7493       if (ok)\r
7494         err = NO_ERROR;\r
7495       else\r
7496         err = GetLastError();\r
7497     }\r
7498   }\r
7499   return err;\r
7500 }\r
7501 \r
7502 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7503 void CheckForInputBufferFull( InputSource * is )\r
7504 {\r
7505     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7506         /* Look for end of line */\r
7507         char * p = is->buf;\r
7508         \r
7509         while( p < is->next && *p != '\n' ) {\r
7510             p++;\r
7511         }\r
7512 \r
7513         if( p >= is->next ) {\r
7514             if (appData.debugMode) {\r
7515                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7516             }\r
7517 \r
7518             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7519             is->count = (DWORD) -1;\r
7520             is->next = is->buf;\r
7521         }\r
7522     }\r
7523 }\r
7524 \r
7525 DWORD\r
7526 InputThread(LPVOID arg)\r
7527 {\r
7528   InputSource *is;\r
7529   OVERLAPPED ovl;\r
7530 \r
7531   is = (InputSource *) arg;\r
7532   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7533   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7534   while (is->hThread != NULL) {\r
7535     is->error = DoReadFile(is->hFile, is->next,\r
7536                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7537                            &is->count, &ovl);\r
7538     if (is->error == NO_ERROR) {\r
7539       is->next += is->count;\r
7540     } else {\r
7541       if (is->error == ERROR_BROKEN_PIPE) {\r
7542         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7543         is->count = 0;\r
7544       } else {\r
7545         is->count = (DWORD) -1;\r
7546         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7547         break; \r
7548       }\r
7549     }\r
7550 \r
7551     CheckForInputBufferFull( is );\r
7552 \r
7553     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7554 \r
7555     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7556 \r
7557     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7558   }\r
7559 \r
7560   CloseHandle(ovl.hEvent);\r
7561   CloseHandle(is->hFile);\r
7562 \r
7563   if (appData.debugMode) {\r
7564     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7565   }\r
7566 \r
7567   return 0;\r
7568 }\r
7569 \r
7570 \r
7571 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7572 DWORD\r
7573 NonOvlInputThread(LPVOID arg)\r
7574 {\r
7575   InputSource *is;\r
7576   char *p, *q;\r
7577   int i;\r
7578   char prev;\r
7579 \r
7580   is = (InputSource *) arg;\r
7581   while (is->hThread != NULL) {\r
7582     is->error = ReadFile(is->hFile, is->next,\r
7583                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7584                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7585     if (is->error == NO_ERROR) {\r
7586       /* Change CRLF to LF */\r
7587       if (is->next > is->buf) {\r
7588         p = is->next - 1;\r
7589         i = is->count + 1;\r
7590       } else {\r
7591         p = is->next;\r
7592         i = is->count;\r
7593       }\r
7594       q = p;\r
7595       prev = NULLCHAR;\r
7596       while (i > 0) {\r
7597         if (prev == '\r' && *p == '\n') {\r
7598           *(q-1) = '\n';\r
7599           is->count--;\r
7600         } else { \r
7601           *q++ = *p;\r
7602         }\r
7603         prev = *p++;\r
7604         i--;\r
7605       }\r
7606       *q = NULLCHAR;\r
7607       is->next = q;\r
7608     } else {\r
7609       if (is->error == ERROR_BROKEN_PIPE) {\r
7610         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7611         is->count = 0; \r
7612       } else {\r
7613         is->count = (DWORD) -1;\r
7614       }\r
7615     }\r
7616 \r
7617     CheckForInputBufferFull( is );\r
7618 \r
7619     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7620 \r
7621     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7622 \r
7623     if (is->count < 0) break;  /* Quit on error */\r
7624   }\r
7625   CloseHandle(is->hFile);\r
7626   return 0;\r
7627 }\r
7628 \r
7629 DWORD\r
7630 SocketInputThread(LPVOID arg)\r
7631 {\r
7632   InputSource *is;\r
7633 \r
7634   is = (InputSource *) arg;\r
7635   while (is->hThread != NULL) {\r
7636     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7637     if ((int)is->count == SOCKET_ERROR) {\r
7638       is->count = (DWORD) -1;\r
7639       is->error = WSAGetLastError();\r
7640     } else {\r
7641       is->error = NO_ERROR;\r
7642       is->next += is->count;\r
7643       if (is->count == 0 && is->second == is) {\r
7644         /* End of file on stderr; quit with no message */\r
7645         break;\r
7646       }\r
7647     }\r
7648     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7649 \r
7650     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7651 \r
7652     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7653   }\r
7654   return 0;\r
7655 }\r
7656 \r
7657 VOID\r
7658 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7659 {\r
7660   InputSource *is;\r
7661 \r
7662   is = (InputSource *) lParam;\r
7663   if (is->lineByLine) {\r
7664     /* Feed in lines one by one */\r
7665     char *p = is->buf;\r
7666     char *q = p;\r
7667     while (q < is->next) {\r
7668       if (*q++ == '\n') {\r
7669         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7670         p = q;\r
7671       }\r
7672     }\r
7673     \r
7674     /* Move any partial line to the start of the buffer */\r
7675     q = is->buf;\r
7676     while (p < is->next) {\r
7677       *q++ = *p++;\r
7678     }\r
7679     is->next = q;\r
7680 \r
7681     if (is->error != NO_ERROR || is->count == 0) {\r
7682       /* Notify backend of the error.  Note: If there was a partial\r
7683          line at the end, it is not flushed through. */\r
7684       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7685     }\r
7686   } else {\r
7687     /* Feed in the whole chunk of input at once */\r
7688     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7689     is->next = is->buf;\r
7690   }\r
7691 }\r
7692 \r
7693 /*---------------------------------------------------------------------------*\\r
7694  *\r
7695  *  Menu enables. Used when setting various modes.\r
7696  *\r
7697 \*---------------------------------------------------------------------------*/\r
7698 \r
7699 typedef struct {\r
7700   int item;\r
7701   int flags;\r
7702 } Enables;\r
7703 \r
7704 VOID\r
7705 GreyRevert(Boolean grey)\r
7706 { // [HGM] vari: for retracting variations in local mode\r
7707   HMENU hmenu = GetMenu(hwndMain);\r
7708   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7709   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7710 }\r
7711 \r
7712 VOID\r
7713 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7714 {\r
7715   while (enab->item > 0) {\r
7716     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7717     enab++;\r
7718   }\r
7719 }\r
7720 \r
7721 Enables gnuEnables[] = {\r
7722   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7723   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7724   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7725   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7726   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7727   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7728   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7729   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7730   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7731   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7732   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7733   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7734   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7735   { -1, -1 }\r
7736 };\r
7737 \r
7738 Enables icsEnables[] = {\r
7739   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7740   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7741   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7742   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7743   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7744   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7745   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7746   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7747   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7748   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7749   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7750   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7751   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7752   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7753   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7754   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7755   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7756   { -1, -1 }\r
7757 };\r
7758 \r
7759 #if ZIPPY\r
7760 Enables zippyEnables[] = {\r
7761   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7762   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7763   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7764   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7765   { -1, -1 }\r
7766 };\r
7767 #endif\r
7768 \r
7769 Enables ncpEnables[] = {\r
7770   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7771   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7772   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7773   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7774   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7775   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7776   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7777   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7778   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7779   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7780   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7781   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7782   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7783   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7784   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7785   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7786   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7787   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7788   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7789   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7790   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7791   { -1, -1 }\r
7792 };\r
7793 \r
7794 Enables trainingOnEnables[] = {\r
7795   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7796   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7797   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7798   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7799   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7800   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7801   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7802   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7803   { -1, -1 }\r
7804 };\r
7805 \r
7806 Enables trainingOffEnables[] = {\r
7807   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7808   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7809   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7810   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7811   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7812   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7813   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7814   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7815   { -1, -1 }\r
7816 };\r
7817 \r
7818 /* These modify either ncpEnables or gnuEnables */\r
7819 Enables cmailEnables[] = {\r
7820   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7821   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7822   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7823   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7824   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7825   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7826   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7827   { -1, -1 }\r
7828 };\r
7829 \r
7830 Enables machineThinkingEnables[] = {\r
7831   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7832   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7833   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7834   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7835   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7836   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7837   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7838   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7839   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7840   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7841   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7842   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7843   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7844   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7845   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7846   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7847   { -1, -1 }\r
7848 };\r
7849 \r
7850 Enables userThinkingEnables[] = {\r
7851   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7852   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7853   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7854   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7855   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7856   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7857   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7858   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7859   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7860   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7861   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7862   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7863   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7864   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7865   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7866   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7867   { -1, -1 }\r
7868 };\r
7869 \r
7870 /*---------------------------------------------------------------------------*\\r
7871  *\r
7872  *  Front-end interface functions exported by XBoard.\r
7873  *  Functions appear in same order as prototypes in frontend.h.\r
7874  * \r
7875 \*---------------------------------------------------------------------------*/\r
7876 VOID\r
7877 ModeHighlight()\r
7878 {\r
7879   static UINT prevChecked = 0;\r
7880   static int prevPausing = 0;\r
7881   UINT nowChecked;\r
7882 \r
7883   if (pausing != prevPausing) {\r
7884     prevPausing = pausing;\r
7885     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7886                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7887     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7888   }\r
7889 \r
7890   switch (gameMode) {\r
7891   case BeginningOfGame:\r
7892     if (appData.icsActive)\r
7893       nowChecked = IDM_IcsClient;\r
7894     else if (appData.noChessProgram)\r
7895       nowChecked = IDM_EditGame;\r
7896     else\r
7897       nowChecked = IDM_MachineBlack;\r
7898     break;\r
7899   case MachinePlaysBlack:\r
7900     nowChecked = IDM_MachineBlack;\r
7901     break;\r
7902   case MachinePlaysWhite:\r
7903     nowChecked = IDM_MachineWhite;\r
7904     break;\r
7905   case TwoMachinesPlay:\r
7906     nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match\r
7907     break;\r
7908   case AnalyzeMode:\r
7909     nowChecked = IDM_AnalysisMode;\r
7910     break;\r
7911   case AnalyzeFile:\r
7912     nowChecked = IDM_AnalyzeFile;\r
7913     break;\r
7914   case EditGame:\r
7915     nowChecked = IDM_EditGame;\r
7916     break;\r
7917   case PlayFromGameFile:\r
7918     nowChecked = IDM_LoadGame;\r
7919     break;\r
7920   case EditPosition:\r
7921     nowChecked = IDM_EditPosition;\r
7922     break;\r
7923   case Training:\r
7924     nowChecked = IDM_Training;\r
7925     break;\r
7926   case IcsPlayingWhite:\r
7927   case IcsPlayingBlack:\r
7928   case IcsObserving:\r
7929   case IcsIdle:\r
7930     nowChecked = IDM_IcsClient;\r
7931     break;\r
7932   default:\r
7933   case EndOfGame:\r
7934     nowChecked = 0;\r
7935     break;\r
7936   }\r
7937   if (prevChecked != 0)\r
7938     (void) CheckMenuItem(GetMenu(hwndMain),\r
7939                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7940   if (nowChecked != 0)\r
7941     (void) CheckMenuItem(GetMenu(hwndMain),\r
7942                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
7943 \r
7944   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7945     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7946                           MF_BYCOMMAND|MF_ENABLED);\r
7947   } else {\r
7948     (void) EnableMenuItem(GetMenu(hwndMain), \r
7949                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7950   }\r
7951 \r
7952   prevChecked = nowChecked;\r
7953 \r
7954   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
7955   if (appData.icsActive) {\r
7956        if (appData.icsEngineAnalyze) {\r
7957                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7958                        MF_BYCOMMAND|MF_CHECKED);\r
7959        } else {\r
7960                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7961                        MF_BYCOMMAND|MF_UNCHECKED);\r
7962        }\r
7963   }\r
7964 }\r
7965 \r
7966 VOID\r
7967 SetICSMode()\r
7968 {\r
7969   HMENU hmenu = GetMenu(hwndMain);\r
7970   SetMenuEnables(hmenu, icsEnables);\r
7971   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
7972     MF_BYPOSITION|MF_ENABLED);\r
7973 #if ZIPPY\r
7974   if (appData.zippyPlay) {\r
7975     SetMenuEnables(hmenu, zippyEnables);\r
7976     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
7977          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7978           MF_BYCOMMAND|MF_ENABLED);\r
7979   }\r
7980 #endif\r
7981 }\r
7982 \r
7983 VOID\r
7984 SetGNUMode()\r
7985 {\r
7986   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
7987 }\r
7988 \r
7989 VOID\r
7990 SetNCPMode()\r
7991 {\r
7992   HMENU hmenu = GetMenu(hwndMain);\r
7993   SetMenuEnables(hmenu, ncpEnables);\r
7994   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
7995     MF_BYPOSITION|MF_GRAYED);\r
7996     DrawMenuBar(hwndMain);\r
7997 }\r
7998 \r
7999 VOID\r
8000 SetCmailMode()\r
8001 {\r
8002   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8003 }\r
8004 \r
8005 VOID \r
8006 SetTrainingModeOn()\r
8007 {\r
8008   int i;\r
8009   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8010   for (i = 0; i < N_BUTTONS; i++) {\r
8011     if (buttonDesc[i].hwnd != NULL)\r
8012       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8013   }\r
8014   CommentPopDown();\r
8015 }\r
8016 \r
8017 VOID SetTrainingModeOff()\r
8018 {\r
8019   int i;\r
8020   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8021   for (i = 0; i < N_BUTTONS; i++) {\r
8022     if (buttonDesc[i].hwnd != NULL)\r
8023       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8024   }\r
8025 }\r
8026 \r
8027 \r
8028 VOID\r
8029 SetUserThinkingEnables()\r
8030 {\r
8031   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8032 }\r
8033 \r
8034 VOID\r
8035 SetMachineThinkingEnables()\r
8036 {\r
8037   HMENU hMenu = GetMenu(hwndMain);\r
8038   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8039 \r
8040   SetMenuEnables(hMenu, machineThinkingEnables);\r
8041 \r
8042   if (gameMode == MachinePlaysBlack) {\r
8043     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8044   } else if (gameMode == MachinePlaysWhite) {\r
8045     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8046   } else if (gameMode == TwoMachinesPlay) {\r
8047     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8048   }\r
8049 }\r
8050 \r
8051 \r
8052 VOID\r
8053 DisplayTitle(char *str)\r
8054 {\r
8055   char title[MSG_SIZ], *host;\r
8056   if (str[0] != NULLCHAR) {\r
8057     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8058   } else if (appData.icsActive) {\r
8059     if (appData.icsCommPort[0] != NULLCHAR)\r
8060       host = "ICS";\r
8061     else \r
8062       host = appData.icsHost;\r
8063       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8064   } else if (appData.noChessProgram) {\r
8065     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8066   } else {\r
8067     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8068     strcat(title, ": ");\r
8069     strcat(title, first.tidy);\r
8070   }\r
8071   SetWindowText(hwndMain, title);\r
8072 }\r
8073 \r
8074 \r
8075 VOID\r
8076 DisplayMessage(char *str1, char *str2)\r
8077 {\r
8078   HDC hdc;\r
8079   HFONT oldFont;\r
8080   int remain = MESSAGE_TEXT_MAX - 1;\r
8081   int len;\r
8082 \r
8083   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8084   messageText[0] = NULLCHAR;\r
8085   if (*str1) {\r
8086     len = strlen(str1);\r
8087     if (len > remain) len = remain;\r
8088     strncpy(messageText, str1, len);\r
8089     messageText[len] = NULLCHAR;\r
8090     remain -= len;\r
8091   }\r
8092   if (*str2 && remain >= 2) {\r
8093     if (*str1) {\r
8094       strcat(messageText, "  ");\r
8095       remain -= 2;\r
8096     }\r
8097     len = strlen(str2);\r
8098     if (len > remain) len = remain;\r
8099     strncat(messageText, str2, len);\r
8100   }\r
8101   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8102 \r
8103   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8104 \r
8105   SAYMACHINEMOVE();\r
8106 \r
8107   hdc = GetDC(hwndMain);\r
8108   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8109   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8110              &messageRect, messageText, strlen(messageText), NULL);\r
8111   (void) SelectObject(hdc, oldFont);\r
8112   (void) ReleaseDC(hwndMain, hdc);\r
8113 }\r
8114 \r
8115 VOID\r
8116 DisplayError(char *str, int error)\r
8117 {\r
8118   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8119   int len;\r
8120 \r
8121   if (error == 0) {\r
8122     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8123   } else {\r
8124     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8125                         NULL, error, LANG_NEUTRAL,\r
8126                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8127     if (len > 0) {\r
8128       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8129     } else {\r
8130       ErrorMap *em = errmap;\r
8131       while (em->err != 0 && em->err != error) em++;\r
8132       if (em->err != 0) {\r
8133         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8134       } else {\r
8135         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8136       }\r
8137     }\r
8138   }\r
8139   \r
8140   ErrorPopUp(_("Error"), buf);\r
8141 }\r
8142 \r
8143 \r
8144 VOID\r
8145 DisplayMoveError(char *str)\r
8146 {\r
8147   fromX = fromY = -1;\r
8148   ClearHighlights();\r
8149   DrawPosition(FALSE, NULL);\r
8150   if (appData.popupMoveErrors) {\r
8151     ErrorPopUp(_("Error"), str);\r
8152   } else {\r
8153     DisplayMessage(str, "");\r
8154     moveErrorMessageUp = TRUE;\r
8155   }\r
8156 }\r
8157 \r
8158 VOID\r
8159 DisplayFatalError(char *str, int error, int exitStatus)\r
8160 {\r
8161   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8162   int len;\r
8163   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8164 \r
8165   if (error != 0) {\r
8166     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8167                         NULL, error, LANG_NEUTRAL,\r
8168                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8169     if (len > 0) {\r
8170       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8171     } else {\r
8172       ErrorMap *em = errmap;\r
8173       while (em->err != 0 && em->err != error) em++;\r
8174       if (em->err != 0) {\r
8175         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8176       } else {\r
8177         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8178       }\r
8179     }\r
8180     str = buf;\r
8181   }\r
8182   if (appData.debugMode) {\r
8183     fprintf(debugFP, "%s: %s\n", label, str);\r
8184   }\r
8185   if (appData.popupExitMessage) {\r
8186     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8187                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8188   }\r
8189   ExitEvent(exitStatus);\r
8190 }\r
8191 \r
8192 \r
8193 VOID\r
8194 DisplayInformation(char *str)\r
8195 {\r
8196   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8197 }\r
8198 \r
8199 \r
8200 VOID\r
8201 DisplayNote(char *str)\r
8202 {\r
8203   ErrorPopUp(_("Note"), str);\r
8204 }\r
8205 \r
8206 \r
8207 typedef struct {\r
8208   char *title, *question, *replyPrefix;\r
8209   ProcRef pr;\r
8210 } QuestionParams;\r
8211 \r
8212 LRESULT CALLBACK\r
8213 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8214 {\r
8215   static QuestionParams *qp;\r
8216   char reply[MSG_SIZ];\r
8217   int len, err;\r
8218 \r
8219   switch (message) {\r
8220   case WM_INITDIALOG:\r
8221     qp = (QuestionParams *) lParam;\r
8222     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8223     Translate(hDlg, DLG_Question);\r
8224     SetWindowText(hDlg, qp->title);\r
8225     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8226     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8227     return FALSE;\r
8228 \r
8229   case WM_COMMAND:\r
8230     switch (LOWORD(wParam)) {\r
8231     case IDOK:\r
8232       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8233       if (*reply) strcat(reply, " ");\r
8234       len = strlen(reply);\r
8235       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8236       strcat(reply, "\n");\r
8237       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8238       EndDialog(hDlg, TRUE);\r
8239       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8240       return TRUE;\r
8241     case IDCANCEL:\r
8242       EndDialog(hDlg, FALSE);\r
8243       return TRUE;\r
8244     default:\r
8245       break;\r
8246     }\r
8247     break;\r
8248   }\r
8249   return FALSE;\r
8250 }\r
8251 \r
8252 VOID\r
8253 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8254 {\r
8255     QuestionParams qp;\r
8256     FARPROC lpProc;\r
8257     \r
8258     qp.title = title;\r
8259     qp.question = question;\r
8260     qp.replyPrefix = replyPrefix;\r
8261     qp.pr = pr;\r
8262     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8263     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8264       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8265     FreeProcInstance(lpProc);\r
8266 }\r
8267 \r
8268 /* [AS] Pick FRC position */\r
8269 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8270 {\r
8271     static int * lpIndexFRC;\r
8272     BOOL index_is_ok;\r
8273     char buf[16];\r
8274 \r
8275     switch( message )\r
8276     {\r
8277     case WM_INITDIALOG:\r
8278         lpIndexFRC = (int *) lParam;\r
8279 \r
8280         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8281         Translate(hDlg, DLG_NewGameFRC);\r
8282 \r
8283         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8284         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8285         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8286         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8287 \r
8288         break;\r
8289 \r
8290     case WM_COMMAND:\r
8291         switch( LOWORD(wParam) ) {\r
8292         case IDOK:\r
8293             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8294             EndDialog( hDlg, 0 );\r
8295             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8296             return TRUE;\r
8297         case IDCANCEL:\r
8298             EndDialog( hDlg, 1 );   \r
8299             return TRUE;\r
8300         case IDC_NFG_Edit:\r
8301             if( HIWORD(wParam) == EN_CHANGE ) {\r
8302                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8303 \r
8304                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8305             }\r
8306             return TRUE;\r
8307         case IDC_NFG_Random:\r
8308           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8309             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8310             return TRUE;\r
8311         }\r
8312 \r
8313         break;\r
8314     }\r
8315 \r
8316     return FALSE;\r
8317 }\r
8318 \r
8319 int NewGameFRC()\r
8320 {\r
8321     int result;\r
8322     int index = appData.defaultFrcPosition;\r
8323     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8324 \r
8325     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8326 \r
8327     if( result == 0 ) {\r
8328         appData.defaultFrcPosition = index;\r
8329     }\r
8330 \r
8331     return result;\r
8332 }\r
8333 \r
8334 /* [AS] Game list options. Refactored by HGM */\r
8335 \r
8336 HWND gameListOptionsDialog;\r
8337 \r
8338 // low-level front-end: clear text edit / list widget\r
8339 void\r
8340 GLT_ClearList()\r
8341 {\r
8342     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8343 }\r
8344 \r
8345 // low-level front-end: clear text edit / list widget\r
8346 void\r
8347 GLT_DeSelectList()\r
8348 {\r
8349     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8350 }\r
8351 \r
8352 // low-level front-end: append line to text edit / list widget\r
8353 void\r
8354 GLT_AddToList( char *name )\r
8355 {\r
8356     if( name != 0 ) {\r
8357             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8358     }\r
8359 }\r
8360 \r
8361 // low-level front-end: get line from text edit / list widget\r
8362 Boolean\r
8363 GLT_GetFromList( int index, char *name )\r
8364 {\r
8365     if( name != 0 ) {\r
8366             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8367                 return TRUE;\r
8368     }\r
8369     return FALSE;\r
8370 }\r
8371 \r
8372 void GLT_MoveSelection( HWND hDlg, int delta )\r
8373 {\r
8374     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8375     int idx2 = idx1 + delta;\r
8376     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8377 \r
8378     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8379         char buf[128];\r
8380 \r
8381         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8382         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8383         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8384         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8385     }\r
8386 }\r
8387 \r
8388 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8389 {\r
8390     switch( message )\r
8391     {\r
8392     case WM_INITDIALOG:\r
8393         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8394         \r
8395         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8396         Translate(hDlg, DLG_GameListOptions);\r
8397 \r
8398         /* Initialize list */\r
8399         GLT_TagsToList( lpUserGLT );\r
8400 \r
8401         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8402 \r
8403         break;\r
8404 \r
8405     case WM_COMMAND:\r
8406         switch( LOWORD(wParam) ) {\r
8407         case IDOK:\r
8408             GLT_ParseList();\r
8409             EndDialog( hDlg, 0 );\r
8410             return TRUE;\r
8411         case IDCANCEL:\r
8412             EndDialog( hDlg, 1 );\r
8413             return TRUE;\r
8414 \r
8415         case IDC_GLT_Default:\r
8416             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8417             return TRUE;\r
8418 \r
8419         case IDC_GLT_Restore:\r
8420             GLT_TagsToList( appData.gameListTags );\r
8421             return TRUE;\r
8422 \r
8423         case IDC_GLT_Up:\r
8424             GLT_MoveSelection( hDlg, -1 );\r
8425             return TRUE;\r
8426 \r
8427         case IDC_GLT_Down:\r
8428             GLT_MoveSelection( hDlg, +1 );\r
8429             return TRUE;\r
8430         }\r
8431 \r
8432         break;\r
8433     }\r
8434 \r
8435     return FALSE;\r
8436 }\r
8437 \r
8438 int GameListOptions()\r
8439 {\r
8440     int result;\r
8441     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8442 \r
8443       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8444 \r
8445     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8446 \r
8447     if( result == 0 ) {\r
8448         /* [AS] Memory leak here! */\r
8449         appData.gameListTags = strdup( lpUserGLT ); \r
8450     }\r
8451 \r
8452     return result;\r
8453 }\r
8454 \r
8455 VOID\r
8456 DisplayIcsInteractionTitle(char *str)\r
8457 {\r
8458   char consoleTitle[MSG_SIZ];\r
8459 \r
8460     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8461   SetWindowText(hwndConsole, consoleTitle);\r
8462 }\r
8463 \r
8464 void\r
8465 DrawPosition(int fullRedraw, Board board)\r
8466 {\r
8467   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8468 }\r
8469 \r
8470 void NotifyFrontendLogin()\r
8471 {\r
8472         if (hwndConsole)\r
8473                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8474 }\r
8475 \r
8476 VOID\r
8477 ResetFrontEnd()\r
8478 {\r
8479   fromX = fromY = -1;\r
8480   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8481     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8482     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8483     dragInfo.lastpos = dragInfo.pos;\r
8484     dragInfo.start.x = dragInfo.start.y = -1;\r
8485     dragInfo.from = dragInfo.start;\r
8486     ReleaseCapture();\r
8487     DrawPosition(TRUE, NULL);\r
8488   }\r
8489   TagsPopDown();\r
8490 }\r
8491 \r
8492 \r
8493 VOID\r
8494 CommentPopUp(char *title, char *str)\r
8495 {\r
8496   HWND hwnd = GetActiveWindow();\r
8497   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8498   SAY(str);\r
8499   SetActiveWindow(hwnd);\r
8500 }\r
8501 \r
8502 VOID\r
8503 CommentPopDown(void)\r
8504 {\r
8505   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
8506   if (commentDialog) {\r
8507     ShowWindow(commentDialog, SW_HIDE);\r
8508   }\r
8509   commentUp = FALSE;\r
8510 }\r
8511 \r
8512 VOID\r
8513 EditCommentPopUp(int index, char *title, char *str)\r
8514 {\r
8515   EitherCommentPopUp(index, title, str, TRUE);\r
8516 }\r
8517 \r
8518 \r
8519 VOID\r
8520 RingBell()\r
8521 {\r
8522   MyPlaySound(&sounds[(int)SoundMove]);\r
8523 }\r
8524 \r
8525 VOID PlayIcsWinSound()\r
8526 {\r
8527   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8528 }\r
8529 \r
8530 VOID PlayIcsLossSound()\r
8531 {\r
8532   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8533 }\r
8534 \r
8535 VOID PlayIcsDrawSound()\r
8536 {\r
8537   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8538 }\r
8539 \r
8540 VOID PlayIcsUnfinishedSound()\r
8541 {\r
8542   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8543 }\r
8544 \r
8545 VOID\r
8546 PlayAlarmSound()\r
8547 {\r
8548   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8549 }\r
8550 \r
8551 \r
8552 VOID\r
8553 EchoOn()\r
8554 {\r
8555   HWND hInput;\r
8556   consoleEcho = TRUE;\r
8557   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8558   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8559   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8560 }\r
8561 \r
8562 \r
8563 VOID\r
8564 EchoOff()\r
8565 {\r
8566   CHARFORMAT cf;\r
8567   HWND hInput;\r
8568   consoleEcho = FALSE;\r
8569   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8570   /* This works OK: set text and background both to the same color */\r
8571   cf = consoleCF;\r
8572   cf.crTextColor = COLOR_ECHOOFF;\r
8573   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8574   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8575 }\r
8576 \r
8577 /* No Raw()...? */\r
8578 \r
8579 void Colorize(ColorClass cc, int continuation)\r
8580 {\r
8581   currentColorClass = cc;\r
8582   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8583   consoleCF.crTextColor = textAttribs[cc].color;\r
8584   consoleCF.dwEffects = textAttribs[cc].effects;\r
8585   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8586 }\r
8587 \r
8588 char *\r
8589 UserName()\r
8590 {\r
8591   static char buf[MSG_SIZ];\r
8592   DWORD bufsiz = MSG_SIZ;\r
8593 \r
8594   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8595         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8596   }\r
8597   if (!GetUserName(buf, &bufsiz)) {\r
8598     /*DisplayError("Error getting user name", GetLastError());*/\r
8599     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8600   }\r
8601   return buf;\r
8602 }\r
8603 \r
8604 char *\r
8605 HostName()\r
8606 {\r
8607   static char buf[MSG_SIZ];\r
8608   DWORD bufsiz = MSG_SIZ;\r
8609 \r
8610   if (!GetComputerName(buf, &bufsiz)) {\r
8611     /*DisplayError("Error getting host name", GetLastError());*/\r
8612     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8613   }\r
8614   return buf;\r
8615 }\r
8616 \r
8617 \r
8618 int\r
8619 ClockTimerRunning()\r
8620 {\r
8621   return clockTimerEvent != 0;\r
8622 }\r
8623 \r
8624 int\r
8625 StopClockTimer()\r
8626 {\r
8627   if (clockTimerEvent == 0) return FALSE;\r
8628   KillTimer(hwndMain, clockTimerEvent);\r
8629   clockTimerEvent = 0;\r
8630   return TRUE;\r
8631 }\r
8632 \r
8633 void\r
8634 StartClockTimer(long millisec)\r
8635 {\r
8636   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8637                              (UINT) millisec, NULL);\r
8638 }\r
8639 \r
8640 void\r
8641 DisplayWhiteClock(long timeRemaining, int highlight)\r
8642 {\r
8643   HDC hdc;\r
8644   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8645 \r
8646   if(appData.noGUI) return;\r
8647   hdc = GetDC(hwndMain);\r
8648   if (!IsIconic(hwndMain)) {\r
8649     DisplayAClock(hdc, timeRemaining, highlight, \r
8650                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8651   }\r
8652   if (highlight && iconCurrent == iconBlack) {\r
8653     iconCurrent = iconWhite;\r
8654     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8655     if (IsIconic(hwndMain)) {\r
8656       DrawIcon(hdc, 2, 2, iconCurrent);\r
8657     }\r
8658   }\r
8659   (void) ReleaseDC(hwndMain, hdc);\r
8660   if (hwndConsole)\r
8661     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8662 }\r
8663 \r
8664 void\r
8665 DisplayBlackClock(long timeRemaining, int highlight)\r
8666 {\r
8667   HDC hdc;\r
8668   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8669 \r
8670   if(appData.noGUI) return;\r
8671   hdc = GetDC(hwndMain);\r
8672   if (!IsIconic(hwndMain)) {\r
8673     DisplayAClock(hdc, timeRemaining, highlight, \r
8674                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8675   }\r
8676   if (highlight && iconCurrent == iconWhite) {\r
8677     iconCurrent = iconBlack;\r
8678     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8679     if (IsIconic(hwndMain)) {\r
8680       DrawIcon(hdc, 2, 2, iconCurrent);\r
8681     }\r
8682   }\r
8683   (void) ReleaseDC(hwndMain, hdc);\r
8684   if (hwndConsole)\r
8685     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8686 }\r
8687 \r
8688 \r
8689 int\r
8690 LoadGameTimerRunning()\r
8691 {\r
8692   return loadGameTimerEvent != 0;\r
8693 }\r
8694 \r
8695 int\r
8696 StopLoadGameTimer()\r
8697 {\r
8698   if (loadGameTimerEvent == 0) return FALSE;\r
8699   KillTimer(hwndMain, loadGameTimerEvent);\r
8700   loadGameTimerEvent = 0;\r
8701   return TRUE;\r
8702 }\r
8703 \r
8704 void\r
8705 StartLoadGameTimer(long millisec)\r
8706 {\r
8707   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8708                                 (UINT) millisec, NULL);\r
8709 }\r
8710 \r
8711 void\r
8712 AutoSaveGame()\r
8713 {\r
8714   char *defName;\r
8715   FILE *f;\r
8716   char fileTitle[MSG_SIZ];\r
8717 \r
8718   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8719   f = OpenFileDialog(hwndMain, "a", defName,\r
8720                      appData.oldSaveStyle ? "gam" : "pgn",\r
8721                      GAME_FILT, \r
8722                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8723   if (f != NULL) {\r
8724     SaveGame(f, 0, "");\r
8725     fclose(f);\r
8726   }\r
8727 }\r
8728 \r
8729 \r
8730 void\r
8731 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8732 {\r
8733   if (delayedTimerEvent != 0) {\r
8734     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8735       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8736     }\r
8737     KillTimer(hwndMain, delayedTimerEvent);\r
8738     delayedTimerEvent = 0;\r
8739     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8740     delayedTimerCallback();\r
8741   }\r
8742   delayedTimerCallback = cb;\r
8743   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8744                                 (UINT) millisec, NULL);\r
8745 }\r
8746 \r
8747 DelayedEventCallback\r
8748 GetDelayedEvent()\r
8749 {\r
8750   if (delayedTimerEvent) {\r
8751     return delayedTimerCallback;\r
8752   } else {\r
8753     return NULL;\r
8754   }\r
8755 }\r
8756 \r
8757 void\r
8758 CancelDelayedEvent()\r
8759 {\r
8760   if (delayedTimerEvent) {\r
8761     KillTimer(hwndMain, delayedTimerEvent);\r
8762     delayedTimerEvent = 0;\r
8763   }\r
8764 }\r
8765 \r
8766 DWORD GetWin32Priority(int nice)\r
8767 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8768 /*\r
8769 REALTIME_PRIORITY_CLASS     0x00000100\r
8770 HIGH_PRIORITY_CLASS         0x00000080\r
8771 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8772 NORMAL_PRIORITY_CLASS       0x00000020\r
8773 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8774 IDLE_PRIORITY_CLASS         0x00000040\r
8775 */\r
8776         if (nice < -15) return 0x00000080;\r
8777         if (nice < 0)   return 0x00008000;\r
8778         if (nice == 0)  return 0x00000020;\r
8779         if (nice < 15)  return 0x00004000;\r
8780         return 0x00000040;\r
8781 }\r
8782 \r
8783 /* Start a child process running the given program.\r
8784    The process's standard output can be read from "from", and its\r
8785    standard input can be written to "to".\r
8786    Exit with fatal error if anything goes wrong.\r
8787    Returns an opaque pointer that can be used to destroy the process\r
8788    later.\r
8789 */\r
8790 int\r
8791 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8792 {\r
8793 #define BUFSIZE 4096\r
8794 \r
8795   HANDLE hChildStdinRd, hChildStdinWr,\r
8796     hChildStdoutRd, hChildStdoutWr;\r
8797   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8798   SECURITY_ATTRIBUTES saAttr;\r
8799   BOOL fSuccess;\r
8800   PROCESS_INFORMATION piProcInfo;\r
8801   STARTUPINFO siStartInfo;\r
8802   ChildProc *cp;\r
8803   char buf[MSG_SIZ];\r
8804   DWORD err;\r
8805 \r
8806   if (appData.debugMode) {\r
8807     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8808   }\r
8809 \r
8810   *pr = NoProc;\r
8811 \r
8812   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8813   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8814   saAttr.bInheritHandle = TRUE;\r
8815   saAttr.lpSecurityDescriptor = NULL;\r
8816 \r
8817   /*\r
8818    * The steps for redirecting child's STDOUT:\r
8819    *     1. Create anonymous pipe to be STDOUT for child.\r
8820    *     2. Create a noninheritable duplicate of read handle,\r
8821    *         and close the inheritable read handle.\r
8822    */\r
8823 \r
8824   /* Create a pipe for the child's STDOUT. */\r
8825   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8826     return GetLastError();\r
8827   }\r
8828 \r
8829   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8830   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8831                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8832                              FALSE,     /* not inherited */\r
8833                              DUPLICATE_SAME_ACCESS);\r
8834   if (! fSuccess) {\r
8835     return GetLastError();\r
8836   }\r
8837   CloseHandle(hChildStdoutRd);\r
8838 \r
8839   /*\r
8840    * The steps for redirecting child's STDIN:\r
8841    *     1. Create anonymous pipe to be STDIN for child.\r
8842    *     2. Create a noninheritable duplicate of write handle,\r
8843    *         and close the inheritable write handle.\r
8844    */\r
8845 \r
8846   /* Create a pipe for the child's STDIN. */\r
8847   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8848     return GetLastError();\r
8849   }\r
8850 \r
8851   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8852   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8853                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8854                              FALSE,     /* not inherited */\r
8855                              DUPLICATE_SAME_ACCESS);\r
8856   if (! fSuccess) {\r
8857     return GetLastError();\r
8858   }\r
8859   CloseHandle(hChildStdinWr);\r
8860 \r
8861   /* Arrange to (1) look in dir for the child .exe file, and\r
8862    * (2) have dir be the child's working directory.  Interpret\r
8863    * dir relative to the directory WinBoard loaded from. */\r
8864   GetCurrentDirectory(MSG_SIZ, buf);\r
8865   SetCurrentDirectory(installDir);\r
8866   SetCurrentDirectory(dir);\r
8867 \r
8868   /* Now create the child process. */\r
8869 \r
8870   siStartInfo.cb = sizeof(STARTUPINFO);\r
8871   siStartInfo.lpReserved = NULL;\r
8872   siStartInfo.lpDesktop = NULL;\r
8873   siStartInfo.lpTitle = NULL;\r
8874   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8875   siStartInfo.cbReserved2 = 0;\r
8876   siStartInfo.lpReserved2 = NULL;\r
8877   siStartInfo.hStdInput = hChildStdinRd;\r
8878   siStartInfo.hStdOutput = hChildStdoutWr;\r
8879   siStartInfo.hStdError = hChildStdoutWr;\r
8880 \r
8881   fSuccess = CreateProcess(NULL,\r
8882                            cmdLine,        /* command line */\r
8883                            NULL,           /* process security attributes */\r
8884                            NULL,           /* primary thread security attrs */\r
8885                            TRUE,           /* handles are inherited */\r
8886                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8887                            NULL,           /* use parent's environment */\r
8888                            NULL,\r
8889                            &siStartInfo, /* STARTUPINFO pointer */\r
8890                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8891 \r
8892   err = GetLastError();\r
8893   SetCurrentDirectory(buf); /* return to prev directory */\r
8894   if (! fSuccess) {\r
8895     return err;\r
8896   }\r
8897 \r
8898   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8899     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8900     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8901   }\r
8902 \r
8903   /* Close the handles we don't need in the parent */\r
8904   CloseHandle(piProcInfo.hThread);\r
8905   CloseHandle(hChildStdinRd);\r
8906   CloseHandle(hChildStdoutWr);\r
8907 \r
8908   /* Prepare return value */\r
8909   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8910   cp->kind = CPReal;\r
8911   cp->hProcess = piProcInfo.hProcess;\r
8912   cp->pid = piProcInfo.dwProcessId;\r
8913   cp->hFrom = hChildStdoutRdDup;\r
8914   cp->hTo = hChildStdinWrDup;\r
8915 \r
8916   *pr = (void *) cp;\r
8917 \r
8918   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8919      2000 where engines sometimes don't see the initial command(s)\r
8920      from WinBoard and hang.  I don't understand how that can happen,\r
8921      but the Sleep is harmless, so I've put it in.  Others have also\r
8922      reported what may be the same problem, so hopefully this will fix\r
8923      it for them too.  */\r
8924   Sleep(500);\r
8925 \r
8926   return NO_ERROR;\r
8927 }\r
8928 \r
8929 \r
8930 void\r
8931 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8932 {\r
8933   ChildProc *cp; int result;\r
8934 \r
8935   cp = (ChildProc *) pr;\r
8936   if (cp == NULL) return;\r
8937 \r
8938   switch (cp->kind) {\r
8939   case CPReal:\r
8940     /* TerminateProcess is considered harmful, so... */\r
8941     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8942     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8943     /* The following doesn't work because the chess program\r
8944        doesn't "have the same console" as WinBoard.  Maybe\r
8945        we could arrange for this even though neither WinBoard\r
8946        nor the chess program uses a console for stdio? */\r
8947     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8948 \r
8949     /* [AS] Special termination modes for misbehaving programs... */\r
8950     if( signal == 9 ) { \r
8951         result = TerminateProcess( cp->hProcess, 0 );\r
8952 \r
8953         if ( appData.debugMode) {\r
8954             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
8955         }\r
8956     }\r
8957     else if( signal == 10 ) {\r
8958         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
8959 \r
8960         if( dw != WAIT_OBJECT_0 ) {\r
8961             result = TerminateProcess( cp->hProcess, 0 );\r
8962 \r
8963             if ( appData.debugMode) {\r
8964                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
8965             }\r
8966 \r
8967         }\r
8968     }\r
8969 \r
8970     CloseHandle(cp->hProcess);\r
8971     break;\r
8972 \r
8973   case CPComm:\r
8974     if (cp->hFrom) CloseHandle(cp->hFrom);\r
8975     break;\r
8976 \r
8977   case CPSock:\r
8978     closesocket(cp->sock);\r
8979     WSACleanup();\r
8980     break;\r
8981 \r
8982   case CPRcmd:\r
8983     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
8984     closesocket(cp->sock);\r
8985     closesocket(cp->sock2);\r
8986     WSACleanup();\r
8987     break;\r
8988   }\r
8989   free(cp);\r
8990 }\r
8991 \r
8992 void\r
8993 InterruptChildProcess(ProcRef pr)\r
8994 {\r
8995   ChildProc *cp;\r
8996 \r
8997   cp = (ChildProc *) pr;\r
8998   if (cp == NULL) return;\r
8999   switch (cp->kind) {\r
9000   case CPReal:\r
9001     /* The following doesn't work because the chess program\r
9002        doesn't "have the same console" as WinBoard.  Maybe\r
9003        we could arrange for this even though neither WinBoard\r
9004        nor the chess program uses a console for stdio */\r
9005     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9006     break;\r
9007 \r
9008   case CPComm:\r
9009   case CPSock:\r
9010     /* Can't interrupt */\r
9011     break;\r
9012 \r
9013   case CPRcmd:\r
9014     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9015     break;\r
9016   }\r
9017 }\r
9018 \r
9019 \r
9020 int\r
9021 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9022 {\r
9023   char cmdLine[MSG_SIZ];\r
9024 \r
9025   if (port[0] == NULLCHAR) {\r
9026     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9027   } else {\r
9028     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9029   }\r
9030   return StartChildProcess(cmdLine, "", pr);\r
9031 }\r
9032 \r
9033 \r
9034 /* Code to open TCP sockets */\r
9035 \r
9036 int\r
9037 OpenTCP(char *host, char *port, ProcRef *pr)\r
9038 {\r
9039   ChildProc *cp;\r
9040   int err;\r
9041   SOCKET s;\r
9042   struct sockaddr_in sa, mysa;\r
9043   struct hostent FAR *hp;\r
9044   unsigned short uport;\r
9045   WORD wVersionRequested;\r
9046   WSADATA wsaData;\r
9047 \r
9048   /* Initialize socket DLL */\r
9049   wVersionRequested = MAKEWORD(1, 1);\r
9050   err = WSAStartup(wVersionRequested, &wsaData);\r
9051   if (err != 0) return err;\r
9052 \r
9053   /* Make socket */\r
9054   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9055     err = WSAGetLastError();\r
9056     WSACleanup();\r
9057     return err;\r
9058   }\r
9059 \r
9060   /* Bind local address using (mostly) don't-care values.\r
9061    */\r
9062   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9063   mysa.sin_family = AF_INET;\r
9064   mysa.sin_addr.s_addr = INADDR_ANY;\r
9065   uport = (unsigned short) 0;\r
9066   mysa.sin_port = htons(uport);\r
9067   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9068       == SOCKET_ERROR) {\r
9069     err = WSAGetLastError();\r
9070     WSACleanup();\r
9071     return err;\r
9072   }\r
9073 \r
9074   /* Resolve remote host name */\r
9075   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9076   if (!(hp = gethostbyname(host))) {\r
9077     unsigned int b0, b1, b2, b3;\r
9078 \r
9079     err = WSAGetLastError();\r
9080 \r
9081     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9082       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9083       hp->h_addrtype = AF_INET;\r
9084       hp->h_length = 4;\r
9085       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9086       hp->h_addr_list[0] = (char *) malloc(4);\r
9087       hp->h_addr_list[0][0] = (char) b0;\r
9088       hp->h_addr_list[0][1] = (char) b1;\r
9089       hp->h_addr_list[0][2] = (char) b2;\r
9090       hp->h_addr_list[0][3] = (char) b3;\r
9091     } else {\r
9092       WSACleanup();\r
9093       return err;\r
9094     }\r
9095   }\r
9096   sa.sin_family = hp->h_addrtype;\r
9097   uport = (unsigned short) atoi(port);\r
9098   sa.sin_port = htons(uport);\r
9099   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9100 \r
9101   /* Make connection */\r
9102   if (connect(s, (struct sockaddr *) &sa,\r
9103               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9104     err = WSAGetLastError();\r
9105     WSACleanup();\r
9106     return err;\r
9107   }\r
9108 \r
9109   /* Prepare return value */\r
9110   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9111   cp->kind = CPSock;\r
9112   cp->sock = s;\r
9113   *pr = (ProcRef *) cp;\r
9114 \r
9115   return NO_ERROR;\r
9116 }\r
9117 \r
9118 int\r
9119 OpenCommPort(char *name, ProcRef *pr)\r
9120 {\r
9121   HANDLE h;\r
9122   COMMTIMEOUTS ct;\r
9123   ChildProc *cp;\r
9124   char fullname[MSG_SIZ];\r
9125 \r
9126   if (*name != '\\')\r
9127     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9128   else\r
9129     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9130 \r
9131   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9132                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9133   if (h == (HANDLE) -1) {\r
9134     return GetLastError();\r
9135   }\r
9136   hCommPort = h;\r
9137 \r
9138   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9139 \r
9140   /* Accumulate characters until a 100ms pause, then parse */\r
9141   ct.ReadIntervalTimeout = 100;\r
9142   ct.ReadTotalTimeoutMultiplier = 0;\r
9143   ct.ReadTotalTimeoutConstant = 0;\r
9144   ct.WriteTotalTimeoutMultiplier = 0;\r
9145   ct.WriteTotalTimeoutConstant = 0;\r
9146   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9147 \r
9148   /* Prepare return value */\r
9149   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9150   cp->kind = CPComm;\r
9151   cp->hFrom = h;\r
9152   cp->hTo = h;\r
9153   *pr = (ProcRef *) cp;\r
9154 \r
9155   return NO_ERROR;\r
9156 }\r
9157 \r
9158 int\r
9159 OpenLoopback(ProcRef *pr)\r
9160 {\r
9161   DisplayFatalError(_("Not implemented"), 0, 1);\r
9162   return NO_ERROR;\r
9163 }\r
9164 \r
9165 \r
9166 int\r
9167 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9168 {\r
9169   ChildProc *cp;\r
9170   int err;\r
9171   SOCKET s, s2, s3;\r
9172   struct sockaddr_in sa, mysa;\r
9173   struct hostent FAR *hp;\r
9174   unsigned short uport;\r
9175   WORD wVersionRequested;\r
9176   WSADATA wsaData;\r
9177   int fromPort;\r
9178   char stderrPortStr[MSG_SIZ];\r
9179 \r
9180   /* Initialize socket DLL */\r
9181   wVersionRequested = MAKEWORD(1, 1);\r
9182   err = WSAStartup(wVersionRequested, &wsaData);\r
9183   if (err != 0) return err;\r
9184 \r
9185   /* Resolve remote host name */\r
9186   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9187   if (!(hp = gethostbyname(host))) {\r
9188     unsigned int b0, b1, b2, b3;\r
9189 \r
9190     err = WSAGetLastError();\r
9191 \r
9192     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9193       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9194       hp->h_addrtype = AF_INET;\r
9195       hp->h_length = 4;\r
9196       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9197       hp->h_addr_list[0] = (char *) malloc(4);\r
9198       hp->h_addr_list[0][0] = (char) b0;\r
9199       hp->h_addr_list[0][1] = (char) b1;\r
9200       hp->h_addr_list[0][2] = (char) b2;\r
9201       hp->h_addr_list[0][3] = (char) b3;\r
9202     } else {\r
9203       WSACleanup();\r
9204       return err;\r
9205     }\r
9206   }\r
9207   sa.sin_family = hp->h_addrtype;\r
9208   uport = (unsigned short) 514;\r
9209   sa.sin_port = htons(uport);\r
9210   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9211 \r
9212   /* Bind local socket to unused "privileged" port address\r
9213    */\r
9214   s = INVALID_SOCKET;\r
9215   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9216   mysa.sin_family = AF_INET;\r
9217   mysa.sin_addr.s_addr = INADDR_ANY;\r
9218   for (fromPort = 1023;; fromPort--) {\r
9219     if (fromPort < 0) {\r
9220       WSACleanup();\r
9221       return WSAEADDRINUSE;\r
9222     }\r
9223     if (s == INVALID_SOCKET) {\r
9224       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9225         err = WSAGetLastError();\r
9226         WSACleanup();\r
9227         return err;\r
9228       }\r
9229     }\r
9230     uport = (unsigned short) fromPort;\r
9231     mysa.sin_port = htons(uport);\r
9232     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9233         == SOCKET_ERROR) {\r
9234       err = WSAGetLastError();\r
9235       if (err == WSAEADDRINUSE) continue;\r
9236       WSACleanup();\r
9237       return err;\r
9238     }\r
9239     if (connect(s, (struct sockaddr *) &sa,\r
9240       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9241       err = WSAGetLastError();\r
9242       if (err == WSAEADDRINUSE) {\r
9243         closesocket(s);\r
9244         s = -1;\r
9245         continue;\r
9246       }\r
9247       WSACleanup();\r
9248       return err;\r
9249     }\r
9250     break;\r
9251   }\r
9252 \r
9253   /* Bind stderr local socket to unused "privileged" port address\r
9254    */\r
9255   s2 = INVALID_SOCKET;\r
9256   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9257   mysa.sin_family = AF_INET;\r
9258   mysa.sin_addr.s_addr = INADDR_ANY;\r
9259   for (fromPort = 1023;; fromPort--) {\r
9260     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9261     if (fromPort < 0) {\r
9262       (void) closesocket(s);\r
9263       WSACleanup();\r
9264       return WSAEADDRINUSE;\r
9265     }\r
9266     if (s2 == INVALID_SOCKET) {\r
9267       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9268         err = WSAGetLastError();\r
9269         closesocket(s);\r
9270         WSACleanup();\r
9271         return err;\r
9272       }\r
9273     }\r
9274     uport = (unsigned short) fromPort;\r
9275     mysa.sin_port = htons(uport);\r
9276     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9277         == SOCKET_ERROR) {\r
9278       err = WSAGetLastError();\r
9279       if (err == WSAEADDRINUSE) continue;\r
9280       (void) closesocket(s);\r
9281       WSACleanup();\r
9282       return err;\r
9283     }\r
9284     if (listen(s2, 1) == SOCKET_ERROR) {\r
9285       err = WSAGetLastError();\r
9286       if (err == WSAEADDRINUSE) {\r
9287         closesocket(s2);\r
9288         s2 = INVALID_SOCKET;\r
9289         continue;\r
9290       }\r
9291       (void) closesocket(s);\r
9292       (void) closesocket(s2);\r
9293       WSACleanup();\r
9294       return err;\r
9295     }\r
9296     break;\r
9297   }\r
9298   prevStderrPort = fromPort; // remember port used\r
9299   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9300 \r
9301   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9302     err = WSAGetLastError();\r
9303     (void) closesocket(s);\r
9304     (void) closesocket(s2);\r
9305     WSACleanup();\r
9306     return err;\r
9307   }\r
9308 \r
9309   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9310     err = WSAGetLastError();\r
9311     (void) closesocket(s);\r
9312     (void) closesocket(s2);\r
9313     WSACleanup();\r
9314     return err;\r
9315   }\r
9316   if (*user == NULLCHAR) user = UserName();\r
9317   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9318     err = WSAGetLastError();\r
9319     (void) closesocket(s);\r
9320     (void) closesocket(s2);\r
9321     WSACleanup();\r
9322     return err;\r
9323   }\r
9324   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9325     err = WSAGetLastError();\r
9326     (void) closesocket(s);\r
9327     (void) closesocket(s2);\r
9328     WSACleanup();\r
9329     return err;\r
9330   }\r
9331 \r
9332   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9333     err = WSAGetLastError();\r
9334     (void) closesocket(s);\r
9335     (void) closesocket(s2);\r
9336     WSACleanup();\r
9337     return err;\r
9338   }\r
9339   (void) closesocket(s2);  /* Stop listening */\r
9340 \r
9341   /* Prepare return value */\r
9342   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9343   cp->kind = CPRcmd;\r
9344   cp->sock = s;\r
9345   cp->sock2 = s3;\r
9346   *pr = (ProcRef *) cp;\r
9347 \r
9348   return NO_ERROR;\r
9349 }\r
9350 \r
9351 \r
9352 InputSourceRef\r
9353 AddInputSource(ProcRef pr, int lineByLine,\r
9354                InputCallback func, VOIDSTAR closure)\r
9355 {\r
9356   InputSource *is, *is2 = NULL;\r
9357   ChildProc *cp = (ChildProc *) pr;\r
9358 \r
9359   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9360   is->lineByLine = lineByLine;\r
9361   is->func = func;\r
9362   is->closure = closure;\r
9363   is->second = NULL;\r
9364   is->next = is->buf;\r
9365   if (pr == NoProc) {\r
9366     is->kind = CPReal;\r
9367     consoleInputSource = is;\r
9368   } else {\r
9369     is->kind = cp->kind;\r
9370     /* \r
9371         [AS] Try to avoid a race condition if the thread is given control too early:\r
9372         we create all threads suspended so that the is->hThread variable can be\r
9373         safely assigned, then let the threads start with ResumeThread.\r
9374     */\r
9375     switch (cp->kind) {\r
9376     case CPReal:\r
9377       is->hFile = cp->hFrom;\r
9378       cp->hFrom = NULL; /* now owned by InputThread */\r
9379       is->hThread =\r
9380         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9381                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9382       break;\r
9383 \r
9384     case CPComm:\r
9385       is->hFile = cp->hFrom;\r
9386       cp->hFrom = NULL; /* now owned by InputThread */\r
9387       is->hThread =\r
9388         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9389                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9390       break;\r
9391 \r
9392     case CPSock:\r
9393       is->sock = cp->sock;\r
9394       is->hThread =\r
9395         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9396                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9397       break;\r
9398 \r
9399     case CPRcmd:\r
9400       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9401       *is2 = *is;\r
9402       is->sock = cp->sock;\r
9403       is->second = is2;\r
9404       is2->sock = cp->sock2;\r
9405       is2->second = is2;\r
9406       is->hThread =\r
9407         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9408                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9409       is2->hThread =\r
9410         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9411                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9412       break;\r
9413     }\r
9414 \r
9415     if( is->hThread != NULL ) {\r
9416         ResumeThread( is->hThread );\r
9417     }\r
9418 \r
9419     if( is2 != NULL && is2->hThread != NULL ) {\r
9420         ResumeThread( is2->hThread );\r
9421     }\r
9422   }\r
9423 \r
9424   return (InputSourceRef) is;\r
9425 }\r
9426 \r
9427 void\r
9428 RemoveInputSource(InputSourceRef isr)\r
9429 {\r
9430   InputSource *is;\r
9431 \r
9432   is = (InputSource *) isr;\r
9433   is->hThread = NULL;  /* tell thread to stop */\r
9434   CloseHandle(is->hThread);\r
9435   if (is->second != NULL) {\r
9436     is->second->hThread = NULL;\r
9437     CloseHandle(is->second->hThread);\r
9438   }\r
9439 }\r
9440 \r
9441 int no_wrap(char *message, int count)\r
9442 {\r
9443     ConsoleOutput(message, count, FALSE);\r
9444     return count;\r
9445 }\r
9446 \r
9447 int\r
9448 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9449 {\r
9450   DWORD dOutCount;\r
9451   int outCount = SOCKET_ERROR;\r
9452   ChildProc *cp = (ChildProc *) pr;\r
9453   static OVERLAPPED ovl;\r
9454   static int line = 0;\r
9455 \r
9456   if (pr == NoProc)\r
9457   {\r
9458     if (appData.noJoin || !appData.useInternalWrap)\r
9459       return no_wrap(message, count);\r
9460     else\r
9461     {\r
9462       int width = get_term_width();\r
9463       int len = wrap(NULL, message, count, width, &line);\r
9464       char *msg = malloc(len);\r
9465       int dbgchk;\r
9466 \r
9467       if (!msg)\r
9468         return no_wrap(message, count);\r
9469       else\r
9470       {\r
9471         dbgchk = wrap(msg, message, count, width, &line);\r
9472         if (dbgchk != len && appData.debugMode)\r
9473             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9474         ConsoleOutput(msg, len, FALSE);\r
9475         free(msg);\r
9476         return len;\r
9477       }\r
9478     }\r
9479   }\r
9480 \r
9481   if (ovl.hEvent == NULL) {\r
9482     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9483   }\r
9484   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9485 \r
9486   switch (cp->kind) {\r
9487   case CPSock:\r
9488   case CPRcmd:\r
9489     outCount = send(cp->sock, message, count, 0);\r
9490     if (outCount == SOCKET_ERROR) {\r
9491       *outError = WSAGetLastError();\r
9492     } else {\r
9493       *outError = NO_ERROR;\r
9494     }\r
9495     break;\r
9496 \r
9497   case CPReal:\r
9498     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9499                   &dOutCount, NULL)) {\r
9500       *outError = NO_ERROR;\r
9501       outCount = (int) dOutCount;\r
9502     } else {\r
9503       *outError = GetLastError();\r
9504     }\r
9505     break;\r
9506 \r
9507   case CPComm:\r
9508     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9509                             &dOutCount, &ovl);\r
9510     if (*outError == NO_ERROR) {\r
9511       outCount = (int) dOutCount;\r
9512     }\r
9513     break;\r
9514   }\r
9515   return outCount;\r
9516 }\r
9517 \r
9518 int\r
9519 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9520                        long msdelay)\r
9521 {\r
9522   /* Ignore delay, not implemented for WinBoard */\r
9523   return OutputToProcess(pr, message, count, outError);\r
9524 }\r
9525 \r
9526 \r
9527 void\r
9528 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9529                         char *buf, int count, int error)\r
9530 {\r
9531   DisplayFatalError(_("Not implemented"), 0, 1);\r
9532 }\r
9533 \r
9534 /* see wgamelist.c for Game List functions */\r
9535 /* see wedittags.c for Edit Tags functions */\r
9536 \r
9537 \r
9538 VOID\r
9539 ICSInitScript()\r
9540 {\r
9541   FILE *f;\r
9542   char buf[MSG_SIZ];\r
9543   char *dummy;\r
9544 \r
9545   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9546     f = fopen(buf, "r");\r
9547     if (f != NULL) {\r
9548       ProcessICSInitScript(f);\r
9549       fclose(f);\r
9550     }\r
9551   }\r
9552 }\r
9553 \r
9554 \r
9555 VOID\r
9556 StartAnalysisClock()\r
9557 {\r
9558   if (analysisTimerEvent) return;\r
9559   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9560                                         (UINT) 2000, NULL);\r
9561 }\r
9562 \r
9563 VOID\r
9564 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9565 {\r
9566   highlightInfo.sq[0].x = fromX;\r
9567   highlightInfo.sq[0].y = fromY;\r
9568   highlightInfo.sq[1].x = toX;\r
9569   highlightInfo.sq[1].y = toY;\r
9570 }\r
9571 \r
9572 VOID\r
9573 ClearHighlights()\r
9574 {\r
9575   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9576     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9577 }\r
9578 \r
9579 VOID\r
9580 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9581 {\r
9582   premoveHighlightInfo.sq[0].x = fromX;\r
9583   premoveHighlightInfo.sq[0].y = fromY;\r
9584   premoveHighlightInfo.sq[1].x = toX;\r
9585   premoveHighlightInfo.sq[1].y = toY;\r
9586 }\r
9587 \r
9588 VOID\r
9589 ClearPremoveHighlights()\r
9590 {\r
9591   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9592     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9593 }\r
9594 \r
9595 VOID\r
9596 ShutDownFrontEnd()\r
9597 {\r
9598   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9599   DeleteClipboardTempFiles();\r
9600 }\r
9601 \r
9602 void\r
9603 BoardToTop()\r
9604 {\r
9605     if (IsIconic(hwndMain))\r
9606       ShowWindow(hwndMain, SW_RESTORE);\r
9607 \r
9608     SetActiveWindow(hwndMain);\r
9609 }\r
9610 \r
9611 /*\r
9612  * Prototypes for animation support routines\r
9613  */\r
9614 static void ScreenSquare(int column, int row, POINT * pt);\r
9615 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9616      POINT frames[], int * nFrames);\r
9617 \r
9618 \r
9619 void\r
9620 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
9621 {       // [HGM] atomic: animate blast wave\r
9622         int i;\r
9623 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
9624         explodeInfo.fromX = fromX;\r
9625         explodeInfo.fromY = fromY;\r
9626         explodeInfo.toX = toX;\r
9627         explodeInfo.toY = toY;\r
9628         for(i=1; i<nFrames; i++) {\r
9629             explodeInfo.radius = (i*180)/(nFrames-1);\r
9630             DrawPosition(FALSE, NULL);\r
9631             Sleep(appData.animSpeed);\r
9632         }\r
9633         explodeInfo.radius = 0;\r
9634         DrawPosition(TRUE, NULL);\r
9635 }\r
9636 \r
9637 #define kFactor 4\r
9638 \r
9639 void\r
9640 AnimateMove(board, fromX, fromY, toX, toY)\r
9641      Board board;\r
9642      int fromX;\r
9643      int fromY;\r
9644      int toX;\r
9645      int toY;\r
9646 {\r
9647   ChessSquare piece;\r
9648   POINT start, finish, mid;\r
9649   POINT frames[kFactor * 2 + 1];\r
9650   int nFrames, n;\r
9651 \r
9652   if (!appData.animate) return;\r
9653   if (doingSizing) return;\r
9654   if (fromY < 0 || fromX < 0) return;\r
9655   piece = board[fromY][fromX];\r
9656   if (piece >= EmptySquare) return;\r
9657 \r
9658   ScreenSquare(fromX, fromY, &start);\r
9659   ScreenSquare(toX, toY, &finish);\r
9660 \r
9661   /* All pieces except knights move in straight line */\r
9662   if (piece != WhiteKnight && piece != BlackKnight) {\r
9663     mid.x = start.x + (finish.x - start.x) / 2;\r
9664     mid.y = start.y + (finish.y - start.y) / 2;\r
9665   } else {\r
9666     /* Knight: make diagonal movement then straight */\r
9667     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9668        mid.x = start.x + (finish.x - start.x) / 2;\r
9669        mid.y = finish.y;\r
9670      } else {\r
9671        mid.x = finish.x;\r
9672        mid.y = start.y + (finish.y - start.y) / 2;\r
9673      }\r
9674   }\r
9675   \r
9676   /* Don't use as many frames for very short moves */\r
9677   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9678     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9679   else\r
9680     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9681 \r
9682   animInfo.from.x = fromX;\r
9683   animInfo.from.y = fromY;\r
9684   animInfo.to.x = toX;\r
9685   animInfo.to.y = toY;\r
9686   animInfo.lastpos = start;\r
9687   animInfo.piece = piece;\r
9688   for (n = 0; n < nFrames; n++) {\r
9689     animInfo.pos = frames[n];\r
9690     DrawPosition(FALSE, NULL);\r
9691     animInfo.lastpos = animInfo.pos;\r
9692     Sleep(appData.animSpeed);\r
9693   }\r
9694   animInfo.pos = finish;\r
9695   DrawPosition(FALSE, NULL);\r
9696   animInfo.piece = EmptySquare;\r
9697   if(gameInfo.variant == VariantAtomic && \r
9698      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
9699         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
9700 }\r
9701 \r
9702 /*      Convert board position to corner of screen rect and color       */\r
9703 \r
9704 static void\r
9705 ScreenSquare(column, row, pt)\r
9706      int column; int row; POINT * pt;\r
9707 {\r
9708   if (flipView) {\r
9709     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9710     pt->y = lineGap + row * (squareSize + lineGap);\r
9711   } else {\r
9712     pt->x = lineGap + column * (squareSize + lineGap);\r
9713     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9714   }\r
9715 }\r
9716 \r
9717 /*      Generate a series of frame coords from start->mid->finish.\r
9718         The movement rate doubles until the half way point is\r
9719         reached, then halves back down to the final destination,\r
9720         which gives a nice slow in/out effect. The algorithmn\r
9721         may seem to generate too many intermediates for short\r
9722         moves, but remember that the purpose is to attract the\r
9723         viewers attention to the piece about to be moved and\r
9724         then to where it ends up. Too few frames would be less\r
9725         noticeable.                                             */\r
9726 \r
9727 static void\r
9728 Tween(start, mid, finish, factor, frames, nFrames)\r
9729      POINT * start; POINT * mid;\r
9730      POINT * finish; int factor;\r
9731      POINT frames[]; int * nFrames;\r
9732 {\r
9733   int n, fraction = 1, count = 0;\r
9734 \r
9735   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9736   for (n = 0; n < factor; n++)\r
9737     fraction *= 2;\r
9738   for (n = 0; n < factor; n++) {\r
9739     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9740     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9741     count ++;\r
9742     fraction = fraction / 2;\r
9743   }\r
9744   \r
9745   /* Midpoint */\r
9746   frames[count] = *mid;\r
9747   count ++;\r
9748   \r
9749   /* Slow out, stepping 1/2, then 1/4, ... */\r
9750   fraction = 2;\r
9751   for (n = 0; n < factor; n++) {\r
9752     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9753     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9754     count ++;\r
9755     fraction = fraction * 2;\r
9756   }\r
9757   *nFrames = count;\r
9758 }\r
9759 \r
9760 void\r
9761 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9762 {\r
9763     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9764 \r
9765     EvalGraphSet( first, last, current, pvInfoList );\r
9766 }\r