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