b637fe984513dd4eedb584823757bdc6a1e9d5f6
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 \r
77 #if __GNUC__\r
78 #include <errno.h>\r
79 #include <string.h>\r
80 #endif\r
81 \r
82 #include "common.h"\r
83 #include "frontend.h"\r
84 #include "backend.h"\r
85 #include "winboard.h"\r
86 #include "moves.h"\r
87 #include "wclipbrd.h"\r
88 #include "woptions.h"\r
89 #include "wsockerr.h"\r
90 #include "defaults.h"\r
91 #include "help.h"\r
92 #include "wsnap.h"\r
93 \r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
95 \r
96   int myrandom(void);\r
97   void mysrandom(unsigned int seed);\r
98 \r
99 extern int whiteFlag, blackFlag;\r
100 Boolean flipClock = FALSE;\r
101 extern HANDLE chatHandle[];\r
102 extern int ics_type;\r
103 \r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
105 VOID NewVariantPopup(HWND hwnd);\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
107                    /*char*/int promoChar));\r
108 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
109 void DisplayMove P((int moveNumber));\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
111 void ChatPopUp P((char *s));\r
112 typedef struct {\r
113   ChessSquare piece;  \r
114   POINT pos;      /* window coordinates of current pos */\r
115   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
116   POINT from;     /* board coordinates of the piece's orig pos */\r
117   POINT to;       /* board coordinates of the piece's new pos */\r
118 } AnimInfo;\r
119 \r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
121 \r
122 typedef struct {\r
123   POINT start;    /* window coordinates of start pos */\r
124   POINT pos;      /* window coordinates of current pos */\r
125   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
126   POINT from;     /* board coordinates of the piece's orig pos */\r
127 } DragInfo;\r
128 \r
129 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
130 \r
131 typedef struct {\r
132   POINT sq[2];    /* board coordinates of from, to squares */\r
133 } HighlightInfo;\r
134 \r
135 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
136 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
137 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
138 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
139 \r
140 typedef struct { // [HGM] atomic\r
141   int fromX, fromY, toX, toY, radius;\r
142 } ExplodeInfo;\r
143 \r
144 static ExplodeInfo explodeInfo;\r
145 \r
146 /* Window class names */\r
147 char szAppName[] = "WinBoard";\r
148 char szConsoleName[] = "WBConsole";\r
149 \r
150 /* Title bar text */\r
151 char szTitle[] = "WinBoard";\r
152 char szConsoleTitle[] = "I C S Interaction";\r
153 \r
154 char *programName;\r
155 char *settingsFileName;\r
156 Boolean saveSettingsOnExit;\r
157 char installDir[MSG_SIZ];\r
158 int errorExitStatus;\r
159 \r
160 BoardSize boardSize;\r
161 Boolean chessProgram;\r
162 //static int boardX, boardY;\r
163 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
164 int squareSize, lineGap, minorSize;\r
165 static int winW, winH;\r
166 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
167 static int logoHeight = 0;\r
168 static char messageText[MESSAGE_TEXT_MAX];\r
169 static int clockTimerEvent = 0;\r
170 static int loadGameTimerEvent = 0;\r
171 static int analysisTimerEvent = 0;\r
172 static DelayedEventCallback delayedTimerCallback;\r
173 static int delayedTimerEvent = 0;\r
174 static int buttonCount = 2;\r
175 char *icsTextMenuString;\r
176 char *icsNames;\r
177 char *firstChessProgramNames;\r
178 char *secondChessProgramNames;\r
179 \r
180 #define PALETTESIZE 256\r
181 \r
182 HINSTANCE hInst;          /* current instance */\r
183 Boolean alwaysOnTop = FALSE;\r
184 RECT boardRect;\r
185 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
186   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
187 HPALETTE hPal;\r
188 ColorClass currentColorClass;\r
189 \r
190 HWND hCommPort = NULL;    /* currently open comm port */\r
191 static HWND hwndPause;    /* pause button */\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,\r
194   blackSquareBrush, /* [HGM] for band between board and holdings */\r
195   explodeBrush,     /* [HGM] atomic */\r
196   markerBrush,      /* [HGM] markers */\r
197   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
198 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
199 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
200 static HPEN gridPen = NULL;\r
201 static HPEN highlightPen = NULL;\r
202 static HPEN premovePen = NULL;\r
203 static NPLOGPALETTE pLogPal;\r
204 static BOOL paletteChanged = FALSE;\r
205 static HICON iconWhite, iconBlack, iconCurrent;\r
206 static int doingSizing = FALSE;\r
207 static int lastSizing = 0;\r
208 static int prevStderrPort;\r
209 static HBITMAP userLogo;\r
210 \r
211 static HBITMAP liteBackTexture = NULL;\r
212 static HBITMAP darkBackTexture = NULL;\r
213 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
214 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
215 static int backTextureSquareSize = 0;\r
216 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
217 \r
218 #if __GNUC__ && !defined(_winmajor)\r
219 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
220 #else\r
221 #if defined(_winmajor)\r
222 #define oldDialog (_winmajor < 4)\r
223 #else\r
224 #define oldDialog 0\r
225 #endif\r
226 #endif\r
227 \r
228 #define INTERNATIONAL\r
229 \r
230 #ifdef INTERNATIONAL\r
231 #  define _(s) T_(s)\r
232 #  define N_(s) s\r
233 #else\r
234 #  define _(s) s\r
235 #  define N_(s) s\r
236 #  define T_(s) s\r
237 #  define Translate(x, y)\r
238 #  define LoadLanguageFile(s)\r
239 #endif\r
240 \r
241 #ifdef INTERNATIONAL\r
242 \r
243 Boolean barbaric; // flag indicating if translation is needed\r
244 \r
245 // list of item numbers used in each dialog (used to alter language at run time)\r
246 \r
247 #define ABOUTBOX -1  /* not sure why these are needed */\r
248 #define ABOUTBOX2 -1\r
249 \r
250 int dialogItems[][40] = {\r
251 { ABOUTBOX, IDOK, 400 }, \r
252 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
253   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
254 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL }, \r
255 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
256   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
257 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
258 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
259   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
260 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
261 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
262   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
263 { ABOUTBOX2, IDC_ChessBoard }, \r
264 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
265   OPT_GameListClose, IDC_GameListDoFilter }, \r
266 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
267 { DLG_Error, IDOK }, \r
268 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
269   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
270 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
271 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
272   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
273   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
274 { DLG_IndexNumber, IDC_Index }, \r
275 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
276 { DLG_TypeInName, IDOK, IDCANCEL }, \r
277 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
278   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
279 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
280   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
281   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
282   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
283   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
284   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
285   OPT_HighlightMoveArrow, OPT_AutoLogo }, \r
286 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
287   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
288   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
289   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
290   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
291   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
292   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
293   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
294   GPB_General, GPB_Alarm }, \r
295 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
296   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
297   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
298   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
299   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
300   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
301   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
302   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size }, \r
303 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
304   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
305   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
306   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
307   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
308   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
309   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat,\r
310   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
311   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
312 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
313   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
314   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,\r
315   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
316 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
317 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
318   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
319 { DLG_MoveHistory }, \r
320 { DLG_EvalGraph }, \r
321 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
322 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
323 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
324   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
325   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
326   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
327 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
328   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
329   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
330 { 0 }\r
331 };\r
332 \r
333 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
334 static int lastChecked;\r
335 static char oldLanguage[MSG_SIZ], *menuText[10][25];\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     sprintf(buf, "%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     strcpy(oldLanguage, buf);\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]) strcpy(buf, menuText[i][j]); else {\r
434                 GetMenuString(subMenu, j, buf, MSG_SIZ, MF_BYPOSITION);\r
435                 menuText[i][j] = strdup(buf); // remember original on first change\r
436             }\r
437             if(buf[0] == NULLCHAR) continue;\r
438 //fprintf(debugFP, "menu(%d,%d) = %s (%08x, %08x) %d\n", i, j, buf, mainMenu, subMenu, k);\r
439             ModifyMenu(subMenu, j, MF_STRING|MF_BYPOSITION\r
440                                    |CheckMenuItem(subMenu, j, MF_BYPOSITION)\r
441                                    |EnableMenuItem(subMenu, j, MF_BYPOSITION), k, T_(buf));\r
442           }\r
443         }\r
444         DrawMenuBar(hwndMain);\r
445     }\r
446 \r
447     if(!addLanguage) return;\r
448     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
449         HMENU mainMenu = GetMenu(hwndMain);\r
450         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
451         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
452         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
453         i = 0; lastChecked = IDM_English;\r
454         do {\r
455             char *p, *q = fileData.cFileName;\r
456             int checkFlag = MF_UNCHECKED;\r
457             languageFile[i] = strdup(q);\r
458             if(barbaric && !strcmp(oldLanguage, q)) {\r
459                 checkFlag = MF_CHECKED;\r
460                 lastChecked = IDM_English + i + 1;\r
461                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
462             }\r
463             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
464             p = strstr(fileData.cFileName, ".lng");\r
465             if(p) *p = 0;\r
466             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
467         } while(FindNextFile(hFind, &fileData));\r
468         FindClose(hFind);\r
469     }\r
470 }\r
471 \r
472 #endif\r
473 \r
474 typedef struct {\r
475   char *name;\r
476   int squareSize;\r
477   int lineGap;\r
478   int smallLayout;\r
479   int tinyLayout;\r
480   int cliWidth, cliHeight;\r
481 } SizeInfo;\r
482 \r
483 SizeInfo sizeInfo[] = \r
484 {\r
485   { "tiny",     21, 0, 1, 1, 0, 0 },\r
486   { "teeny",    25, 1, 1, 1, 0, 0 },\r
487   { "dinky",    29, 1, 1, 1, 0, 0 },\r
488   { "petite",   33, 1, 1, 1, 0, 0 },\r
489   { "slim",     37, 2, 1, 0, 0, 0 },\r
490   { "small",    40, 2, 1, 0, 0, 0 },\r
491   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
492   { "middling", 49, 2, 0, 0, 0, 0 },\r
493   { "average",  54, 2, 0, 0, 0, 0 },\r
494   { "moderate", 58, 3, 0, 0, 0, 0 },\r
495   { "medium",   64, 3, 0, 0, 0, 0 },\r
496   { "bulky",    72, 3, 0, 0, 0, 0 },\r
497   { "large",    80, 3, 0, 0, 0, 0 },\r
498   { "big",      87, 3, 0, 0, 0, 0 },\r
499   { "huge",     95, 3, 0, 0, 0, 0 },\r
500   { "giant",    108, 3, 0, 0, 0, 0 },\r
501   { "colossal", 116, 4, 0, 0, 0, 0 },\r
502   { "titanic",  129, 4, 0, 0, 0, 0 },\r
503   { NULL, 0, 0, 0, 0, 0, 0 }\r
504 };\r
505 \r
506 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
507 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
508 {\r
509   { 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
510   { 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
511   { 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
512   { 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
513   { 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
514   { 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
515   { 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
516   { 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
517   { 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
518   { 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
519   { 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
520   { 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
521   { 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
522   { 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
523   { 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
524   { 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
525   { 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
526   { 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
527 };\r
528 \r
529 MyFont *font[NUM_SIZES][NUM_FONTS];\r
530 \r
531 typedef struct {\r
532   char *label;\r
533   int id;\r
534   HWND hwnd;\r
535   WNDPROC wndproc;\r
536 } MyButtonDesc;\r
537 \r
538 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
539 #define N_BUTTONS 5\r
540 \r
541 MyButtonDesc buttonDesc[N_BUTTONS] =\r
542 {\r
543   {"<<", IDM_ToStart, NULL, NULL},\r
544   {"<", IDM_Backward, NULL, NULL},\r
545   {"P", IDM_Pause, NULL, NULL},\r
546   {">", IDM_Forward, NULL, NULL},\r
547   {">>", IDM_ToEnd, NULL, NULL},\r
548 };\r
549 \r
550 int tinyLayout = 0, smallLayout = 0;\r
551 #define MENU_BAR_ITEMS 7\r
552 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
553   { N_("&File"), N_("&Mode"), N_("&Action"), N_("&Step"), N_("&Options"), N_("&Help"), NULL },\r
554   { N_("&F"), N_("&M"), N_("&A"), N_("&S"), N_("&O"), N_("&H"), NULL },\r
555 };\r
556 \r
557 \r
558 MySound sounds[(int)NSoundClasses];\r
559 MyTextAttribs textAttribs[(int)NColorClasses];\r
560 \r
561 MyColorizeAttribs colorizeAttribs[] = {\r
562   { (COLORREF)0, 0, N_("Shout Text") },\r
563   { (COLORREF)0, 0, N_("SShout/CShout") },\r
564   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
565   { (COLORREF)0, 0, N_("Channel Text") },\r
566   { (COLORREF)0, 0, N_("Kibitz Text") },\r
567   { (COLORREF)0, 0, N_("Tell Text") },\r
568   { (COLORREF)0, 0, N_("Challenge Text") },\r
569   { (COLORREF)0, 0, N_("Request Text") },\r
570   { (COLORREF)0, 0, N_("Seek Text") },\r
571   { (COLORREF)0, 0, N_("Normal Text") },\r
572   { (COLORREF)0, 0, N_("None") }\r
573 };\r
574 \r
575 \r
576 \r
577 static char *commentTitle;\r
578 static char *commentText;\r
579 static int commentIndex;\r
580 static Boolean editComment = FALSE;\r
581 \r
582 \r
583 char errorTitle[MSG_SIZ];\r
584 char errorMessage[2*MSG_SIZ];\r
585 HWND errorDialog = NULL;\r
586 BOOLEAN moveErrorMessageUp = FALSE;\r
587 BOOLEAN consoleEcho = TRUE;\r
588 CHARFORMAT consoleCF;\r
589 COLORREF consoleBackgroundColor;\r
590 \r
591 char *programVersion;\r
592 \r
593 #define CPReal 1\r
594 #define CPComm 2\r
595 #define CPSock 3\r
596 #define CPRcmd 4\r
597 typedef int CPKind;\r
598 \r
599 typedef struct {\r
600   CPKind kind;\r
601   HANDLE hProcess;\r
602   DWORD pid;\r
603   HANDLE hTo;\r
604   HANDLE hFrom;\r
605   SOCKET sock;\r
606   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
607 } ChildProc;\r
608 \r
609 #define INPUT_SOURCE_BUF_SIZE 4096\r
610 \r
611 typedef struct _InputSource {\r
612   CPKind kind;\r
613   HANDLE hFile;\r
614   SOCKET sock;\r
615   int lineByLine;\r
616   HANDLE hThread;\r
617   DWORD id;\r
618   char buf[INPUT_SOURCE_BUF_SIZE];\r
619   char *next;\r
620   DWORD count;\r
621   int error;\r
622   InputCallback func;\r
623   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
624   VOIDSTAR closure;\r
625 } InputSource;\r
626 \r
627 InputSource *consoleInputSource;\r
628 \r
629 DCB dcb;\r
630 \r
631 /* forward */\r
632 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
633 VOID ConsoleCreate();\r
634 LRESULT CALLBACK\r
635   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
636 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
637 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
638 VOID ParseCommSettings(char *arg, DCB *dcb);\r
639 LRESULT CALLBACK\r
640   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
641 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
642 void ParseIcsTextMenu(char *icsTextMenuString);\r
643 VOID PopUpMoveDialog(char firstchar);\r
644 VOID PopUpNameDialog(char firstchar);\r
645 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
646 \r
647 /* [AS] */\r
648 int NewGameFRC();\r
649 int GameListOptions();\r
650 \r
651 int dummy; // [HGM] for obsolete args\r
652 \r
653 HWND hwndMain = NULL;        /* root window*/\r
654 HWND hwndConsole = NULL;\r
655 HWND commentDialog = NULL;\r
656 HWND moveHistoryDialog = NULL;\r
657 HWND evalGraphDialog = NULL;\r
658 HWND engineOutputDialog = NULL;\r
659 HWND gameListDialog = NULL;\r
660 HWND editTagsDialog = NULL;\r
661 \r
662 int commentUp = FALSE;\r
663 \r
664 WindowPlacement wpMain;\r
665 WindowPlacement wpConsole;\r
666 WindowPlacement wpComment;\r
667 WindowPlacement wpMoveHistory;\r
668 WindowPlacement wpEvalGraph;\r
669 WindowPlacement wpEngineOutput;\r
670 WindowPlacement wpGameList;\r
671 WindowPlacement wpTags;\r
672 \r
673 VOID EngineOptionsPopup(); // [HGM] settings\r
674 \r
675 VOID GothicPopUp(char *title, VariantClass variant);\r
676 /*\r
677  * Setting "frozen" should disable all user input other than deleting\r
678  * the window.  We do this while engines are initializing themselves.\r
679  */\r
680 static int frozen = 0;\r
681 static int oldMenuItemState[MENU_BAR_ITEMS];\r
682 void FreezeUI()\r
683 {\r
684   HMENU hmenu;\r
685   int i;\r
686 \r
687   if (frozen) return;\r
688   frozen = 1;\r
689   hmenu = GetMenu(hwndMain);\r
690   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
691     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
692   }\r
693   DrawMenuBar(hwndMain);\r
694 }\r
695 \r
696 /* Undo a FreezeUI */\r
697 void ThawUI()\r
698 {\r
699   HMENU hmenu;\r
700   int i;\r
701 \r
702   if (!frozen) return;\r
703   frozen = 0;\r
704   hmenu = GetMenu(hwndMain);\r
705   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
706     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
707   }\r
708   DrawMenuBar(hwndMain);\r
709 }\r
710 \r
711 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
712 \r
713 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
714 #ifdef JAWS\r
715 #include "jaws.c"\r
716 #else\r
717 #define JAWS_INIT\r
718 #define JAWS_ARGS\r
719 #define JAWS_ALT_INTERCEPT\r
720 #define JAWS_KB_NAVIGATION\r
721 #define JAWS_MENU_ITEMS\r
722 #define JAWS_SILENCE\r
723 #define JAWS_REPLAY\r
724 #define JAWS_ACCEL\r
725 #define JAWS_COPYRIGHT\r
726 #define JAWS_DELETE(X) X\r
727 #define SAYMACHINEMOVE()\r
728 #define SAY(X)\r
729 #endif\r
730 \r
731 /*---------------------------------------------------------------------------*\\r
732  *\r
733  * WinMain\r
734  *\r
735 \*---------------------------------------------------------------------------*/\r
736 \r
737 int APIENTRY\r
738 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
739         LPSTR lpCmdLine, int nCmdShow)\r
740 {\r
741   MSG msg;\r
742   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
743 //  INITCOMMONCONTROLSEX ex;\r
744 \r
745   debugFP = stderr;\r
746 \r
747   LoadLibrary("RICHED32.DLL");\r
748   consoleCF.cbSize = sizeof(CHARFORMAT);\r
749 \r
750   if (!InitApplication(hInstance)) {\r
751     return (FALSE);\r
752   }\r
753   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
754     return (FALSE);\r
755   }\r
756 \r
757   JAWS_INIT\r
758 \r
759 //  InitCommonControlsEx(&ex);\r
760   InitCommonControls();\r
761 \r
762   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
763   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
764   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
765 \r
766   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
767 \r
768   while (GetMessage(&msg, /* message structure */\r
769                     NULL, /* handle of window receiving the message */\r
770                     0,    /* lowest message to examine */\r
771                     0))   /* highest message to examine */\r
772     {\r
773 \r
774       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
775         // [HGM] navigate: switch between all windows with tab\r
776         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
777         int i, currentElement = 0;\r
778 \r
779         // first determine what element of the chain we come from (if any)\r
780         if(appData.icsActive) {\r
781             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
782             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
783         }\r
784         if(engineOutputDialog && EngineOutputIsUp()) {\r
785             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
786             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
787         }\r
788         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
789             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
790         }\r
791         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
792         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
793         if(msg.hwnd == e1)                 currentElement = 2; else\r
794         if(msg.hwnd == e2)                 currentElement = 3; else\r
795         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
796         if(msg.hwnd == mh)                currentElement = 4; else\r
797         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
798         if(msg.hwnd == hText)  currentElement = 5; else\r
799         if(msg.hwnd == hInput) currentElement = 6; else\r
800         for (i = 0; i < N_BUTTONS; i++) {\r
801             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
802         }\r
803 \r
804         // determine where to go to\r
805         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
806           do {\r
807             currentElement = (currentElement + direction) % 7;\r
808             switch(currentElement) {\r
809                 case 0:\r
810                   h = hwndMain; break; // passing this case always makes the loop exit\r
811                 case 1:\r
812                   h = buttonDesc[0].hwnd; break; // could be NULL\r
813                 case 2:\r
814                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
815                   h = e1; break;\r
816                 case 3:\r
817                   if(!EngineOutputIsUp()) continue;\r
818                   h = e2; break;\r
819                 case 4:\r
820                   if(!MoveHistoryIsUp()) continue;\r
821                   h = mh; break;\r
822 //              case 6: // input to eval graph does not seem to get here!\r
823 //                if(!EvalGraphIsUp()) continue;\r
824 //                h = evalGraphDialog; break;\r
825                 case 5:\r
826                   if(!appData.icsActive) continue;\r
827                   SAY("display");\r
828                   h = hText; break;\r
829                 case 6:\r
830                   if(!appData.icsActive) continue;\r
831                   SAY("input");\r
832                   h = hInput; break;\r
833             }\r
834           } while(h == 0);\r
835 \r
836           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
837           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
838           SetFocus(h);\r
839 \r
840           continue; // this message now has been processed\r
841         }\r
842       }\r
843 \r
844       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
845           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
846           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
847           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
848           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
849           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
850           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
851           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
852           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
853           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
854         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
855         for(i=0; i<MAX_CHAT; i++) \r
856             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
857                 done = 1; break;\r
858         }\r
859         if(done) continue; // [HGM] chat: end patch\r
860         TranslateMessage(&msg); /* Translates virtual key codes */\r
861         DispatchMessage(&msg);  /* Dispatches message to window */\r
862       }\r
863     }\r
864 \r
865 \r
866   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
867 }\r
868 \r
869 /*---------------------------------------------------------------------------*\\r
870  *\r
871  * Initialization functions\r
872  *\r
873 \*---------------------------------------------------------------------------*/\r
874 \r
875 void\r
876 SetUserLogo()\r
877 {   // update user logo if necessary\r
878     static char oldUserName[MSG_SIZ], *curName;\r
879 \r
880     if(appData.autoLogo) {\r
881           curName = UserName();\r
882           if(strcmp(curName, oldUserName)) {\r
883                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
884                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
885                 strcpy(oldUserName, curName);\r
886           }\r
887     }\r
888 }\r
889 \r
890 BOOL\r
891 InitApplication(HINSTANCE hInstance)\r
892 {\r
893   WNDCLASS wc;\r
894 \r
895   /* Fill in window class structure with parameters that describe the */\r
896   /* main window. */\r
897 \r
898   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
899   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
900   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
901   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
902   wc.hInstance     = hInstance;         /* Owner of this class */\r
903   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
904   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
905   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
906   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
907   wc.lpszClassName = szAppName;                 /* Name to register as */\r
908 \r
909   /* Register the window class and return success/failure code. */\r
910   if (!RegisterClass(&wc)) return FALSE;\r
911 \r
912   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
913   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
914   wc.cbClsExtra    = 0;\r
915   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
916   wc.hInstance     = hInstance;\r
917   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
918   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
919   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
920   wc.lpszMenuName  = NULL;\r
921   wc.lpszClassName = szConsoleName;\r
922 \r
923   if (!RegisterClass(&wc)) return FALSE;\r
924   return TRUE;\r
925 }\r
926 \r
927 \r
928 /* Set by InitInstance, used by EnsureOnScreen */\r
929 int screenHeight, screenWidth;\r
930 \r
931 void\r
932 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
933 {\r
934 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
935   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
936   if (*x > screenWidth - 32) *x = 0;\r
937   if (*y > screenHeight - 32) *y = 0;\r
938   if (*x < minX) *x = minX;\r
939   if (*y < minY) *y = minY;\r
940 }\r
941 \r
942 BOOL\r
943 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
944 {\r
945   HWND hwnd; /* Main window handle. */\r
946   int ibs;\r
947   WINDOWPLACEMENT wp;\r
948   char *filepart;\r
949 \r
950   hInst = hInstance;    /* Store instance handle in our global variable */\r
951   programName = szAppName;\r
952 \r
953   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
954     *filepart = NULLCHAR;\r
955   } else {\r
956     GetCurrentDirectory(MSG_SIZ, installDir);\r
957   }\r
958   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
959   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
960   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
961   /* xboard, and older WinBoards, controlled the move sound with the\r
962      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
963      always turn the option on (so that the backend will call us),\r
964      then let the user turn the sound off by setting it to silence if\r
965      desired.  To accommodate old winboard.ini files saved by old\r
966      versions of WinBoard, we also turn off the sound if the option\r
967      was initially set to false. [HGM] taken out of InitAppData */\r
968   if (!appData.ringBellAfterMoves) {\r
969     sounds[(int)SoundMove].name = strdup("");\r
970     appData.ringBellAfterMoves = TRUE;\r
971   }\r
972   if (appData.debugMode) {\r
973     debugFP = fopen(appData.nameOfDebugFile, "w");\r
974     setbuf(debugFP, NULL);\r
975   }\r
976 \r
977   LoadLanguageFile(appData.language);\r
978 \r
979   InitBackEnd1();\r
980 \r
981 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
982 //  InitEngineUCI( installDir, &second );\r
983 \r
984   /* Create a main window for this application instance. */\r
985   hwnd = CreateWindow(szAppName, szTitle,\r
986                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
987                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
988                       NULL, NULL, hInstance, NULL);\r
989   hwndMain = hwnd;\r
990 \r
991   /* If window could not be created, return "failure" */\r
992   if (!hwnd) {\r
993     return (FALSE);\r
994   }\r
995 \r
996   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
997   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
998       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
999 \r
1000       if (first.programLogo == NULL && appData.debugMode) {\r
1001           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
1002       }\r
1003   } else if(appData.autoLogo) {\r
1004       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
1005         char buf[MSG_SIZ];\r
1006         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
1007         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
1008       }\r
1009   }\r
1010 \r
1011   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
1012       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1013 \r
1014       if (second.programLogo == NULL && appData.debugMode) {\r
1015           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
1016       }\r
1017   } else if(appData.autoLogo) {\r
1018       char buf[MSG_SIZ];\r
1019       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
1020         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1021         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1022       } else\r
1023       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
1024         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
1025         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
1026       }\r
1027   }\r
1028 \r
1029   SetUserLogo();\r
1030 \r
1031   iconWhite = LoadIcon(hInstance, "icon_white");\r
1032   iconBlack = LoadIcon(hInstance, "icon_black");\r
1033   iconCurrent = iconWhite;\r
1034   InitDrawingColors();\r
1035   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1036   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1037   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1038     /* Compute window size for each board size, and use the largest\r
1039        size that fits on this screen as the default. */\r
1040     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1041     if (boardSize == (BoardSize)-1 &&\r
1042         winH <= screenHeight\r
1043            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1044         && winW <= screenWidth) {\r
1045       boardSize = (BoardSize)ibs;\r
1046     }\r
1047   }\r
1048 \r
1049   InitDrawingSizes(boardSize, 0);\r
1050   TranslateMenus(1);\r
1051   InitMenuChecks();\r
1052   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1053 \r
1054   /* [AS] Load textures if specified */\r
1055   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1056   \r
1057   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1058       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1059       liteBackTextureMode = appData.liteBackTextureMode;\r
1060 \r
1061       if (liteBackTexture == NULL && appData.debugMode) {\r
1062           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1063       }\r
1064   }\r
1065   \r
1066   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1067       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1068       darkBackTextureMode = appData.darkBackTextureMode;\r
1069 \r
1070       if (darkBackTexture == NULL && appData.debugMode) {\r
1071           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1072       }\r
1073   }\r
1074 \r
1075   mysrandom( (unsigned) time(NULL) );\r
1076 \r
1077   /* [AS] Restore layout */\r
1078   if( wpMoveHistory.visible ) {\r
1079       MoveHistoryPopUp();\r
1080   }\r
1081 \r
1082   if( wpEvalGraph.visible ) {\r
1083       EvalGraphPopUp();\r
1084   }\r
1085 \r
1086   if( wpEngineOutput.visible ) {\r
1087       EngineOutputPopUp();\r
1088   }\r
1089 \r
1090   /* Make the window visible; update its client area; and return "success" */\r
1091   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1092   wp.length = sizeof(WINDOWPLACEMENT);\r
1093   wp.flags = 0;\r
1094   wp.showCmd = nCmdShow;\r
1095   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1096   wp.rcNormalPosition.left = wpMain.x;\r
1097   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1098   wp.rcNormalPosition.top = wpMain.y;\r
1099   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1100   SetWindowPlacement(hwndMain, &wp);\r
1101 \r
1102   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1103 \r
1104   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1105                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1106 \r
1107   if (hwndConsole) {\r
1108 #if AOT_CONSOLE\r
1109     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1110                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1111 #endif\r
1112     ShowWindow(hwndConsole, nCmdShow);\r
1113     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
1114       char buf[MSG_SIZ], *p = buf, *q;\r
1115       strcpy(buf, appData.chatBoxes);\r
1116       do {\r
1117         q = strchr(p, ';');\r
1118         if(q) *q++ = 0;\r
1119         if(*p) ChatPopUp(p);\r
1120       } while(p=q);\r
1121     }\r
1122     SetActiveWindow(hwndConsole);\r
1123   }\r
1124   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1125   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1126 \r
1127   return TRUE;\r
1128 \r
1129 }\r
1130 \r
1131 VOID\r
1132 InitMenuChecks()\r
1133 {\r
1134   HMENU hmenu = GetMenu(hwndMain);\r
1135 \r
1136   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1137                         MF_BYCOMMAND|((appData.icsActive &&\r
1138                                        *appData.icsCommPort != NULLCHAR) ?\r
1139                                       MF_ENABLED : MF_GRAYED));\r
1140   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1141                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1142                                      MF_CHECKED : MF_UNCHECKED));\r
1143 }\r
1144 \r
1145 //---------------------------------------------------------------------------------------------------------\r
1146 \r
1147 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1148 #define XBOARD FALSE\r
1149 \r
1150 #define OPTCHAR "/"\r
1151 #define SEPCHAR "="\r
1152 \r
1153 #include "args.h"\r
1154 \r
1155 // front-end part of option handling\r
1156 \r
1157 VOID\r
1158 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1159 {\r
1160   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1161   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1162   DeleteDC(hdc);\r
1163   lf->lfWidth = 0;\r
1164   lf->lfEscapement = 0;\r
1165   lf->lfOrientation = 0;\r
1166   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1167   lf->lfItalic = mfp->italic;\r
1168   lf->lfUnderline = mfp->underline;\r
1169   lf->lfStrikeOut = mfp->strikeout;\r
1170   lf->lfCharSet = mfp->charset;\r
1171   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1172   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1173   lf->lfQuality = DEFAULT_QUALITY;\r
1174   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1175   strcpy(lf->lfFaceName, mfp->faceName);\r
1176 }\r
1177 \r
1178 void\r
1179 CreateFontInMF(MyFont *mf)\r
1180\r
1181   LFfromMFP(&mf->lf, &mf->mfp);\r
1182   if (mf->hf) DeleteObject(mf->hf);\r
1183   mf->hf = CreateFontIndirect(&mf->lf);\r
1184 }\r
1185 \r
1186 // [HGM] This platform-dependent table provides the location for storing the color info\r
1187 void *\r
1188 colorVariable[] = {\r
1189   &whitePieceColor, \r
1190   &blackPieceColor, \r
1191   &lightSquareColor,\r
1192   &darkSquareColor, \r
1193   &highlightSquareColor,\r
1194   &premoveHighlightColor,\r
1195   NULL,\r
1196   &consoleBackgroundColor,\r
1197   &appData.fontForeColorWhite,\r
1198   &appData.fontBackColorWhite,\r
1199   &appData.fontForeColorBlack,\r
1200   &appData.fontBackColorBlack,\r
1201   &appData.evalHistColorWhite,\r
1202   &appData.evalHistColorBlack,\r
1203   &appData.highlightArrowColor,\r
1204 };\r
1205 \r
1206 /* Command line font name parser.  NULL name means do nothing.\r
1207    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1208    For backward compatibility, syntax without the colon is also\r
1209    accepted, but font names with digits in them won't work in that case.\r
1210 */\r
1211 VOID\r
1212 ParseFontName(char *name, MyFontParams *mfp)\r
1213 {\r
1214   char *p, *q;\r
1215   if (name == NULL) return;\r
1216   p = name;\r
1217   q = strchr(p, ':');\r
1218   if (q) {\r
1219     if (q - p >= sizeof(mfp->faceName))\r
1220       ExitArgError(_("Font name too long:"), name);\r
1221     memcpy(mfp->faceName, p, q - p);\r
1222     mfp->faceName[q - p] = NULLCHAR;\r
1223     p = q + 1;\r
1224   } else {\r
1225     q = mfp->faceName;\r
1226     while (*p && !isdigit(*p)) {\r
1227       *q++ = *p++;\r
1228       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1229         ExitArgError(_("Font name too long:"), name);\r
1230     }\r
1231     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1232     *q = NULLCHAR;\r
1233   }\r
1234   if (!*p) ExitArgError(_("Font point size missing:"), name);\r
1235   mfp->pointSize = (float) atof(p);\r
1236   mfp->bold = (strchr(p, 'b') != NULL);\r
1237   mfp->italic = (strchr(p, 'i') != NULL);\r
1238   mfp->underline = (strchr(p, 'u') != NULL);\r
1239   mfp->strikeout = (strchr(p, 's') != NULL);\r
1240   mfp->charset = DEFAULT_CHARSET;\r
1241   q = strchr(p, 'c');\r
1242   if (q)\r
1243     mfp->charset = (BYTE) atoi(q+1);\r
1244 }\r
1245 \r
1246 void\r
1247 ParseFont(char *name, int number)\r
1248 { // wrapper to shield back-end from 'font'\r
1249   ParseFontName(name, &font[boardSize][number]->mfp);\r
1250 }\r
1251 \r
1252 void\r
1253 SetFontDefaults()\r
1254 { // in WB  we have a 2D array of fonts; this initializes their description\r
1255   int i, j;\r
1256   /* Point font array elements to structures and\r
1257      parse default font names */\r
1258   for (i=0; i<NUM_FONTS; i++) {\r
1259     for (j=0; j<NUM_SIZES; j++) {\r
1260       font[j][i] = &fontRec[j][i];\r
1261       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1262     }\r
1263   }\r
1264 }\r
1265 \r
1266 void\r
1267 CreateFonts()\r
1268 { // here we create the actual fonts from the selected descriptions\r
1269   int i, j;\r
1270   for (i=0; i<NUM_FONTS; i++) {\r
1271     for (j=0; j<NUM_SIZES; j++) {\r
1272       CreateFontInMF(font[j][i]);\r
1273     }\r
1274   }\r
1275 }\r
1276 /* Color name parser.\r
1277    X version accepts X color names, but this one\r
1278    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1279 COLORREF\r
1280 ParseColorName(char *name)\r
1281 {\r
1282   int red, green, blue, count;\r
1283   char buf[MSG_SIZ];\r
1284 \r
1285   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1286   if (count != 3) {\r
1287     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1288       &red, &green, &blue);\r
1289   }\r
1290   if (count != 3) {\r
1291     sprintf(buf, _("Can't parse color name %s"), name);\r
1292     DisplayError(buf, 0);\r
1293     return RGB(0, 0, 0);\r
1294   }\r
1295   return PALETTERGB(red, green, blue);\r
1296 }\r
1297 \r
1298 void\r
1299 ParseColor(int n, char *name)\r
1300 { // for WinBoard the color is an int, which needs to be derived from the string\r
1301   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1302 }\r
1303 \r
1304 void\r
1305 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1306 {\r
1307   char *e = argValue;\r
1308   int eff = 0;\r
1309 \r
1310   while (*e) {\r
1311     if (*e == 'b')      eff |= CFE_BOLD;\r
1312     else if (*e == 'i') eff |= CFE_ITALIC;\r
1313     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1314     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1315     else if (*e == '#' || isdigit(*e)) break;\r
1316     e++;\r
1317   }\r
1318   *effects = eff;\r
1319   *color   = ParseColorName(e);\r
1320 }\r
1321 \r
1322 void\r
1323 ParseTextAttribs(ColorClass cc, char *s)\r
1324 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1325     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1326     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1327 }\r
1328 \r
1329 void\r
1330 ParseBoardSize(void *addr, char *name)\r
1331 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1332   BoardSize bs = SizeTiny;\r
1333   while (sizeInfo[bs].name != NULL) {\r
1334     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1335         *(BoardSize *)addr = bs;\r
1336         return;\r
1337     }\r
1338     bs++;\r
1339   }\r
1340   ExitArgError(_("Unrecognized board size value"), name);\r
1341 }\r
1342 \r
1343 void\r
1344 LoadAllSounds()\r
1345 { // [HGM] import name from appData first\r
1346   ColorClass cc;\r
1347   SoundClass sc;\r
1348   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1349     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1350     textAttribs[cc].sound.data = NULL;\r
1351     MyLoadSound(&textAttribs[cc].sound);\r
1352   }\r
1353   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1354     textAttribs[cc].sound.name = strdup("");\r
1355     textAttribs[cc].sound.data = NULL;\r
1356   }\r
1357   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1358     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1359     sounds[sc].data = NULL;\r
1360     MyLoadSound(&sounds[sc]);\r
1361   }\r
1362 }\r
1363 \r
1364 void\r
1365 SetCommPortDefaults()\r
1366 {\r
1367    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1368   dcb.DCBlength = sizeof(DCB);\r
1369   dcb.BaudRate = 9600;\r
1370   dcb.fBinary = TRUE;\r
1371   dcb.fParity = FALSE;\r
1372   dcb.fOutxCtsFlow = FALSE;\r
1373   dcb.fOutxDsrFlow = FALSE;\r
1374   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1375   dcb.fDsrSensitivity = FALSE;\r
1376   dcb.fTXContinueOnXoff = TRUE;\r
1377   dcb.fOutX = FALSE;\r
1378   dcb.fInX = FALSE;\r
1379   dcb.fNull = FALSE;\r
1380   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1381   dcb.fAbortOnError = FALSE;\r
1382   dcb.ByteSize = 7;\r
1383   dcb.Parity = SPACEPARITY;\r
1384   dcb.StopBits = ONESTOPBIT;\r
1385 }\r
1386 \r
1387 // [HGM] args: these three cases taken out to stay in front-end\r
1388 void\r
1389 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1390 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1391         // while the curent board size determines the element. This system should be ported to XBoard.\r
1392         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1393         int bs;\r
1394         for (bs=0; bs<NUM_SIZES; bs++) {\r
1395           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1396           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1397           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1398             ad->argName, mfp->faceName, mfp->pointSize,\r
1399             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1400             mfp->bold ? "b" : "",\r
1401             mfp->italic ? "i" : "",\r
1402             mfp->underline ? "u" : "",\r
1403             mfp->strikeout ? "s" : "",\r
1404             (int)mfp->charset);\r
1405         }\r
1406       }\r
1407 \r
1408 void\r
1409 ExportSounds()\r
1410 { // [HGM] copy the names from the internal WB variables to appData\r
1411   ColorClass cc;\r
1412   SoundClass sc;\r
1413   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1414     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1415   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1416     (&appData.soundMove)[sc] = sounds[sc].name;\r
1417 }\r
1418 \r
1419 void\r
1420 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1421 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1422         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1423         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1424           (ta->effects & CFE_BOLD) ? "b" : "",\r
1425           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1426           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1427           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1428           (ta->effects) ? " " : "",\r
1429           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1430       }\r
1431 \r
1432 void\r
1433 SaveColor(FILE *f, ArgDescriptor *ad)\r
1434 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1435         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1436         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1437           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1438 }\r
1439 \r
1440 void\r
1441 SaveBoardSize(FILE *f, char *name, void *addr)\r
1442 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1443   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1444 }\r
1445 \r
1446 void\r
1447 ParseCommPortSettings(char *s)\r
1448 { // wrapper to keep dcb from back-end\r
1449   ParseCommSettings(s, &dcb);\r
1450 }\r
1451 \r
1452 void\r
1453 GetWindowCoords()\r
1454 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1455   GetActualPlacement(hwndMain, &wpMain);\r
1456   GetActualPlacement(hwndConsole, &wpConsole);\r
1457   GetActualPlacement(commentDialog, &wpComment);\r
1458   GetActualPlacement(editTagsDialog, &wpTags);\r
1459   GetActualPlacement(gameListDialog, &wpGameList);\r
1460   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1461   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1462   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1463 }\r
1464 \r
1465 void\r
1466 PrintCommPortSettings(FILE *f, char *name)\r
1467 { // wrapper to shield back-end from DCB\r
1468       PrintCommSettings(f, name, &dcb);\r
1469 }\r
1470 \r
1471 int\r
1472 MySearchPath(char *installDir, char *name, char *fullname)\r
1473 {\r
1474   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1475   if(name[0]== '%') {\r
1476     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1477     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1478       strcpy(buf, p+1);\r
1479       *strchr(buf, '%') = 0;\r
1480       strcat(fullname, getenv(buf));\r
1481       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1482     }\r
1483     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1484     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1485     return (int) strlen(fullname);\r
1486   }\r
1487   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1488 }\r
1489 \r
1490 int\r
1491 MyGetFullPathName(char *name, char *fullname)\r
1492 {\r
1493   char *dummy;\r
1494   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1495 }\r
1496 \r
1497 int\r
1498 MainWindowUp()\r
1499 { // [HGM] args: allows testing if main window is realized from back-end\r
1500   return hwndMain != NULL;\r
1501 }\r
1502 \r
1503 void\r
1504 PopUpStartupDialog()\r
1505 {\r
1506     FARPROC lpProc;\r
1507     \r
1508     LoadLanguageFile(appData.language);\r
1509     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1510     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1511     FreeProcInstance(lpProc);\r
1512 }\r
1513 \r
1514 /*---------------------------------------------------------------------------*\\r
1515  *\r
1516  * GDI board drawing routines\r
1517  *\r
1518 \*---------------------------------------------------------------------------*/\r
1519 \r
1520 /* [AS] Draw square using background texture */\r
1521 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1522 {\r
1523     XFORM   x;\r
1524 \r
1525     if( mode == 0 ) {\r
1526         return; /* Should never happen! */\r
1527     }\r
1528 \r
1529     SetGraphicsMode( dst, GM_ADVANCED );\r
1530 \r
1531     switch( mode ) {\r
1532     case 1:\r
1533         /* Identity */\r
1534         break;\r
1535     case 2:\r
1536         /* X reflection */\r
1537         x.eM11 = -1.0;\r
1538         x.eM12 = 0;\r
1539         x.eM21 = 0;\r
1540         x.eM22 = 1.0;\r
1541         x.eDx = (FLOAT) dw + dx - 1;\r
1542         x.eDy = 0;\r
1543         dx = 0;\r
1544         SetWorldTransform( dst, &x );\r
1545         break;\r
1546     case 3:\r
1547         /* Y reflection */\r
1548         x.eM11 = 1.0;\r
1549         x.eM12 = 0;\r
1550         x.eM21 = 0;\r
1551         x.eM22 = -1.0;\r
1552         x.eDx = 0;\r
1553         x.eDy = (FLOAT) dh + dy - 1;\r
1554         dy = 0;\r
1555         SetWorldTransform( dst, &x );\r
1556         break;\r
1557     case 4:\r
1558         /* X/Y flip */\r
1559         x.eM11 = 0;\r
1560         x.eM12 = 1.0;\r
1561         x.eM21 = 1.0;\r
1562         x.eM22 = 0;\r
1563         x.eDx = (FLOAT) dx;\r
1564         x.eDy = (FLOAT) dy;\r
1565         dx = 0;\r
1566         dy = 0;\r
1567         SetWorldTransform( dst, &x );\r
1568         break;\r
1569     }\r
1570 \r
1571     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1572 \r
1573     x.eM11 = 1.0;\r
1574     x.eM12 = 0;\r
1575     x.eM21 = 0;\r
1576     x.eM22 = 1.0;\r
1577     x.eDx = 0;\r
1578     x.eDy = 0;\r
1579     SetWorldTransform( dst, &x );\r
1580 \r
1581     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1582 }\r
1583 \r
1584 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1585 enum {\r
1586     PM_WP = (int) WhitePawn, \r
1587     PM_WN = (int) WhiteKnight, \r
1588     PM_WB = (int) WhiteBishop, \r
1589     PM_WR = (int) WhiteRook, \r
1590     PM_WQ = (int) WhiteQueen, \r
1591     PM_WF = (int) WhiteFerz, \r
1592     PM_WW = (int) WhiteWazir, \r
1593     PM_WE = (int) WhiteAlfil, \r
1594     PM_WM = (int) WhiteMan, \r
1595     PM_WO = (int) WhiteCannon, \r
1596     PM_WU = (int) WhiteUnicorn, \r
1597     PM_WH = (int) WhiteNightrider, \r
1598     PM_WA = (int) WhiteAngel, \r
1599     PM_WC = (int) WhiteMarshall, \r
1600     PM_WAB = (int) WhiteCardinal, \r
1601     PM_WD = (int) WhiteDragon, \r
1602     PM_WL = (int) WhiteLance, \r
1603     PM_WS = (int) WhiteCobra, \r
1604     PM_WV = (int) WhiteFalcon, \r
1605     PM_WSG = (int) WhiteSilver, \r
1606     PM_WG = (int) WhiteGrasshopper, \r
1607     PM_WK = (int) WhiteKing,\r
1608     PM_BP = (int) BlackPawn, \r
1609     PM_BN = (int) BlackKnight, \r
1610     PM_BB = (int) BlackBishop, \r
1611     PM_BR = (int) BlackRook, \r
1612     PM_BQ = (int) BlackQueen, \r
1613     PM_BF = (int) BlackFerz, \r
1614     PM_BW = (int) BlackWazir, \r
1615     PM_BE = (int) BlackAlfil, \r
1616     PM_BM = (int) BlackMan,\r
1617     PM_BO = (int) BlackCannon, \r
1618     PM_BU = (int) BlackUnicorn, \r
1619     PM_BH = (int) BlackNightrider, \r
1620     PM_BA = (int) BlackAngel, \r
1621     PM_BC = (int) BlackMarshall, \r
1622     PM_BG = (int) BlackGrasshopper, \r
1623     PM_BAB = (int) BlackCardinal,\r
1624     PM_BD = (int) BlackDragon,\r
1625     PM_BL = (int) BlackLance,\r
1626     PM_BS = (int) BlackCobra,\r
1627     PM_BV = (int) BlackFalcon,\r
1628     PM_BSG = (int) BlackSilver,\r
1629     PM_BK = (int) BlackKing\r
1630 };\r
1631 \r
1632 static HFONT hPieceFont = NULL;\r
1633 static HBITMAP hPieceMask[(int) EmptySquare];\r
1634 static HBITMAP hPieceFace[(int) EmptySquare];\r
1635 static int fontBitmapSquareSize = 0;\r
1636 static char pieceToFontChar[(int) EmptySquare] =\r
1637                               { 'p', 'n', 'b', 'r', 'q', \r
1638                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1639                       'k', 'o', 'm', 'v', 't', 'w', \r
1640                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1641                                                               'l' };\r
1642 \r
1643 extern BOOL SetCharTable( char *table, const char * map );\r
1644 /* [HGM] moved to backend.c */\r
1645 \r
1646 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1647 {\r
1648     HBRUSH hbrush;\r
1649     BYTE r1 = GetRValue( color );\r
1650     BYTE g1 = GetGValue( color );\r
1651     BYTE b1 = GetBValue( color );\r
1652     BYTE r2 = r1 / 2;\r
1653     BYTE g2 = g1 / 2;\r
1654     BYTE b2 = b1 / 2;\r
1655     RECT rc;\r
1656 \r
1657     /* Create a uniform background first */\r
1658     hbrush = CreateSolidBrush( color );\r
1659     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1660     FillRect( hdc, &rc, hbrush );\r
1661     DeleteObject( hbrush );\r
1662     \r
1663     if( mode == 1 ) {\r
1664         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1665         int steps = squareSize / 2;\r
1666         int i;\r
1667 \r
1668         for( i=0; i<steps; i++ ) {\r
1669             BYTE r = r1 - (r1-r2) * i / steps;\r
1670             BYTE g = g1 - (g1-g2) * i / steps;\r
1671             BYTE b = b1 - (b1-b2) * i / steps;\r
1672 \r
1673             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1674             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1675             FillRect( hdc, &rc, hbrush );\r
1676             DeleteObject(hbrush);\r
1677         }\r
1678     }\r
1679     else if( mode == 2 ) {\r
1680         /* Diagonal gradient, good more or less for every piece */\r
1681         POINT triangle[3];\r
1682         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1683         HBRUSH hbrush_old;\r
1684         int steps = squareSize;\r
1685         int i;\r
1686 \r
1687         triangle[0].x = squareSize - steps;\r
1688         triangle[0].y = squareSize;\r
1689         triangle[1].x = squareSize;\r
1690         triangle[1].y = squareSize;\r
1691         triangle[2].x = squareSize;\r
1692         triangle[2].y = squareSize - steps;\r
1693 \r
1694         for( i=0; i<steps; i++ ) {\r
1695             BYTE r = r1 - (r1-r2) * i / steps;\r
1696             BYTE g = g1 - (g1-g2) * i / steps;\r
1697             BYTE b = b1 - (b1-b2) * i / steps;\r
1698 \r
1699             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1700             hbrush_old = SelectObject( hdc, hbrush );\r
1701             Polygon( hdc, triangle, 3 );\r
1702             SelectObject( hdc, hbrush_old );\r
1703             DeleteObject(hbrush);\r
1704             triangle[0].x++;\r
1705             triangle[2].y++;\r
1706         }\r
1707 \r
1708         SelectObject( hdc, hpen );\r
1709     }\r
1710 }\r
1711 \r
1712 /*\r
1713     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1714     seems to work ok. The main problem here is to find the "inside" of a chess\r
1715     piece: follow the steps as explained below.\r
1716 */\r
1717 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1718 {\r
1719     HBITMAP hbm;\r
1720     HBITMAP hbm_old;\r
1721     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1722     RECT rc;\r
1723     SIZE sz;\r
1724     POINT pt;\r
1725     int backColor = whitePieceColor; \r
1726     int foreColor = blackPieceColor;\r
1727     \r
1728     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1729         backColor = appData.fontBackColorWhite;\r
1730         foreColor = appData.fontForeColorWhite;\r
1731     }\r
1732     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1733         backColor = appData.fontBackColorBlack;\r
1734         foreColor = appData.fontForeColorBlack;\r
1735     }\r
1736 \r
1737     /* Mask */\r
1738     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1739 \r
1740     hbm_old = SelectObject( hdc, hbm );\r
1741 \r
1742     rc.left = 0;\r
1743     rc.top = 0;\r
1744     rc.right = squareSize;\r
1745     rc.bottom = squareSize;\r
1746 \r
1747     /* Step 1: background is now black */\r
1748     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1749 \r
1750     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1751 \r
1752     pt.x = (squareSize - sz.cx) / 2;\r
1753     pt.y = (squareSize - sz.cy) / 2;\r
1754 \r
1755     SetBkMode( hdc, TRANSPARENT );\r
1756     SetTextColor( hdc, chroma );\r
1757     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1758     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1759 \r
1760     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1761     /* Step 3: the area outside the piece is filled with white */\r
1762 //    FloodFill( hdc, 0, 0, chroma );\r
1763     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1764     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1765     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1766     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1767     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1768     /* \r
1769         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1770         but if the start point is not inside the piece we're lost!\r
1771         There should be a better way to do this... if we could create a region or path\r
1772         from the fill operation we would be fine for example.\r
1773     */\r
1774 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1775     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1776 \r
1777     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1778         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1779         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1780 \r
1781         SelectObject( dc2, bm2 );\r
1782         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1783         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1784         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1785         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1786         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1787 \r
1788         DeleteDC( dc2 );\r
1789         DeleteObject( bm2 );\r
1790     }\r
1791 \r
1792     SetTextColor( hdc, 0 );\r
1793     /* \r
1794         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1795         draw the piece again in black for safety.\r
1796     */\r
1797     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1798 \r
1799     SelectObject( hdc, hbm_old );\r
1800 \r
1801     if( hPieceMask[index] != NULL ) {\r
1802         DeleteObject( hPieceMask[index] );\r
1803     }\r
1804 \r
1805     hPieceMask[index] = hbm;\r
1806 \r
1807     /* Face */\r
1808     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1809 \r
1810     SelectObject( hdc, hbm );\r
1811 \r
1812     {\r
1813         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1814         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1815         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1816 \r
1817         SelectObject( dc1, hPieceMask[index] );\r
1818         SelectObject( dc2, bm2 );\r
1819         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1820         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1821         \r
1822         /* \r
1823             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1824             the piece background and deletes (makes transparent) the rest.\r
1825             Thanks to that mask, we are free to paint the background with the greates\r
1826             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1827             We use this, to make gradients and give the pieces a "roundish" look.\r
1828         */\r
1829         SetPieceBackground( hdc, backColor, 2 );\r
1830         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1831 \r
1832         DeleteDC( dc2 );\r
1833         DeleteDC( dc1 );\r
1834         DeleteObject( bm2 );\r
1835     }\r
1836 \r
1837     SetTextColor( hdc, foreColor );\r
1838     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1839 \r
1840     SelectObject( hdc, hbm_old );\r
1841 \r
1842     if( hPieceFace[index] != NULL ) {\r
1843         DeleteObject( hPieceFace[index] );\r
1844     }\r
1845 \r
1846     hPieceFace[index] = hbm;\r
1847 }\r
1848 \r
1849 static int TranslatePieceToFontPiece( int piece )\r
1850 {\r
1851     switch( piece ) {\r
1852     case BlackPawn:\r
1853         return PM_BP;\r
1854     case BlackKnight:\r
1855         return PM_BN;\r
1856     case BlackBishop:\r
1857         return PM_BB;\r
1858     case BlackRook:\r
1859         return PM_BR;\r
1860     case BlackQueen:\r
1861         return PM_BQ;\r
1862     case BlackKing:\r
1863         return PM_BK;\r
1864     case WhitePawn:\r
1865         return PM_WP;\r
1866     case WhiteKnight:\r
1867         return PM_WN;\r
1868     case WhiteBishop:\r
1869         return PM_WB;\r
1870     case WhiteRook:\r
1871         return PM_WR;\r
1872     case WhiteQueen:\r
1873         return PM_WQ;\r
1874     case WhiteKing:\r
1875         return PM_WK;\r
1876 \r
1877     case BlackAngel:\r
1878         return PM_BA;\r
1879     case BlackMarshall:\r
1880         return PM_BC;\r
1881     case BlackFerz:\r
1882         return PM_BF;\r
1883     case BlackNightrider:\r
1884         return PM_BH;\r
1885     case BlackAlfil:\r
1886         return PM_BE;\r
1887     case BlackWazir:\r
1888         return PM_BW;\r
1889     case BlackUnicorn:\r
1890         return PM_BU;\r
1891     case BlackCannon:\r
1892         return PM_BO;\r
1893     case BlackGrasshopper:\r
1894         return PM_BG;\r
1895     case BlackMan:\r
1896         return PM_BM;\r
1897     case BlackSilver:\r
1898         return PM_BSG;\r
1899     case BlackLance:\r
1900         return PM_BL;\r
1901     case BlackFalcon:\r
1902         return PM_BV;\r
1903     case BlackCobra:\r
1904         return PM_BS;\r
1905     case BlackCardinal:\r
1906         return PM_BAB;\r
1907     case BlackDragon:\r
1908         return PM_BD;\r
1909 \r
1910     case WhiteAngel:\r
1911         return PM_WA;\r
1912     case WhiteMarshall:\r
1913         return PM_WC;\r
1914     case WhiteFerz:\r
1915         return PM_WF;\r
1916     case WhiteNightrider:\r
1917         return PM_WH;\r
1918     case WhiteAlfil:\r
1919         return PM_WE;\r
1920     case WhiteWazir:\r
1921         return PM_WW;\r
1922     case WhiteUnicorn:\r
1923         return PM_WU;\r
1924     case WhiteCannon:\r
1925         return PM_WO;\r
1926     case WhiteGrasshopper:\r
1927         return PM_WG;\r
1928     case WhiteMan:\r
1929         return PM_WM;\r
1930     case WhiteSilver:\r
1931         return PM_WSG;\r
1932     case WhiteLance:\r
1933         return PM_WL;\r
1934     case WhiteFalcon:\r
1935         return PM_WV;\r
1936     case WhiteCobra:\r
1937         return PM_WS;\r
1938     case WhiteCardinal:\r
1939         return PM_WAB;\r
1940     case WhiteDragon:\r
1941         return PM_WD;\r
1942     }\r
1943 \r
1944     return 0;\r
1945 }\r
1946 \r
1947 void CreatePiecesFromFont()\r
1948 {\r
1949     LOGFONT lf;\r
1950     HDC hdc_window = NULL;\r
1951     HDC hdc = NULL;\r
1952     HFONT hfont_old;\r
1953     int fontHeight;\r
1954     int i;\r
1955 \r
1956     if( fontBitmapSquareSize < 0 ) {\r
1957         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1958         return;\r
1959     }\r
1960 \r
1961     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1962         fontBitmapSquareSize = -1;\r
1963         return;\r
1964     }\r
1965 \r
1966     if( fontBitmapSquareSize != squareSize ) {\r
1967         hdc_window = GetDC( hwndMain );\r
1968         hdc = CreateCompatibleDC( hdc_window );\r
1969 \r
1970         if( hPieceFont != NULL ) {\r
1971             DeleteObject( hPieceFont );\r
1972         }\r
1973         else {\r
1974             for( i=0; i<=(int)BlackKing; i++ ) {\r
1975                 hPieceMask[i] = NULL;\r
1976                 hPieceFace[i] = NULL;\r
1977             }\r
1978         }\r
1979 \r
1980         fontHeight = 75;\r
1981 \r
1982         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
1983             fontHeight = appData.fontPieceSize;\r
1984         }\r
1985 \r
1986         fontHeight = (fontHeight * squareSize) / 100;\r
1987 \r
1988         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
1989         lf.lfWidth = 0;\r
1990         lf.lfEscapement = 0;\r
1991         lf.lfOrientation = 0;\r
1992         lf.lfWeight = FW_NORMAL;\r
1993         lf.lfItalic = 0;\r
1994         lf.lfUnderline = 0;\r
1995         lf.lfStrikeOut = 0;\r
1996         lf.lfCharSet = DEFAULT_CHARSET;\r
1997         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1998         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1999         lf.lfQuality = PROOF_QUALITY;\r
2000         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2001         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2002         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2003 \r
2004         hPieceFont = CreateFontIndirect( &lf );\r
2005 \r
2006         if( hPieceFont == NULL ) {\r
2007             fontBitmapSquareSize = -2;\r
2008         }\r
2009         else {\r
2010             /* Setup font-to-piece character table */\r
2011             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2012                 /* No (or wrong) global settings, try to detect the font */\r
2013                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2014                     /* Alpha */\r
2015                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2016                 }\r
2017                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2018                     /* DiagramTT* family */\r
2019                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2020                 }\r
2021                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2022                     /* Fairy symbols */\r
2023                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2024                 }\r
2025                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2026                     /* Good Companion (Some characters get warped as literal :-( */\r
2027                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2028                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2029                     SetCharTable(pieceToFontChar, s);\r
2030                 }\r
2031                 else {\r
2032                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2033                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2034                 }\r
2035             }\r
2036 \r
2037             /* Create bitmaps */\r
2038             hfont_old = SelectObject( hdc, hPieceFont );\r
2039             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2040                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2041                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2042 \r
2043             SelectObject( hdc, hfont_old );\r
2044 \r
2045             fontBitmapSquareSize = squareSize;\r
2046         }\r
2047     }\r
2048 \r
2049     if( hdc != NULL ) {\r
2050         DeleteDC( hdc );\r
2051     }\r
2052 \r
2053     if( hdc_window != NULL ) {\r
2054         ReleaseDC( hwndMain, hdc_window );\r
2055     }\r
2056 }\r
2057 \r
2058 HBITMAP\r
2059 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2060 {\r
2061   char name[128];\r
2062 \r
2063   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2064   if (gameInfo.event &&\r
2065       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2066       strcmp(name, "k80s") == 0) {\r
2067     strcpy(name, "tim");\r
2068   }\r
2069   return LoadBitmap(hinst, name);\r
2070 }\r
2071 \r
2072 \r
2073 /* Insert a color into the program's logical palette\r
2074    structure.  This code assumes the given color is\r
2075    the result of the RGB or PALETTERGB macro, and it\r
2076    knows how those macros work (which is documented).\r
2077 */\r
2078 VOID\r
2079 InsertInPalette(COLORREF color)\r
2080 {\r
2081   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2082 \r
2083   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2084     DisplayFatalError(_("Too many colors"), 0, 1);\r
2085     pLogPal->palNumEntries--;\r
2086     return;\r
2087   }\r
2088 \r
2089   pe->peFlags = (char) 0;\r
2090   pe->peRed = (char) (0xFF & color);\r
2091   pe->peGreen = (char) (0xFF & (color >> 8));\r
2092   pe->peBlue = (char) (0xFF & (color >> 16));\r
2093   return;\r
2094 }\r
2095 \r
2096 \r
2097 VOID\r
2098 InitDrawingColors()\r
2099 {\r
2100   if (pLogPal == NULL) {\r
2101     /* Allocate enough memory for a logical palette with\r
2102      * PALETTESIZE entries and set the size and version fields\r
2103      * of the logical palette structure.\r
2104      */\r
2105     pLogPal = (NPLOGPALETTE)\r
2106       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2107                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2108     pLogPal->palVersion    = 0x300;\r
2109   }\r
2110   pLogPal->palNumEntries = 0;\r
2111 \r
2112   InsertInPalette(lightSquareColor);\r
2113   InsertInPalette(darkSquareColor);\r
2114   InsertInPalette(whitePieceColor);\r
2115   InsertInPalette(blackPieceColor);\r
2116   InsertInPalette(highlightSquareColor);\r
2117   InsertInPalette(premoveHighlightColor);\r
2118 \r
2119   /*  create a logical color palette according the information\r
2120    *  in the LOGPALETTE structure.\r
2121    */\r
2122   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2123 \r
2124   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2125   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2126   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2127   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2128   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2129   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2130   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2131   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2132   /* [AS] Force rendering of the font-based pieces */\r
2133   if( fontBitmapSquareSize > 0 ) {\r
2134     fontBitmapSquareSize = 0;\r
2135   }\r
2136 }\r
2137 \r
2138 \r
2139 int\r
2140 BoardWidth(int boardSize, int n)\r
2141 { /* [HGM] argument n added to allow different width and height */\r
2142   int lineGap = sizeInfo[boardSize].lineGap;\r
2143 \r
2144   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2145       lineGap = appData.overrideLineGap;\r
2146   }\r
2147 \r
2148   return (n + 1) * lineGap +\r
2149           n * sizeInfo[boardSize].squareSize;\r
2150 }\r
2151 \r
2152 /* Respond to board resize by dragging edge */\r
2153 VOID\r
2154 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2155 {\r
2156   BoardSize newSize = NUM_SIZES - 1;\r
2157   static int recurse = 0;\r
2158   if (IsIconic(hwndMain)) return;\r
2159   if (recurse > 0) return;\r
2160   recurse++;\r
2161   while (newSize > 0) {\r
2162         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2163         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2164            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2165     newSize--;\r
2166   } \r
2167   boardSize = newSize;\r
2168   InitDrawingSizes(boardSize, flags);\r
2169   recurse--;\r
2170 }\r
2171 \r
2172 \r
2173 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2174 \r
2175 VOID\r
2176 InitDrawingSizes(BoardSize boardSize, int flags)\r
2177 {\r
2178   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2179   ChessSquare piece;\r
2180   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2181   HDC hdc;\r
2182   SIZE clockSize, messageSize;\r
2183   HFONT oldFont;\r
2184   char buf[MSG_SIZ];\r
2185   char *str;\r
2186   HMENU hmenu = GetMenu(hwndMain);\r
2187   RECT crect, wrect, oldRect;\r
2188   int offby;\r
2189   LOGBRUSH logbrush;\r
2190 \r
2191   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2192   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2193 \r
2194   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2195   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2196 \r
2197   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2198   oldRect.top = wpMain.y;\r
2199   oldRect.right = wpMain.x + wpMain.width;\r
2200   oldRect.bottom = wpMain.y + wpMain.height;\r
2201 \r
2202   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2203   smallLayout = sizeInfo[boardSize].smallLayout;\r
2204   squareSize = sizeInfo[boardSize].squareSize;\r
2205   lineGap = sizeInfo[boardSize].lineGap;\r
2206   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2207 \r
2208   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2209       lineGap = appData.overrideLineGap;\r
2210   }\r
2211 \r
2212   if (tinyLayout != oldTinyLayout) {\r
2213     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
2214     if (tinyLayout) {\r
2215       style &= ~WS_SYSMENU;\r
2216       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2217                  "&Minimize\tCtrl+F4");\r
2218     } else {\r
2219       style |= WS_SYSMENU;\r
2220       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2221     }\r
2222     SetWindowLong(hwndMain, GWL_STYLE, style);\r
2223 \r
2224     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2225       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2226         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2227     }\r
2228     DrawMenuBar(hwndMain);\r
2229   }\r
2230 \r
2231   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2232   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2233 \r
2234   /* Get text area sizes */\r
2235   hdc = GetDC(hwndMain);\r
2236   if (appData.clockMode) {\r
2237     sprintf(buf, _("White: %s"), TimeString(23*60*60*1000L));\r
2238   } else {\r
2239     sprintf(buf, _("White"));\r
2240   }\r
2241   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2242   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2243   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2244   str = _("We only care about the height here");\r
2245   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2246   SelectObject(hdc, oldFont);\r
2247   ReleaseDC(hwndMain, hdc);\r
2248 \r
2249   /* Compute where everything goes */\r
2250   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2251         /* [HGM] logo: if either logo is on, reserve space for it */\r
2252         logoHeight =  2*clockSize.cy;\r
2253         leftLogoRect.left   = OUTER_MARGIN;\r
2254         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2255         leftLogoRect.top    = OUTER_MARGIN;\r
2256         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2257 \r
2258         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2259         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2260         rightLogoRect.top    = OUTER_MARGIN;\r
2261         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2262 \r
2263 \r
2264     whiteRect.left = leftLogoRect.right;\r
2265     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2266     whiteRect.top = OUTER_MARGIN;\r
2267     whiteRect.bottom = whiteRect.top + logoHeight;\r
2268 \r
2269     blackRect.right = rightLogoRect.left;\r
2270     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2271     blackRect.top = whiteRect.top;\r
2272     blackRect.bottom = whiteRect.bottom;\r
2273   } else {\r
2274     whiteRect.left = OUTER_MARGIN;\r
2275     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2276     whiteRect.top = OUTER_MARGIN;\r
2277     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2278 \r
2279     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2280     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2281     blackRect.top = whiteRect.top;\r
2282     blackRect.bottom = whiteRect.bottom;\r
2283 \r
2284     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2285   }\r
2286 \r
2287   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2288   if (appData.showButtonBar) {\r
2289     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2290       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2291   } else {\r
2292     messageRect.right = OUTER_MARGIN + boardWidth;\r
2293   }\r
2294   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2295   messageRect.bottom = messageRect.top + messageSize.cy;\r
2296 \r
2297   boardRect.left = OUTER_MARGIN;\r
2298   boardRect.right = boardRect.left + boardWidth;\r
2299   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2300   boardRect.bottom = boardRect.top + boardHeight;\r
2301 \r
2302   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2303   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2304   oldBoardSize = boardSize;\r
2305   oldTinyLayout = tinyLayout;\r
2306   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2307   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2308     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2309   winW *= 1 + twoBoards;\r
2310   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2311   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2312   wpMain.height = winH; //       without disturbing window attachments\r
2313   GetWindowRect(hwndMain, &wrect);\r
2314   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2315                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2316 \r
2317   // [HGM] placement: let attached windows follow size change.\r
2318   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2319   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2320   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2321   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2322   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2323 \r
2324   /* compensate if menu bar wrapped */\r
2325   GetClientRect(hwndMain, &crect);\r
2326   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2327   wpMain.height += offby;\r
2328   switch (flags) {\r
2329   case WMSZ_TOPLEFT:\r
2330     SetWindowPos(hwndMain, NULL, \r
2331                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2332                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2333     break;\r
2334 \r
2335   case WMSZ_TOPRIGHT:\r
2336   case WMSZ_TOP:\r
2337     SetWindowPos(hwndMain, NULL, \r
2338                  wrect.left, wrect.bottom - wpMain.height, \r
2339                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2340     break;\r
2341 \r
2342   case WMSZ_BOTTOMLEFT:\r
2343   case WMSZ_LEFT:\r
2344     SetWindowPos(hwndMain, NULL, \r
2345                  wrect.right - wpMain.width, wrect.top, \r
2346                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2347     break;\r
2348 \r
2349   case WMSZ_BOTTOMRIGHT:\r
2350   case WMSZ_BOTTOM:\r
2351   case WMSZ_RIGHT:\r
2352   default:\r
2353     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2354                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2355     break;\r
2356   }\r
2357 \r
2358   hwndPause = NULL;\r
2359   for (i = 0; i < N_BUTTONS; i++) {\r
2360     if (buttonDesc[i].hwnd != NULL) {\r
2361       DestroyWindow(buttonDesc[i].hwnd);\r
2362       buttonDesc[i].hwnd = NULL;\r
2363     }\r
2364     if (appData.showButtonBar) {\r
2365       buttonDesc[i].hwnd =\r
2366         CreateWindow("BUTTON", buttonDesc[i].label,\r
2367                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2368                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2369                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2370                      (HMENU) buttonDesc[i].id,\r
2371                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
2372       if (tinyLayout) {\r
2373         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2374                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2375                     MAKELPARAM(FALSE, 0));\r
2376       }\r
2377       if (buttonDesc[i].id == IDM_Pause)\r
2378         hwndPause = buttonDesc[i].hwnd;\r
2379       buttonDesc[i].wndproc = (WNDPROC)\r
2380         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
2381     }\r
2382   }\r
2383   if (gridPen != NULL) DeleteObject(gridPen);\r
2384   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2385   if (premovePen != NULL) DeleteObject(premovePen);\r
2386   if (lineGap != 0) {\r
2387     logbrush.lbStyle = BS_SOLID;\r
2388     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2389     gridPen =\r
2390       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2391                    lineGap, &logbrush, 0, NULL);\r
2392     logbrush.lbColor = highlightSquareColor;\r
2393     highlightPen =\r
2394       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2395                    lineGap, &logbrush, 0, NULL);\r
2396 \r
2397     logbrush.lbColor = premoveHighlightColor; \r
2398     premovePen =\r
2399       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2400                    lineGap, &logbrush, 0, NULL);\r
2401 \r
2402     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2403     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2404       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2405       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2406         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2407       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2408         BOARD_WIDTH * (squareSize + lineGap);\r
2409       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2410     }\r
2411     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2412       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2413       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2414         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2415         lineGap / 2 + (i * (squareSize + lineGap));\r
2416       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2417         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2418       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2419     }\r
2420   }\r
2421 \r
2422   /* [HGM] Licensing requirement */\r
2423 #ifdef GOTHIC\r
2424   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2425 #endif\r
2426 #ifdef FALCON\r
2427   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2428 #endif\r
2429   GothicPopUp( "", VariantNormal);\r
2430 \r
2431 \r
2432 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2433 \r
2434   /* Load piece bitmaps for this board size */\r
2435   for (i=0; i<=2; i++) {\r
2436     for (piece = WhitePawn;\r
2437          (int) piece < (int) BlackPawn;\r
2438          piece = (ChessSquare) ((int) piece + 1)) {\r
2439       if (pieceBitmap[i][piece] != NULL)\r
2440         DeleteObject(pieceBitmap[i][piece]);\r
2441     }\r
2442   }\r
2443 \r
2444   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2445   // Orthodox Chess pieces\r
2446   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2447   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2448   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2449   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2450   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2451   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2452   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2453   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2454   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2455   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2456   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2457   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2458   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2459   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2460   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2461   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
2462     // in Shogi, Hijack the unused Queen for Lance\r
2463     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2464     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2465     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2466   } else {\r
2467     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2468     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2469     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2470   }\r
2471 \r
2472   if(squareSize <= 72 && squareSize >= 33) { \r
2473     /* A & C are available in most sizes now */\r
2474     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2475       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2476       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2477       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2478       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2479       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2480       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2481       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2482       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2483       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2484       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2485       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2486       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2487     } else { // Smirf-like\r
2488       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2489       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2490       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2491     }\r
2492     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2493       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2494       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2495       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2496     } else { // WinBoard standard\r
2497       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2498       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2499       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2500     }\r
2501   }\r
2502 \r
2503 \r
2504   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2505     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2506     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2507     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2508     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2509     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2510     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2511     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2512     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2513     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2514     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2515     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2516     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2517     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2518     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2519     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2520     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2521     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2522     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2523     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2524     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2525     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2526     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2527     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2528     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2529     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2530     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2531     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2532     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2533     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2534     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2535 \r
2536     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2537       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2538       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2539       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2540       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2541       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2542       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2543       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2544       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2545       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2546       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2547       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2548       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2549     } else {\r
2550       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2551       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2552       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2553       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2554       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2555       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2556       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2557       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2558       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2559       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2560       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2561       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2562     }\r
2563 \r
2564   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2565     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2566     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2567     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2568     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2569     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2570     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2571     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2572     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2573     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2574     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2575     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2576     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2577     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2578     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2579   }\r
2580 \r
2581 \r
2582   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2583   /* special Shogi support in this size */\r
2584   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2585       for (piece = WhitePawn;\r
2586            (int) piece < (int) BlackPawn;\r
2587            piece = (ChessSquare) ((int) piece + 1)) {\r
2588         if (pieceBitmap[i][piece] != NULL)\r
2589           DeleteObject(pieceBitmap[i][piece]);\r
2590       }\r
2591     }\r
2592   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2593   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2594   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2595   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2596   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2597   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2598   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2599   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2600   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2601   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2602   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2603   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2604   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2605   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2606   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2607   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2608   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2609   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2610   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2611   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2612   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2613   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2614   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2615   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2616   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2617   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2618   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2619   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2620   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2621   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2622   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2623   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2624   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2625   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2626   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2627   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2628   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2629   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2630   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2631   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2632   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2633   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2634   minorSize = 0;\r
2635   }\r
2636 }\r
2637 \r
2638 HBITMAP\r
2639 PieceBitmap(ChessSquare p, int kind)\r
2640 {\r
2641   if ((int) p >= (int) BlackPawn)\r
2642     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2643 \r
2644   return pieceBitmap[kind][(int) p];\r
2645 }\r
2646 \r
2647 /***************************************************************/\r
2648 \r
2649 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2650 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2651 /*\r
2652 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2653 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2654 */\r
2655 \r
2656 VOID\r
2657 SquareToPos(int row, int column, int * x, int * y)\r
2658 {\r
2659   if (flipView) {\r
2660     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2661     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2662   } else {\r
2663     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2664     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2665   }\r
2666 }\r
2667 \r
2668 VOID\r
2669 DrawCoordsOnDC(HDC hdc)\r
2670 {\r
2671   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
2672   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
2673   char str[2] = { NULLCHAR, NULLCHAR };\r
2674   int oldMode, oldAlign, x, y, start, i;\r
2675   HFONT oldFont;\r
2676   HBRUSH oldBrush;\r
2677 \r
2678   if (!appData.showCoords)\r
2679     return;\r
2680 \r
2681   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2682 \r
2683   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2684   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2685   oldAlign = GetTextAlign(hdc);\r
2686   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2687 \r
2688   y = boardRect.top + lineGap;\r
2689   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2690 \r
2691   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2692   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2693     str[0] = files[start + i];\r
2694     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2695     y += squareSize + lineGap;\r
2696   }\r
2697 \r
2698   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2699 \r
2700   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2701   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2702     str[0] = ranks[start + i];\r
2703     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2704     x += squareSize + lineGap;\r
2705   }    \r
2706 \r
2707   SelectObject(hdc, oldBrush);\r
2708   SetBkMode(hdc, oldMode);\r
2709   SetTextAlign(hdc, oldAlign);\r
2710   SelectObject(hdc, oldFont);\r
2711 }\r
2712 \r
2713 VOID\r
2714 DrawGridOnDC(HDC hdc)\r
2715 {\r
2716   HPEN oldPen;\r
2717  \r
2718   if (lineGap != 0) {\r
2719     oldPen = SelectObject(hdc, gridPen);\r
2720     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2721     SelectObject(hdc, oldPen);\r
2722   }\r
2723 }\r
2724 \r
2725 #define HIGHLIGHT_PEN 0\r
2726 #define PREMOVE_PEN   1\r
2727 \r
2728 VOID\r
2729 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2730 {\r
2731   int x1, y1;\r
2732   HPEN oldPen, hPen;\r
2733   if (lineGap == 0) return;\r
2734   if (flipView) {\r
2735     x1 = boardRect.left +\r
2736       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2737     y1 = boardRect.top +\r
2738       lineGap/2 + y * (squareSize + lineGap);\r
2739   } else {\r
2740     x1 = boardRect.left +\r
2741       lineGap/2 + x * (squareSize + lineGap);\r
2742     y1 = boardRect.top +\r
2743       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2744   }\r
2745   hPen = pen ? premovePen : highlightPen;\r
2746   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2747   MoveToEx(hdc, x1, y1, NULL);\r
2748   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2749   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2750   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2751   LineTo(hdc, x1, y1);\r
2752   SelectObject(hdc, oldPen);\r
2753 }\r
2754 \r
2755 VOID\r
2756 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2757 {\r
2758   int i;\r
2759   for (i=0; i<2; i++) {\r
2760     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2761       DrawHighlightOnDC(hdc, TRUE,\r
2762                         h->sq[i].x, h->sq[i].y,\r
2763                         pen);\r
2764   }\r
2765 }\r
2766 \r
2767 /* Note: sqcolor is used only in monoMode */\r
2768 /* Note that this code is largely duplicated in woptions.c,\r
2769    function DrawSampleSquare, so that needs to be updated too */\r
2770 VOID\r
2771 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2772 {\r
2773   HBITMAP oldBitmap;\r
2774   HBRUSH oldBrush;\r
2775   int tmpSize;\r
2776 \r
2777   if (appData.blindfold) return;\r
2778 \r
2779   /* [AS] Use font-based pieces if needed */\r
2780   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2781     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2782     CreatePiecesFromFont();\r
2783 \r
2784     if( fontBitmapSquareSize == squareSize ) {\r
2785         int index = TranslatePieceToFontPiece(piece);\r
2786 \r
2787         SelectObject( tmphdc, hPieceMask[ index ] );\r
2788 \r
2789       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2790         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2791       else\r
2792         BitBlt( hdc,\r
2793             x, y,\r
2794             squareSize, squareSize,\r
2795             tmphdc,\r
2796             0, 0,\r
2797             SRCAND );\r
2798 \r
2799         SelectObject( tmphdc, hPieceFace[ index ] );\r
2800 \r
2801       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2802         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2803       else\r
2804         BitBlt( hdc,\r
2805             x, y,\r
2806             squareSize, squareSize,\r
2807             tmphdc,\r
2808             0, 0,\r
2809             SRCPAINT );\r
2810 \r
2811         return;\r
2812     }\r
2813   }\r
2814 \r
2815   if (appData.monoMode) {\r
2816     SelectObject(tmphdc, PieceBitmap(piece, \r
2817       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2818     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2819            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2820   } else {\r
2821     tmpSize = squareSize;\r
2822     if(minorSize &&\r
2823         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2824          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2825       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2826       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2827       x += (squareSize - minorSize)>>1;\r
2828       y += squareSize - minorSize - 2;\r
2829       tmpSize = minorSize;\r
2830     }\r
2831     if (color || appData.allWhite ) {\r
2832       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2833       if( color )\r
2834               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2835       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2836       if(appData.upsideDown && color==flipView)\r
2837         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2838       else\r
2839         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2840       /* Use black for outline of white pieces */\r
2841       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2842       if(appData.upsideDown && color==flipView)\r
2843         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2844       else\r
2845         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2846     } else {\r
2847       /* Use square color for details of black pieces */\r
2848       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2849       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2850       if(appData.upsideDown && !flipView)\r
2851         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2852       else\r
2853         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2854     }\r
2855     SelectObject(hdc, oldBrush);\r
2856     SelectObject(tmphdc, oldBitmap);\r
2857   }\r
2858 }\r
2859 \r
2860 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2861 int GetBackTextureMode( int algo )\r
2862 {\r
2863     int result = BACK_TEXTURE_MODE_DISABLED;\r
2864 \r
2865     switch( algo ) \r
2866     {\r
2867         case BACK_TEXTURE_MODE_PLAIN:\r
2868             result = 1; /* Always use identity map */\r
2869             break;\r
2870         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2871             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2872             break;\r
2873     }\r
2874 \r
2875     return result;\r
2876 }\r
2877 \r
2878 /* \r
2879     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2880     to handle redraws cleanly (as random numbers would always be different).\r
2881 */\r
2882 VOID RebuildTextureSquareInfo()\r
2883 {\r
2884     BITMAP bi;\r
2885     int lite_w = 0;\r
2886     int lite_h = 0;\r
2887     int dark_w = 0;\r
2888     int dark_h = 0;\r
2889     int row;\r
2890     int col;\r
2891 \r
2892     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2893 \r
2894     if( liteBackTexture != NULL ) {\r
2895         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2896             lite_w = bi.bmWidth;\r
2897             lite_h = bi.bmHeight;\r
2898         }\r
2899     }\r
2900 \r
2901     if( darkBackTexture != NULL ) {\r
2902         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2903             dark_w = bi.bmWidth;\r
2904             dark_h = bi.bmHeight;\r
2905         }\r
2906     }\r
2907 \r
2908     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2909         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2910             if( (col + row) & 1 ) {\r
2911                 /* Lite square */\r
2912                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2913                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2914                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2915                   else\r
2916                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2917                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2918                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2919                   else\r
2920                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2921                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2922                 }\r
2923             }\r
2924             else {\r
2925                 /* Dark square */\r
2926                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2927                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2928                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2929                   else\r
2930                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2931                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2932                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2933                   else\r
2934                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2935                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2936                 }\r
2937             }\r
2938         }\r
2939     }\r
2940 }\r
2941 \r
2942 /* [AS] Arrow highlighting support */\r
2943 \r
2944 static int A_WIDTH = 5; /* Width of arrow body */\r
2945 \r
2946 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2947 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2948 \r
2949 static double Sqr( double x )\r
2950 {\r
2951     return x*x;\r
2952 }\r
2953 \r
2954 static int Round( double x )\r
2955 {\r
2956     return (int) (x + 0.5);\r
2957 }\r
2958 \r
2959 /* Draw an arrow between two points using current settings */\r
2960 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2961 {\r
2962     POINT arrow[7];\r
2963     double dx, dy, j, k, x, y;\r
2964 \r
2965     if( d_x == s_x ) {\r
2966         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2967 \r
2968         arrow[0].x = s_x + A_WIDTH;\r
2969         arrow[0].y = s_y;\r
2970 \r
2971         arrow[1].x = s_x + A_WIDTH;\r
2972         arrow[1].y = d_y - h;\r
2973 \r
2974         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
2975         arrow[2].y = d_y - h;\r
2976 \r
2977         arrow[3].x = d_x;\r
2978         arrow[3].y = d_y;\r
2979 \r
2980         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
2981         arrow[4].y = d_y - h;\r
2982 \r
2983         arrow[5].x = s_x - A_WIDTH;\r
2984         arrow[5].y = d_y - h;\r
2985 \r
2986         arrow[6].x = s_x - A_WIDTH;\r
2987         arrow[6].y = s_y;\r
2988     }\r
2989     else if( d_y == s_y ) {\r
2990         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2991 \r
2992         arrow[0].x = s_x;\r
2993         arrow[0].y = s_y + A_WIDTH;\r
2994 \r
2995         arrow[1].x = d_x - w;\r
2996         arrow[1].y = s_y + A_WIDTH;\r
2997 \r
2998         arrow[2].x = d_x - w;\r
2999         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3000 \r
3001         arrow[3].x = d_x;\r
3002         arrow[3].y = d_y;\r
3003 \r
3004         arrow[4].x = d_x - w;\r
3005         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3006 \r
3007         arrow[5].x = d_x - w;\r
3008         arrow[5].y = s_y - A_WIDTH;\r
3009 \r
3010         arrow[6].x = s_x;\r
3011         arrow[6].y = s_y - A_WIDTH;\r
3012     }\r
3013     else {\r
3014         /* [AS] Needed a lot of paper for this! :-) */\r
3015         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3016         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3017   \r
3018         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3019 \r
3020         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3021 \r
3022         x = s_x;\r
3023         y = s_y;\r
3024 \r
3025         arrow[0].x = Round(x - j);\r
3026         arrow[0].y = Round(y + j*dx);\r
3027 \r
3028         arrow[1].x = Round(x + j);\r
3029         arrow[1].y = Round(y - j*dx);\r
3030 \r
3031         if( d_x > s_x ) {\r
3032             x = (double) d_x - k;\r
3033             y = (double) d_y - k*dy;\r
3034         }\r
3035         else {\r
3036             x = (double) d_x + k;\r
3037             y = (double) d_y + k*dy;\r
3038         }\r
3039 \r
3040         arrow[2].x = Round(x + j);\r
3041         arrow[2].y = Round(y - j*dx);\r
3042 \r
3043         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3044         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3045 \r
3046         arrow[4].x = d_x;\r
3047         arrow[4].y = d_y;\r
3048 \r
3049         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3050         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3051 \r
3052         arrow[6].x = Round(x - j);\r
3053         arrow[6].y = Round(y + j*dx);\r
3054     }\r
3055 \r
3056     Polygon( hdc, arrow, 7 );\r
3057 }\r
3058 \r
3059 /* [AS] Draw an arrow between two squares */\r
3060 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3061 {\r
3062     int s_x, s_y, d_x, d_y;\r
3063     HPEN hpen;\r
3064     HPEN holdpen;\r
3065     HBRUSH hbrush;\r
3066     HBRUSH holdbrush;\r
3067     LOGBRUSH stLB;\r
3068 \r
3069     if( s_col == d_col && s_row == d_row ) {\r
3070         return;\r
3071     }\r
3072 \r
3073     /* Get source and destination points */\r
3074     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3075     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3076 \r
3077     if( d_y > s_y ) {\r
3078         d_y += squareSize / 4;\r
3079     }\r
3080     else if( d_y < s_y ) {\r
3081         d_y += 3 * squareSize / 4;\r
3082     }\r
3083     else {\r
3084         d_y += squareSize / 2;\r
3085     }\r
3086 \r
3087     if( d_x > s_x ) {\r
3088         d_x += squareSize / 4;\r
3089     }\r
3090     else if( d_x < s_x ) {\r
3091         d_x += 3 * squareSize / 4;\r
3092     }\r
3093     else {\r
3094         d_x += squareSize / 2;\r
3095     }\r
3096 \r
3097     s_x += squareSize / 2;\r
3098     s_y += squareSize / 2;\r
3099 \r
3100     /* Adjust width */\r
3101     A_WIDTH = squareSize / 14;\r
3102 \r
3103     /* Draw */\r
3104     stLB.lbStyle = BS_SOLID;\r
3105     stLB.lbColor = appData.highlightArrowColor;\r
3106     stLB.lbHatch = 0;\r
3107 \r
3108     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3109     holdpen = SelectObject( hdc, hpen );\r
3110     hbrush = CreateBrushIndirect( &stLB );\r
3111     holdbrush = SelectObject( hdc, hbrush );\r
3112 \r
3113     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3114 \r
3115     SelectObject( hdc, holdpen );\r
3116     SelectObject( hdc, holdbrush );\r
3117     DeleteObject( hpen );\r
3118     DeleteObject( hbrush );\r
3119 }\r
3120 \r
3121 BOOL HasHighlightInfo()\r
3122 {\r
3123     BOOL result = FALSE;\r
3124 \r
3125     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3126         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3127     {\r
3128         result = TRUE;\r
3129     }\r
3130 \r
3131     return result;\r
3132 }\r
3133 \r
3134 BOOL IsDrawArrowEnabled()\r
3135 {\r
3136     BOOL result = FALSE;\r
3137 \r
3138     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3139         result = TRUE;\r
3140     }\r
3141 \r
3142     return result;\r
3143 }\r
3144 \r
3145 VOID DrawArrowHighlight( HDC hdc )\r
3146 {\r
3147     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3148         DrawArrowBetweenSquares( hdc,\r
3149             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3150             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3151     }\r
3152 }\r
3153 \r
3154 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3155 {\r
3156     HRGN result = NULL;\r
3157 \r
3158     if( HasHighlightInfo() ) {\r
3159         int x1, y1, x2, y2;\r
3160         int sx, sy, dx, dy;\r
3161 \r
3162         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3163         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3164 \r
3165         sx = MIN( x1, x2 );\r
3166         sy = MIN( y1, y2 );\r
3167         dx = MAX( x1, x2 ) + squareSize;\r
3168         dy = MAX( y1, y2 ) + squareSize;\r
3169 \r
3170         result = CreateRectRgn( sx, sy, dx, dy );\r
3171     }\r
3172 \r
3173     return result;\r
3174 }\r
3175 \r
3176 /*\r
3177     Warning: this function modifies the behavior of several other functions. \r
3178     \r
3179     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3180     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3181     repaint is scattered all over the place, which is not good for features such as\r
3182     "arrow highlighting" that require a full repaint of the board.\r
3183 \r
3184     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3185     user interaction, when speed is not so important) but especially to avoid errors\r
3186     in the displayed graphics.\r
3187 \r
3188     In such patched places, I always try refer to this function so there is a single\r
3189     place to maintain knowledge.\r
3190     \r
3191     To restore the original behavior, just return FALSE unconditionally.\r
3192 */\r
3193 BOOL IsFullRepaintPreferrable()\r
3194 {\r
3195     BOOL result = FALSE;\r
3196 \r
3197     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3198         /* Arrow may appear on the board */\r
3199         result = TRUE;\r
3200     }\r
3201 \r
3202     return result;\r
3203 }\r
3204 \r
3205 /* \r
3206     This function is called by DrawPosition to know whether a full repaint must\r
3207     be forced or not.\r
3208 \r
3209     Only DrawPosition may directly call this function, which makes use of \r
3210     some state information. Other function should call DrawPosition specifying \r
3211     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3212 */\r
3213 BOOL DrawPositionNeedsFullRepaint()\r
3214 {\r
3215     BOOL result = FALSE;\r
3216 \r
3217     /* \r
3218         Probably a slightly better policy would be to trigger a full repaint\r
3219         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3220         but animation is fast enough that it's difficult to notice.\r
3221     */\r
3222     if( animInfo.piece == EmptySquare ) {\r
3223         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3224             result = TRUE;\r
3225         }\r
3226     }\r
3227 \r
3228     return result;\r
3229 }\r
3230 \r
3231 VOID\r
3232 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3233 {\r
3234   int row, column, x, y, square_color, piece_color;\r
3235   ChessSquare piece;\r
3236   HBRUSH oldBrush;\r
3237   HDC texture_hdc = NULL;\r
3238 \r
3239   /* [AS] Initialize background textures if needed */\r
3240   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3241       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3242       if( backTextureSquareSize != squareSize \r
3243        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3244           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3245           backTextureSquareSize = squareSize;\r
3246           RebuildTextureSquareInfo();\r
3247       }\r
3248 \r
3249       texture_hdc = CreateCompatibleDC( hdc );\r
3250   }\r
3251 \r
3252   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3253     for (column = 0; column < BOARD_WIDTH; column++) {\r
3254   \r
3255       SquareToPos(row, column, &x, &y);\r
3256 \r
3257       piece = board[row][column];\r
3258 \r
3259       square_color = ((column + row) % 2) == 1;\r
3260       if( gameInfo.variant == VariantXiangqi ) {\r
3261           square_color = !InPalace(row, column);\r
3262           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3263           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3264       }\r
3265       piece_color = (int) piece < (int) BlackPawn;\r
3266 \r
3267 \r
3268       /* [HGM] holdings file: light square or black */\r
3269       if(column == BOARD_LEFT-2) {\r
3270             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3271                 square_color = 1;\r
3272             else {\r
3273                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3274                 continue;\r
3275             }\r
3276       } else\r
3277       if(column == BOARD_RGHT + 1 ) {\r
3278             if( row < gameInfo.holdingsSize )\r
3279                 square_color = 1;\r
3280             else {\r
3281                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3282                 continue;\r
3283             }\r
3284       }\r
3285       if(column == BOARD_LEFT-1 ) /* left align */\r
3286             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3287       else if( column == BOARD_RGHT) /* right align */\r
3288             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3289       else\r
3290       if (appData.monoMode) {\r
3291         if (piece == EmptySquare) {\r
3292           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3293                  square_color ? WHITENESS : BLACKNESS);\r
3294         } else {\r
3295           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3296         }\r
3297       } \r
3298       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3299           /* [AS] Draw the square using a texture bitmap */\r
3300           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3301           int r = row, c = column; // [HGM] do not flip board in flipView\r
3302           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3303 \r
3304           DrawTile( x, y, \r
3305               squareSize, squareSize, \r
3306               hdc, \r
3307               texture_hdc,\r
3308               backTextureSquareInfo[r][c].mode,\r
3309               backTextureSquareInfo[r][c].x,\r
3310               backTextureSquareInfo[r][c].y );\r
3311 \r
3312           SelectObject( texture_hdc, hbm );\r
3313 \r
3314           if (piece != EmptySquare) {\r
3315               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3316           }\r
3317       }\r
3318       else {\r
3319         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3320 \r
3321         oldBrush = SelectObject(hdc, brush );\r
3322         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3323         SelectObject(hdc, oldBrush);\r
3324         if (piece != EmptySquare)\r
3325           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3326       }\r
3327     }\r
3328   }\r
3329 \r
3330   if( texture_hdc != NULL ) {\r
3331     DeleteDC( texture_hdc );\r
3332   }\r
3333 }\r
3334 \r
3335 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3336 void fputDW(FILE *f, int x)\r
3337 {\r
3338         fputc(x     & 255, f);\r
3339         fputc(x>>8  & 255, f);\r
3340         fputc(x>>16 & 255, f);\r
3341         fputc(x>>24 & 255, f);\r
3342 }\r
3343 \r
3344 #define MAX_CLIPS 200   /* more than enough */\r
3345 \r
3346 VOID\r
3347 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3348 {\r
3349 //  HBITMAP bufferBitmap;\r
3350   BITMAP bi;\r
3351 //  RECT Rect;\r
3352   HDC tmphdc;\r
3353   HBITMAP hbm;\r
3354   int w = 100, h = 50;\r
3355 \r
3356   if(logo == NULL) return;\r
3357 //  GetClientRect(hwndMain, &Rect);\r
3358 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3359 //                                      Rect.bottom-Rect.top+1);\r
3360   tmphdc = CreateCompatibleDC(hdc);\r
3361   hbm = SelectObject(tmphdc, logo);\r
3362   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3363             w = bi.bmWidth;\r
3364             h = bi.bmHeight;\r
3365   }\r
3366   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3367                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3368   SelectObject(tmphdc, hbm);\r
3369   DeleteDC(tmphdc);\r
3370 }\r
3371 \r
3372 static HDC hdcSeek;\r
3373 \r
3374 // [HGM] seekgraph\r
3375 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3376 {\r
3377     POINT stPt;\r
3378     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3379     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3380     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3381     SelectObject( hdcSeek, hp );\r
3382 }\r
3383 \r
3384 // front-end wrapper for drawing functions to do rectangles\r
3385 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3386 {\r
3387     HPEN hp;\r
3388     RECT rc;\r
3389 \r
3390     if (hdcSeek == NULL) {\r
3391     hdcSeek = GetDC(hwndMain);\r
3392       if (!appData.monoMode) {\r
3393         SelectPalette(hdcSeek, hPal, FALSE);\r
3394         RealizePalette(hdcSeek);\r
3395       }\r
3396     }\r
3397     hp = SelectObject( hdcSeek, gridPen );\r
3398     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3399     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3400     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3401     SelectObject( hdcSeek, hp );\r
3402 }\r
3403 \r
3404 // front-end wrapper for putting text in graph\r
3405 void DrawSeekText(char *buf, int x, int y)\r
3406 {\r
3407         SIZE stSize;\r
3408         SetBkMode( hdcSeek, TRANSPARENT );\r
3409         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3410         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3411 }\r
3412 \r
3413 void DrawSeekDot(int x, int y, int color)\r
3414 {\r
3415         int square = color & 0x80;\r
3416         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3417                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3418         color &= 0x7F;\r
3419         if(square)\r
3420             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3421                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3422         else\r
3423             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3424                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3425             SelectObject(hdcSeek, oldBrush);\r
3426 }\r
3427 \r
3428 VOID\r
3429 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3430 {\r
3431   static Board lastReq[2], lastDrawn[2];\r
3432   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3433   static int lastDrawnFlipView = 0;\r
3434   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3435   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3436   HDC tmphdc;\r
3437   HDC hdcmem;\r
3438   HBITMAP bufferBitmap;\r
3439   HBITMAP oldBitmap;\r
3440   RECT Rect;\r
3441   HRGN clips[MAX_CLIPS];\r
3442   ChessSquare dragged_piece = EmptySquare;\r
3443   int nr = twoBoards*partnerUp;\r
3444 \r
3445   /* I'm undecided on this - this function figures out whether a full\r
3446    * repaint is necessary on its own, so there's no real reason to have the\r
3447    * caller tell it that.  I think this can safely be set to FALSE - but\r
3448    * if we trust the callers not to request full repaints unnessesarily, then\r
3449    * we could skip some clipping work.  In other words, only request a full\r
3450    * redraw when the majority of pieces have changed positions (ie. flip, \r
3451    * gamestart and similar)  --Hawk\r
3452    */\r
3453   Boolean fullrepaint = repaint;\r
3454 \r
3455   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3456 \r
3457   if( DrawPositionNeedsFullRepaint() ) {\r
3458       fullrepaint = TRUE;\r
3459   }\r
3460 \r
3461   if (board == NULL) {\r
3462     if (!lastReqValid[nr]) {\r
3463       return;\r
3464     }\r
3465     board = lastReq[nr];\r
3466   } else {\r
3467     CopyBoard(lastReq[nr], board);\r
3468     lastReqValid[nr] = 1;\r
3469   }\r
3470 \r
3471   if (doingSizing) {\r
3472     return;\r
3473   }\r
3474 \r
3475   if (IsIconic(hwndMain)) {\r
3476     return;\r
3477   }\r
3478 \r
3479   if (hdc == NULL) {\r
3480     hdc = GetDC(hwndMain);\r
3481     if (!appData.monoMode) {\r
3482       SelectPalette(hdc, hPal, FALSE);\r
3483       RealizePalette(hdc);\r
3484     }\r
3485     releaseDC = TRUE;\r
3486   } else {\r
3487     releaseDC = FALSE;\r
3488   }\r
3489 \r
3490   /* Create some work-DCs */\r
3491   hdcmem = CreateCompatibleDC(hdc);\r
3492   tmphdc = CreateCompatibleDC(hdc);\r
3493 \r
3494   /* If dragging is in progress, we temporarely remove the piece */\r
3495   /* [HGM] or temporarily decrease count if stacked              */\r
3496   /*       !! Moved to before board compare !!                   */\r
3497   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3498     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3499     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3500             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3501         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3502     } else \r
3503     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3504             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3505         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3506     } else \r
3507         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3508   }\r
3509 \r
3510   /* Figure out which squares need updating by comparing the \r
3511    * newest board with the last drawn board and checking if\r
3512    * flipping has changed.\r
3513    */\r
3514   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3515     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3516       for (column = 0; column < BOARD_WIDTH; column++) {\r
3517         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3518           SquareToPos(row, column, &x, &y);\r
3519           clips[num_clips++] =\r
3520             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3521         }\r
3522       }\r
3523     }\r
3524    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3525     for (i=0; i<2; i++) {\r
3526       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3527           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3528         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3529             lastDrawnHighlight.sq[i].y >= 0) {\r
3530           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3531                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3532           clips[num_clips++] =\r
3533             CreateRectRgn(x - lineGap, y - lineGap, \r
3534                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3535         }\r
3536         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3537           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3538           clips[num_clips++] =\r
3539             CreateRectRgn(x - lineGap, y - lineGap, \r
3540                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3541         }\r
3542       }\r
3543     }\r
3544     for (i=0; i<2; i++) {\r
3545       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3546           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3547         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3548             lastDrawnPremove.sq[i].y >= 0) {\r
3549           SquareToPos(lastDrawnPremove.sq[i].y,\r
3550                       lastDrawnPremove.sq[i].x, &x, &y);\r
3551           clips[num_clips++] =\r
3552             CreateRectRgn(x - lineGap, y - lineGap, \r
3553                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3554         }\r
3555         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3556             premoveHighlightInfo.sq[i].y >= 0) {\r
3557           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3558                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3559           clips[num_clips++] =\r
3560             CreateRectRgn(x - lineGap, y - lineGap, \r
3561                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3562         }\r
3563       }\r
3564     }\r
3565    } else { // nr == 1\r
3566         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3567         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3568         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3569         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3570       for (i=0; i<2; i++) {\r
3571         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3572             partnerHighlightInfo.sq[i].y >= 0) {\r
3573           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3574                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3575           clips[num_clips++] =\r
3576             CreateRectRgn(x - lineGap, y - lineGap, \r
3577                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3578         }\r
3579         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3580             oldPartnerHighlight.sq[i].y >= 0) {\r
3581           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3582                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3583           clips[num_clips++] =\r
3584             CreateRectRgn(x - lineGap, y - lineGap, \r
3585                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3586         }\r
3587       }\r
3588    }\r
3589   } else {\r
3590     fullrepaint = TRUE;\r
3591   }\r
3592 \r
3593   /* Create a buffer bitmap - this is the actual bitmap\r
3594    * being written to.  When all the work is done, we can\r
3595    * copy it to the real DC (the screen).  This avoids\r
3596    * the problems with flickering.\r
3597    */\r
3598   GetClientRect(hwndMain, &Rect);\r
3599   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3600                                         Rect.bottom-Rect.top+1);\r
3601   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3602   if (!appData.monoMode) {\r
3603     SelectPalette(hdcmem, hPal, FALSE);\r
3604   }\r
3605 \r
3606   /* Create clips for dragging */\r
3607   if (!fullrepaint) {\r
3608     if (dragInfo.from.x >= 0) {\r
3609       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3610       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3611     }\r
3612     if (dragInfo.start.x >= 0) {\r
3613       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3614       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3615     }\r
3616     if (dragInfo.pos.x >= 0) {\r
3617       x = dragInfo.pos.x - squareSize / 2;\r
3618       y = dragInfo.pos.y - squareSize / 2;\r
3619       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3620     }\r
3621     if (dragInfo.lastpos.x >= 0) {\r
3622       x = dragInfo.lastpos.x - squareSize / 2;\r
3623       y = dragInfo.lastpos.y - squareSize / 2;\r
3624       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3625     }\r
3626   }\r
3627 \r
3628   /* Are we animating a move?  \r
3629    * If so, \r
3630    *   - remove the piece from the board (temporarely)\r
3631    *   - calculate the clipping region\r
3632    */\r
3633   if (!fullrepaint) {\r
3634     if (animInfo.piece != EmptySquare) {\r
3635       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3636       x = boardRect.left + animInfo.lastpos.x;\r
3637       y = boardRect.top + animInfo.lastpos.y;\r
3638       x2 = boardRect.left + animInfo.pos.x;\r
3639       y2 = boardRect.top + animInfo.pos.y;\r
3640       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3641       /* Slight kludge.  The real problem is that after AnimateMove is\r
3642          done, the position on the screen does not match lastDrawn.\r
3643          This currently causes trouble only on e.p. captures in\r
3644          atomic, where the piece moves to an empty square and then\r
3645          explodes.  The old and new positions both had an empty square\r
3646          at the destination, but animation has drawn a piece there and\r
3647          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3648       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3649     }\r
3650   }\r
3651 \r
3652   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3653   if (num_clips == 0)\r
3654     fullrepaint = TRUE;\r
3655 \r
3656   /* Set clipping on the memory DC */\r
3657   if (!fullrepaint) {\r
3658     SelectClipRgn(hdcmem, clips[0]);\r
3659     for (x = 1; x < num_clips; x++) {\r
3660       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3661         abort();  // this should never ever happen!\r
3662     }\r
3663   }\r
3664 \r
3665   /* Do all the drawing to the memory DC */\r
3666   if(explodeInfo.radius) { // [HGM] atomic\r
3667         HBRUSH oldBrush;\r
3668         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3669         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3670         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3671         x += squareSize/2;\r
3672         y += squareSize/2;\r
3673         if(!fullrepaint) {\r
3674           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3675           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3676         }\r
3677         DrawGridOnDC(hdcmem);\r
3678         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3679         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3680         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3681         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3682         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3683         SelectObject(hdcmem, oldBrush);\r
3684   } else {\r
3685     DrawGridOnDC(hdcmem);\r
3686     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3687         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3688         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3689     } else {\r
3690         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3691         oldPartnerHighlight = partnerHighlightInfo;\r
3692     }\r
3693     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3694   }\r
3695   if(nr == 0) // [HGM] dual: markers only on left board\r
3696   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3697     for (column = 0; column < BOARD_WIDTH; column++) {\r
3698         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3699             HBRUSH oldBrush = SelectObject(hdcmem, \r
3700                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3701             SquareToPos(row, column, &x, &y);\r
3702             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3703                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3704             SelectObject(hdcmem, oldBrush);\r
3705         }\r
3706     }\r
3707   }\r
3708   if(logoHeight) {\r
3709         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3710         if(appData.autoLogo) {\r
3711           \r
3712           switch(gameMode) { // pick logos based on game mode\r
3713             case IcsObserving:\r
3714                 whiteLogo = second.programLogo; // ICS logo\r
3715                 blackLogo = second.programLogo;\r
3716             default:\r
3717                 break;\r
3718             case IcsPlayingWhite:\r
3719                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3720                 blackLogo = second.programLogo; // ICS logo\r
3721                 break;\r
3722             case IcsPlayingBlack:\r
3723                 whiteLogo = second.programLogo; // ICS logo\r
3724                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3725                 break;\r
3726             case TwoMachinesPlay:\r
3727                 if(first.twoMachinesColor[0] == 'b') {\r
3728                     whiteLogo = second.programLogo;\r
3729                     blackLogo = first.programLogo;\r
3730                 }\r
3731                 break;\r
3732             case MachinePlaysWhite:\r
3733                 blackLogo = userLogo;\r
3734                 break;\r
3735             case MachinePlaysBlack:\r
3736                 whiteLogo = userLogo;\r
3737                 blackLogo = first.programLogo;\r
3738           }\r
3739         }\r
3740         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3741         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3742   }\r
3743 \r
3744   if( appData.highlightMoveWithArrow ) {\r
3745     DrawArrowHighlight(hdcmem);\r
3746   }\r
3747 \r
3748   DrawCoordsOnDC(hdcmem);\r
3749 \r
3750   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3751                  /* to make sure lastDrawn contains what is actually drawn */\r
3752 \r
3753   /* Put the dragged piece back into place and draw it (out of place!) */\r
3754     if (dragged_piece != EmptySquare) {\r
3755     /* [HGM] or restack */\r
3756     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3757                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3758     else\r
3759     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3760                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3761     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3762     x = dragInfo.pos.x - squareSize / 2;\r
3763     y = dragInfo.pos.y - squareSize / 2;\r
3764     DrawPieceOnDC(hdcmem, dragged_piece,\r
3765                   ((int) dragged_piece < (int) BlackPawn), \r
3766                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3767   }   \r
3768   \r
3769   /* Put the animated piece back into place and draw it */\r
3770   if (animInfo.piece != EmptySquare) {\r
3771     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3772     x = boardRect.left + animInfo.pos.x;\r
3773     y = boardRect.top + animInfo.pos.y;\r
3774     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3775                   ((int) animInfo.piece < (int) BlackPawn),\r
3776                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3777   }\r
3778 \r
3779   /* Release the bufferBitmap by selecting in the old bitmap \r
3780    * and delete the memory DC\r
3781    */\r
3782   SelectObject(hdcmem, oldBitmap);\r
3783   DeleteDC(hdcmem);\r
3784 \r
3785   /* Set clipping on the target DC */\r
3786   if (!fullrepaint) {\r
3787     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3788         RECT rect;\r
3789         GetRgnBox(clips[x], &rect);\r
3790         DeleteObject(clips[x]);\r
3791         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3792                           rect.right + wpMain.width/2, rect.bottom);\r
3793     }\r
3794     SelectClipRgn(hdc, clips[0]);\r
3795     for (x = 1; x < num_clips; x++) {\r
3796       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3797         abort();   // this should never ever happen!\r
3798     } \r
3799   }\r
3800 \r
3801   /* Copy the new bitmap onto the screen in one go.\r
3802    * This way we avoid any flickering\r
3803    */\r
3804   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3805   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3806          boardRect.right - boardRect.left,\r
3807          boardRect.bottom - boardRect.top,\r
3808          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3809   if(saveDiagFlag) { \r
3810     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
3811     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3812 \r
3813     GetObject(bufferBitmap, sizeof(b), &b);\r
3814     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
3815         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3816         bih.biWidth = b.bmWidth;\r
3817         bih.biHeight = b.bmHeight;\r
3818         bih.biPlanes = 1;\r
3819         bih.biBitCount = b.bmBitsPixel;\r
3820         bih.biCompression = 0;\r
3821         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3822         bih.biXPelsPerMeter = 0;\r
3823         bih.biYPelsPerMeter = 0;\r
3824         bih.biClrUsed = 0;\r
3825         bih.biClrImportant = 0;\r
3826 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3827 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3828         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3829 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3830 \r
3831         wb = b.bmWidthBytes;\r
3832         // count colors\r
3833         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3834                 int k = ((int*) pData)[i];\r
3835                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3836                 if(j >= 16) break;\r
3837                 color[j] = k;\r
3838                 if(j >= nrColors) nrColors = j+1;\r
3839         }\r
3840         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3841                 INT p = 0;\r
3842                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3843                     for(w=0; w<(wb>>2); w+=2) {\r
3844                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3845                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3846                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3847                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3848                         pData[p++] = m | j<<4;\r
3849                     }\r
3850                     while(p&3) pData[p++] = 0;\r
3851                 }\r
3852                 fac = 3;\r
3853                 wb = ((wb+31)>>5)<<2;\r
3854         }\r
3855         // write BITMAPFILEHEADER\r
3856         fprintf(diagFile, "BM");\r
3857         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3858         fputDW(diagFile, 0);\r
3859         fputDW(diagFile, 0x36 + (fac?64:0));\r
3860         // write BITMAPINFOHEADER\r
3861         fputDW(diagFile, 40);\r
3862         fputDW(diagFile, b.bmWidth);\r
3863         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3864         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3865         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3866         fputDW(diagFile, 0);\r
3867         fputDW(diagFile, 0);\r
3868         fputDW(diagFile, 0);\r
3869         fputDW(diagFile, 0);\r
3870         fputDW(diagFile, 0);\r
3871         fputDW(diagFile, 0);\r
3872         // write color table\r
3873         if(fac)\r
3874         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3875         // write bitmap data\r
3876         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3877                 fputc(pData[i], diagFile);\r
3878      }\r
3879   }\r
3880 \r
3881   SelectObject(tmphdc, oldBitmap);\r
3882 \r
3883   /* Massive cleanup */\r
3884   for (x = 0; x < num_clips; x++)\r
3885     DeleteObject(clips[x]);\r
3886 \r
3887   DeleteDC(tmphdc);\r
3888   DeleteObject(bufferBitmap);\r
3889 \r
3890   if (releaseDC) \r
3891     ReleaseDC(hwndMain, hdc);\r
3892   \r
3893   if (lastDrawnFlipView != flipView && nr == 0) {\r
3894     if (flipView)\r
3895       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3896     else\r
3897       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3898   }\r
3899 \r
3900 /*  CopyBoard(lastDrawn, board);*/\r
3901   lastDrawnHighlight = highlightInfo;\r
3902   lastDrawnPremove   = premoveHighlightInfo;\r
3903   lastDrawnFlipView = flipView;\r
3904   lastDrawnValid[nr] = 1;\r
3905 }\r
3906 \r
3907 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3908 int\r
3909 SaveDiagram(f)\r
3910      FILE *f;\r
3911 {\r
3912     saveDiagFlag = 1; diagFile = f;\r
3913     HDCDrawPosition(NULL, TRUE, NULL);\r
3914 \r
3915     saveDiagFlag = 0;\r
3916 \r
3917 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
3918     \r
3919     fclose(f);\r
3920     return TRUE;\r
3921 }\r
3922 \r
3923 \r
3924 /*---------------------------------------------------------------------------*\\r
3925 | CLIENT PAINT PROCEDURE\r
3926 |   This is the main event-handler for the WM_PAINT message.\r
3927 |\r
3928 \*---------------------------------------------------------------------------*/\r
3929 VOID\r
3930 PaintProc(HWND hwnd)\r
3931 {\r
3932   HDC         hdc;\r
3933   PAINTSTRUCT ps;\r
3934   HFONT       oldFont;\r
3935 \r
3936   if((hdc = BeginPaint(hwnd, &ps))) {\r
3937     if (IsIconic(hwnd)) {\r
3938       DrawIcon(hdc, 2, 2, iconCurrent);\r
3939     } else {\r
3940       if (!appData.monoMode) {\r
3941         SelectPalette(hdc, hPal, FALSE);\r
3942         RealizePalette(hdc);\r
3943       }\r
3944       HDCDrawPosition(hdc, 1, NULL);\r
3945       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3946         flipView = !flipView; partnerUp = !partnerUp;\r
3947         HDCDrawPosition(hdc, 1, NULL);\r
3948         flipView = !flipView; partnerUp = !partnerUp;\r
3949       }\r
3950       oldFont =\r
3951         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3952       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3953                  ETO_CLIPPED|ETO_OPAQUE,\r
3954                  &messageRect, messageText, strlen(messageText), NULL);\r
3955       SelectObject(hdc, oldFont);\r
3956       DisplayBothClocks();\r
3957     }\r
3958     EndPaint(hwnd,&ps);\r
3959   }\r
3960 \r
3961   return;\r
3962 }\r
3963 \r
3964 \r
3965 /*\r
3966  * If the user selects on a border boundary, return -1; if off the board,\r
3967  *   return -2.  Otherwise map the event coordinate to the square.\r
3968  * The offset boardRect.left or boardRect.top must already have been\r
3969  *   subtracted from x.\r
3970  */\r
3971 int EventToSquare(x, limit)\r
3972      int x, limit;\r
3973 {\r
3974   if (x <= 0)\r
3975     return -2;\r
3976   if (x < lineGap)\r
3977     return -1;\r
3978   x -= lineGap;\r
3979   if ((x % (squareSize + lineGap)) >= squareSize)\r
3980     return -1;\r
3981   x /= (squareSize + lineGap);\r
3982     if (x >= limit)\r
3983     return -2;\r
3984   return x;\r
3985 }\r
3986 \r
3987 typedef struct {\r
3988   char piece;\r
3989   int command;\r
3990   char* name;\r
3991 } DropEnable;\r
3992 \r
3993 DropEnable dropEnables[] = {\r
3994   { 'P', DP_Pawn, N_("Pawn") },\r
3995   { 'N', DP_Knight, N_("Knight") },\r
3996   { 'B', DP_Bishop, N_("Bishop") },\r
3997   { 'R', DP_Rook, N_("Rook") },\r
3998   { 'Q', DP_Queen, N_("Queen") },\r
3999 };\r
4000 \r
4001 VOID\r
4002 SetupDropMenu(HMENU hmenu)\r
4003 {\r
4004   int i, count, enable;\r
4005   char *p;\r
4006   extern char white_holding[], black_holding[];\r
4007   char item[MSG_SIZ];\r
4008 \r
4009   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4010     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4011                dropEnables[i].piece);\r
4012     count = 0;\r
4013     while (p && *p++ == dropEnables[i].piece) count++;\r
4014     sprintf(item, "%s  %d", T_(dropEnables[i].name), count);\r
4015     enable = count > 0 || !appData.testLegality\r
4016       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4017                       && !appData.icsActive);\r
4018     ModifyMenu(hmenu, dropEnables[i].command,\r
4019                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4020                dropEnables[i].command, item);\r
4021   }\r
4022 }\r
4023 \r
4024 void DragPieceBegin(int x, int y)\r
4025 {\r
4026       dragInfo.lastpos.x = boardRect.left + x;\r
4027       dragInfo.lastpos.y = boardRect.top + y;\r
4028       dragInfo.from.x = fromX;\r
4029       dragInfo.from.y = fromY;\r
4030       dragInfo.start = dragInfo.from;\r
4031       SetCapture(hwndMain);\r
4032 }\r
4033 \r
4034 void DragPieceEnd(int x, int y)\r
4035 {\r
4036     ReleaseCapture();\r
4037     dragInfo.start.x = dragInfo.start.y = -1;\r
4038     dragInfo.from = dragInfo.start;\r
4039     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4040 }\r
4041 \r
4042 /* Event handler for mouse messages */\r
4043 VOID\r
4044 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4045 {\r
4046   int x, y, menuNr;\r
4047   POINT pt;\r
4048   static int recursive = 0;\r
4049   HMENU hmenu;\r
4050   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4051 \r
4052   if (recursive) {\r
4053     if (message == WM_MBUTTONUP) {\r
4054       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4055          to the middle button: we simulate pressing the left button too!\r
4056          */\r
4057       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4058       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4059     }\r
4060     return;\r
4061   }\r
4062   recursive++;\r
4063   \r
4064   pt.x = LOWORD(lParam);\r
4065   pt.y = HIWORD(lParam);\r
4066   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4067   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4068   if (!flipView && y >= 0) {\r
4069     y = BOARD_HEIGHT - 1 - y;\r
4070   }\r
4071   if (flipView && x >= 0) {\r
4072     x = BOARD_WIDTH - 1 - x;\r
4073   }\r
4074 \r
4075   switch (message) {\r
4076   case WM_LBUTTONDOWN:\r
4077       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4078         if (gameMode == EditPosition) {\r
4079           SetWhiteToPlayEvent();\r
4080         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4081           AdjustClock(flipClock, -1);\r
4082         } else if (gameMode == IcsPlayingBlack ||\r
4083                    gameMode == MachinePlaysWhite) {\r
4084           CallFlagEvent();\r
4085         }\r
4086       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4087         if (gameMode == EditPosition) {\r
4088           SetBlackToPlayEvent();\r
4089         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4090           AdjustClock(!flipClock, -1);\r
4091         } else if (gameMode == IcsPlayingWhite ||\r
4092                    gameMode == MachinePlaysBlack) {\r
4093           CallFlagEvent();\r
4094         }\r
4095       }\r
4096       dragInfo.start.x = dragInfo.start.y = -1;\r
4097       dragInfo.from = dragInfo.start;\r
4098     if(fromX == -1 && frozen) { // not sure where this is for\r
4099                 fromX = fromY = -1; \r
4100       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4101       break;\r
4102     }\r
4103       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4104       DrawPosition(TRUE, NULL);\r
4105     break;\r
4106 \r
4107   case WM_LBUTTONUP:\r
4108       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4109       DrawPosition(TRUE, NULL);\r
4110     break;\r
4111 \r
4112   case WM_MOUSEMOVE:\r
4113     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4114     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4115     if ((appData.animateDragging || appData.highlightDragging)\r
4116         && (wParam & MK_LBUTTON)\r
4117         && dragInfo.from.x >= 0) \r
4118     {\r
4119       BOOL full_repaint = FALSE;\r
4120 \r
4121       if (appData.animateDragging) {\r
4122         dragInfo.pos = pt;\r
4123       }\r
4124       if (appData.highlightDragging) {\r
4125         SetHighlights(fromX, fromY, x, y);\r
4126         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4127             full_repaint = TRUE;\r
4128         }\r
4129       }\r
4130       \r
4131       DrawPosition( full_repaint, NULL);\r
4132       \r
4133       dragInfo.lastpos = dragInfo.pos;\r
4134     }\r
4135     break;\r
4136 \r
4137   case WM_MOUSEWHEEL: // [DM]\r
4138     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4139        /* Mouse Wheel is being rolled forward\r
4140         * Play moves forward\r
4141         */\r
4142        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4143                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4144        /* Mouse Wheel is being rolled backward\r
4145         * Play moves backward\r
4146         */\r
4147        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4148                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4149     }\r
4150     break;\r
4151 \r
4152   case WM_MBUTTONUP:\r
4153   case WM_RBUTTONUP:\r
4154     ReleaseCapture();\r
4155     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4156     break;\r
4157  \r
4158   case WM_MBUTTONDOWN:\r
4159   case WM_RBUTTONDOWN:\r
4160     ErrorPopDown();\r
4161     ReleaseCapture();\r
4162     fromX = fromY = -1;\r
4163     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4164     dragInfo.start.x = dragInfo.start.y = -1;\r
4165     dragInfo.from = dragInfo.start;\r
4166     dragInfo.lastpos = dragInfo.pos;\r
4167     if (appData.highlightDragging) {\r
4168       ClearHighlights();\r
4169     }\r
4170     if(y == -2) {\r
4171       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4172       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4173           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4174       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4175           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4176       }\r
4177       break;\r
4178     }\r
4179     DrawPosition(TRUE, NULL);\r
4180 \r
4181     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4182     switch (menuNr) {\r
4183     case 0:\r
4184       if (message == WM_MBUTTONDOWN) {\r
4185         buttonCount = 3;  /* even if system didn't think so */\r
4186         if (wParam & MK_SHIFT) \r
4187           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4188         else\r
4189           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4190       } else { /* message == WM_RBUTTONDOWN */\r
4191         /* Just have one menu, on the right button.  Windows users don't\r
4192            think to try the middle one, and sometimes other software steals\r
4193            it, or it doesn't really exist. */\r
4194         if(gameInfo.variant != VariantShogi)\r
4195             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4196         else\r
4197             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4198       }\r
4199       break;\r
4200     case 2:\r
4201       SetCapture(hwndMain);
4202       break;\r
4203     case 1:\r
4204       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4205       SetupDropMenu(hmenu);\r
4206       MenuPopup(hwnd, pt, hmenu, -1);\r
4207     default:\r
4208       break;\r
4209     }\r
4210     break;\r
4211   }\r
4212 \r
4213   recursive--;\r
4214 }\r
4215 \r
4216 /* Preprocess messages for buttons in main window */\r
4217 LRESULT CALLBACK\r
4218 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4219 {\r
4220   int id = GetWindowLong(hwnd, GWL_ID);\r
4221   int i, dir;\r
4222 \r
4223   for (i=0; i<N_BUTTONS; i++) {\r
4224     if (buttonDesc[i].id == id) break;\r
4225   }\r
4226   if (i == N_BUTTONS) return 0;\r
4227   switch (message) {\r
4228   case WM_KEYDOWN:\r
4229     switch (wParam) {\r
4230     case VK_LEFT:\r
4231     case VK_RIGHT:\r
4232       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4233       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4234       return TRUE;\r
4235     }\r
4236     break;\r
4237   case WM_CHAR:\r
4238     switch (wParam) {\r
4239     case '\r':\r
4240       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4241       return TRUE;\r
4242     default:\r
4243       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4244         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4245         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4246         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4247         SetFocus(h);\r
4248         SendMessage(h, WM_CHAR, wParam, lParam);\r
4249         return TRUE;\r
4250       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4251         PopUpMoveDialog((char)wParam);\r
4252       }\r
4253       break;\r
4254     }\r
4255     break;\r
4256   }\r
4257   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4258 }\r
4259 \r
4260 /* Process messages for Promotion dialog box */\r
4261 LRESULT CALLBACK\r
4262 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4263 {\r
4264   char promoChar;\r
4265 \r
4266   switch (message) {\r
4267   case WM_INITDIALOG: /* message: initialize dialog box */\r
4268     /* Center the dialog over the application window */\r
4269     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4270     Translate(hDlg, DLG_PromotionKing);\r
4271     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4272       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4273        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4274                SW_SHOW : SW_HIDE);\r
4275     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4276     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4277        ((PieceToChar(WhiteAngel) >= 'A' &&\r
4278          PieceToChar(WhiteAngel) != '~') ||\r
4279         (PieceToChar(BlackAngel) >= 'A' &&\r
4280          PieceToChar(BlackAngel) != '~')   ) ?\r
4281                SW_SHOW : SW_HIDE);\r
4282     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4283        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
4284          PieceToChar(WhiteMarshall) != '~') ||\r
4285         (PieceToChar(BlackMarshall) >= 'A' &&\r
4286          PieceToChar(BlackMarshall) != '~')   ) ?\r
4287                SW_SHOW : SW_HIDE);\r
4288     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4289     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4290        gameInfo.variant != VariantShogi ?\r
4291                SW_SHOW : SW_HIDE);\r
4292     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4293        gameInfo.variant != VariantShogi ?\r
4294                SW_SHOW : SW_HIDE);\r
4295     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
4296        gameInfo.variant == VariantShogi ?\r
4297                SW_SHOW : SW_HIDE);\r
4298     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
4299        gameInfo.variant == VariantShogi ?\r
4300                SW_SHOW : SW_HIDE);\r
4301     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4302        gameInfo.variant == VariantSuper ?\r
4303                SW_SHOW : SW_HIDE);\r
4304     return TRUE;\r
4305 \r
4306   case WM_COMMAND: /* message: received a command */\r
4307     switch (LOWORD(wParam)) {\r
4308     case IDCANCEL:\r
4309       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4310       ClearHighlights();\r
4311       DrawPosition(FALSE, NULL);\r
4312       return TRUE;\r
4313     case PB_King:\r
4314       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4315       break;\r
4316     case PB_Queen:\r
4317       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
4318       break;\r
4319     case PB_Rook:\r
4320       promoChar = PieceToChar(BlackRook);\r
4321       break;\r
4322     case PB_Bishop:\r
4323       promoChar = PieceToChar(BlackBishop);\r
4324       break;\r
4325     case PB_Chancellor:\r
4326       promoChar = PieceToChar(BlackMarshall);\r
4327       break;\r
4328     case PB_Archbishop:\r
4329       promoChar = PieceToChar(BlackAngel);\r
4330       break;\r
4331     case PB_Knight:\r
4332       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
4333       break;\r
4334     default:\r
4335       return FALSE;\r
4336     }\r
4337     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4338     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
4339        only show the popup when we are already sure the move is valid or\r
4340        legal. We pass a faulty move type, but the kludge is that FinishMove\r
4341        will figure out it is a promotion from the promoChar. */\r
4342     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4343     fromX = fromY = -1;\r
4344     if (!appData.highlightLastMove) {\r
4345       ClearHighlights();\r
4346       DrawPosition(FALSE, NULL);\r
4347     }\r
4348     return TRUE;\r
4349   }\r
4350   return FALSE;\r
4351 }\r
4352 \r
4353 /* Pop up promotion dialog */\r
4354 VOID\r
4355 PromotionPopup(HWND hwnd)\r
4356 {\r
4357   FARPROC lpProc;\r
4358 \r
4359   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4360   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4361     hwnd, (DLGPROC)lpProc);\r
4362   FreeProcInstance(lpProc);\r
4363 }\r
4364 \r
4365 void\r
4366 PromotionPopUp()\r
4367 {\r
4368   DrawPosition(TRUE, NULL);\r
4369   PromotionPopup(hwndMain);\r
4370 }\r
4371 \r
4372 /* Toggle ShowThinking */\r
4373 VOID\r
4374 ToggleShowThinking()\r
4375 {\r
4376   appData.showThinking = !appData.showThinking;\r
4377   ShowThinkingEvent();\r
4378 }\r
4379 \r
4380 VOID\r
4381 LoadGameDialog(HWND hwnd, char* title)\r
4382 {\r
4383   UINT number = 0;\r
4384   FILE *f;\r
4385   char fileTitle[MSG_SIZ];\r
4386   f = OpenFileDialog(hwnd, "rb", "",\r
4387                      appData.oldSaveStyle ? "gam" : "pgn",\r
4388                      GAME_FILT,\r
4389                      title, &number, fileTitle, NULL);\r
4390   if (f != NULL) {\r
4391     cmailMsgLoaded = FALSE;\r
4392     if (number == 0) {\r
4393       int error = GameListBuild(f);\r
4394       if (error) {\r
4395         DisplayError(_("Cannot build game list"), error);\r
4396       } else if (!ListEmpty(&gameList) &&\r
4397                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4398         GameListPopUp(f, fileTitle);\r
4399         return;\r
4400       }\r
4401       GameListDestroy();\r
4402       number = 1;\r
4403     }\r
4404     LoadGame(f, number, fileTitle, FALSE);\r
4405   }\r
4406 }\r
4407 \r
4408 int get_term_width()\r
4409 {\r
4410     HDC hdc;\r
4411     TEXTMETRIC tm;\r
4412     RECT rc;\r
4413     HFONT hfont, hold_font;\r
4414     LOGFONT lf;\r
4415     HWND hText;\r
4416 \r
4417     if (hwndConsole)\r
4418         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4419     else\r
4420         return 79;\r
4421 \r
4422     // get the text metrics\r
4423     hdc = GetDC(hText);\r
4424     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4425     if (consoleCF.dwEffects & CFE_BOLD)\r
4426         lf.lfWeight = FW_BOLD;\r
4427     if (consoleCF.dwEffects & CFE_ITALIC)\r
4428         lf.lfItalic = TRUE;\r
4429     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4430         lf.lfStrikeOut = TRUE;\r
4431     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4432         lf.lfUnderline = TRUE;\r
4433     hfont = CreateFontIndirect(&lf);\r
4434     hold_font = SelectObject(hdc, hfont);\r
4435     GetTextMetrics(hdc, &tm);\r
4436     SelectObject(hdc, hold_font);\r
4437     DeleteObject(hfont);\r
4438     ReleaseDC(hText, hdc);\r
4439 \r
4440     // get the rectangle\r
4441     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4442 \r
4443     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4444 }\r
4445 \r
4446 void UpdateICSWidth(HWND hText)\r
4447 {\r
4448     LONG old_width, new_width;\r
4449 \r
4450     new_width = get_term_width(hText, FALSE);\r
4451     old_width = GetWindowLong(hText, GWL_USERDATA);\r
4452     if (new_width != old_width)\r
4453     {\r
4454         ics_update_width(new_width);\r
4455         SetWindowLong(hText, GWL_USERDATA, new_width);\r
4456     }\r
4457 }\r
4458 \r
4459 VOID\r
4460 ChangedConsoleFont()\r
4461 {\r
4462   CHARFORMAT cfmt;\r
4463   CHARRANGE tmpsel, sel;\r
4464   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4465   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4466   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4467   PARAFORMAT paraf;\r
4468 \r
4469   cfmt.cbSize = sizeof(CHARFORMAT);\r
4470   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4471   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
4472   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4473    * size.  This was undocumented in the version of MSVC++ that I had\r
4474    * when I wrote the code, but is apparently documented now.\r
4475    */\r
4476   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4477   cfmt.bCharSet = f->lf.lfCharSet;\r
4478   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4479   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4480   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4481   /* Why are the following seemingly needed too? */\r
4482   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4483   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4484   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4485   tmpsel.cpMin = 0;\r
4486   tmpsel.cpMax = -1; /*999999?*/\r
4487   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4488   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4489   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4490    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4491    */\r
4492   paraf.cbSize = sizeof(paraf);\r
4493   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4494   paraf.dxStartIndent = 0;\r
4495   paraf.dxOffset = WRAP_INDENT;\r
4496   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4497   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4498   UpdateICSWidth(hText);\r
4499 }\r
4500 \r
4501 /*---------------------------------------------------------------------------*\\r
4502  *\r
4503  * Window Proc for main window\r
4504  *\r
4505 \*---------------------------------------------------------------------------*/\r
4506 \r
4507 /* Process messages for main window, etc. */\r
4508 LRESULT CALLBACK\r
4509 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4510 {\r
4511   FARPROC lpProc;\r
4512   int wmId, wmEvent;\r
4513   char *defName;\r
4514   FILE *f;\r
4515   UINT number;\r
4516   char fileTitle[MSG_SIZ];\r
4517   char buf[MSG_SIZ];\r
4518   static SnapData sd;\r
4519 \r
4520   switch (message) {\r
4521 \r
4522   case WM_PAINT: /* message: repaint portion of window */\r
4523     PaintProc(hwnd);\r
4524     break;\r
4525 \r
4526   case WM_ERASEBKGND:\r
4527     if (IsIconic(hwnd)) {\r
4528       /* Cheat; change the message */\r
4529       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4530     } else {\r
4531       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4532     }\r
4533     break;\r
4534 \r
4535   case WM_LBUTTONDOWN:\r
4536   case WM_MBUTTONDOWN:\r
4537   case WM_RBUTTONDOWN:\r
4538   case WM_LBUTTONUP:\r
4539   case WM_MBUTTONUP:\r
4540   case WM_RBUTTONUP:\r
4541   case WM_MOUSEMOVE:\r
4542   case WM_MOUSEWHEEL:\r
4543     MouseEvent(hwnd, message, wParam, lParam);\r
4544     break;\r
4545 \r
4546   JAWS_KB_NAVIGATION\r
4547 \r
4548   case WM_CHAR:\r
4549     \r
4550     JAWS_ALT_INTERCEPT\r
4551 \r
4552     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4553         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4554         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4555         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4556         SetFocus(h);\r
4557         SendMessage(h, message, wParam, lParam);\r
4558     } else if(lParam != KF_REPEAT) {\r
4559         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4560                 PopUpMoveDialog((char)wParam);\r
4561         } else if((char)wParam == 003) CopyGameToClipboard();\r
4562          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4563     }\r
4564 \r
4565     break;\r
4566 \r
4567   case WM_PALETTECHANGED:\r
4568     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4569       int nnew;\r
4570       HDC hdc = GetDC(hwndMain);\r
4571       SelectPalette(hdc, hPal, TRUE);\r
4572       nnew = RealizePalette(hdc);\r
4573       if (nnew > 0) {\r
4574         paletteChanged = TRUE;\r
4575         InvalidateRect(hwnd, &boardRect, FALSE);\r
4576       }\r
4577       ReleaseDC(hwnd, hdc);\r
4578     }\r
4579     break;\r
4580 \r
4581   case WM_QUERYNEWPALETTE:\r
4582     if (!appData.monoMode /*&& paletteChanged*/) {\r
4583       int nnew;\r
4584       HDC hdc = GetDC(hwndMain);\r
4585       paletteChanged = FALSE;\r
4586       SelectPalette(hdc, hPal, FALSE);\r
4587       nnew = RealizePalette(hdc);\r
4588       if (nnew > 0) {\r
4589         InvalidateRect(hwnd, &boardRect, FALSE);\r
4590       }\r
4591       ReleaseDC(hwnd, hdc);\r
4592       return TRUE;\r
4593     }\r
4594     return FALSE;\r
4595 \r
4596   case WM_COMMAND: /* message: command from application menu */\r
4597     wmId    = LOWORD(wParam);\r
4598     wmEvent = HIWORD(wParam);\r
4599 \r
4600     switch (wmId) {\r
4601     case IDM_NewGame:\r
4602       ResetGameEvent();\r
4603       SAY("new game enter a move to play against the computer with white");\r
4604       break;\r
4605 \r
4606     case IDM_NewGameFRC:\r
4607       if( NewGameFRC() == 0 ) {\r
4608         ResetGameEvent();\r
4609       }\r
4610       break;\r
4611 \r
4612     case IDM_NewVariant:\r
4613       NewVariantPopup(hwnd);\r
4614       break;\r
4615 \r
4616     case IDM_LoadGame:\r
4617       LoadGameDialog(hwnd, _("Load Game from File"));\r
4618       break;\r
4619 \r
4620     case IDM_LoadNextGame:\r
4621       ReloadGame(1);\r
4622       break;\r
4623 \r
4624     case IDM_LoadPrevGame:\r
4625       ReloadGame(-1);\r
4626       break;\r
4627 \r
4628     case IDM_ReloadGame:\r
4629       ReloadGame(0);\r
4630       break;\r
4631 \r
4632     case IDM_LoadPosition:\r
4633       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4634         Reset(FALSE, TRUE);\r
4635       }\r
4636       number = 1;\r
4637       f = OpenFileDialog(hwnd, "rb", "",\r
4638                          appData.oldSaveStyle ? "pos" : "fen",\r
4639                          POSITION_FILT,\r
4640                          _("Load Position from File"), &number, fileTitle, NULL);\r
4641       if (f != NULL) {\r
4642         LoadPosition(f, number, fileTitle);\r
4643       }\r
4644       break;\r
4645 \r
4646     case IDM_LoadNextPosition:\r
4647       ReloadPosition(1);\r
4648       break;\r
4649 \r
4650     case IDM_LoadPrevPosition:\r
4651       ReloadPosition(-1);\r
4652       break;\r
4653 \r
4654     case IDM_ReloadPosition:\r
4655       ReloadPosition(0);\r
4656       break;\r
4657 \r
4658     case IDM_SaveGame:\r
4659       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4660       f = OpenFileDialog(hwnd, "a", defName,\r
4661                          appData.oldSaveStyle ? "gam" : "pgn",\r
4662                          GAME_FILT,\r
4663                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4664       if (f != NULL) {\r
4665         SaveGame(f, 0, "");\r
4666       }\r
4667       break;\r
4668 \r
4669     case IDM_SavePosition:\r
4670       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4671       f = OpenFileDialog(hwnd, "a", defName,\r
4672                          appData.oldSaveStyle ? "pos" : "fen",\r
4673                          POSITION_FILT,\r
4674                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4675       if (f != NULL) {\r
4676         SavePosition(f, 0, "");\r
4677       }\r
4678       break;\r
4679 \r
4680     case IDM_SaveDiagram:\r
4681       defName = "diagram";\r
4682       f = OpenFileDialog(hwnd, "wb", defName,\r
4683                          "bmp",\r
4684                          DIAGRAM_FILT,\r
4685                          "Save Diagram to File", NULL, fileTitle, NULL);\r
4686       if (f != NULL) {\r
4687         SaveDiagram(f);\r
4688       }\r
4689       break;\r
4690 \r
4691     case IDM_CopyGame:\r
4692       CopyGameToClipboard();\r
4693       break;\r
4694 \r
4695     case IDM_PasteGame:\r
4696       PasteGameFromClipboard();\r
4697       break;\r
4698 \r
4699     case IDM_CopyGameListToClipboard:\r
4700       CopyGameListToClipboard();\r
4701       break;\r
4702 \r
4703     /* [AS] Autodetect FEN or PGN data */\r
4704     case IDM_PasteAny:\r
4705       PasteGameOrFENFromClipboard();\r
4706       break;\r
4707 \r
4708     /* [AS] Move history */\r
4709     case IDM_ShowMoveHistory:\r
4710         if( MoveHistoryIsUp() ) {\r
4711             MoveHistoryPopDown();\r
4712         }\r
4713         else {\r
4714             MoveHistoryPopUp();\r
4715         }\r
4716         break;\r
4717 \r
4718     /* [AS] Eval graph */\r
4719     case IDM_ShowEvalGraph:\r
4720         if( EvalGraphIsUp() ) {\r
4721             EvalGraphPopDown();\r
4722         }\r
4723         else {\r
4724             EvalGraphPopUp();\r
4725             SetFocus(hwndMain);\r
4726         }\r
4727         break;\r
4728 \r
4729     /* [AS] Engine output */\r
4730     case IDM_ShowEngineOutput:\r
4731         if( EngineOutputIsUp() ) {\r
4732             EngineOutputPopDown();\r
4733         }\r
4734         else {\r
4735             EngineOutputPopUp();\r
4736         }\r
4737         break;\r
4738 \r
4739     /* [AS] User adjudication */\r
4740     case IDM_UserAdjudication_White:\r
4741         UserAdjudicationEvent( +1 );\r
4742         break;\r
4743 \r
4744     case IDM_UserAdjudication_Black:\r
4745         UserAdjudicationEvent( -1 );\r
4746         break;\r
4747 \r
4748     case IDM_UserAdjudication_Draw:\r
4749         UserAdjudicationEvent( 0 );\r
4750         break;\r
4751 \r
4752     /* [AS] Game list options dialog */\r
4753     case IDM_GameListOptions:\r
4754       GameListOptions();\r
4755       break;\r
4756 \r
4757     case IDM_NewChat:\r
4758       ChatPopUp(NULL);\r
4759       break;\r
4760 \r
4761     case IDM_CopyPosition:\r
4762       CopyFENToClipboard();\r
4763       break;\r
4764 \r
4765     case IDM_PastePosition:\r
4766       PasteFENFromClipboard();\r
4767       break;\r
4768 \r
4769     case IDM_MailMove:\r
4770       MailMoveEvent();\r
4771       break;\r
4772 \r
4773     case IDM_ReloadCMailMsg:\r
4774       Reset(TRUE, TRUE);\r
4775       ReloadCmailMsgEvent(FALSE);\r
4776       break;\r
4777 \r
4778     case IDM_Minimize:\r
4779       ShowWindow(hwnd, SW_MINIMIZE);\r
4780       break;\r
4781 \r
4782     case IDM_Exit:\r
4783       ExitEvent(0);\r
4784       break;\r
4785 \r
4786     case IDM_MachineWhite:\r
4787       MachineWhiteEvent();\r
4788       /*\r
4789        * refresh the tags dialog only if it's visible\r
4790        */\r
4791       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4792           char *tags;\r
4793           tags = PGNTags(&gameInfo);\r
4794           TagsPopUp(tags, CmailMsg());\r
4795           free(tags);\r
4796       }\r
4797       SAY("computer starts playing white");\r
4798       break;\r
4799 \r
4800     case IDM_MachineBlack:\r
4801       MachineBlackEvent();\r
4802       /*\r
4803        * refresh the tags dialog only if it's visible\r
4804        */\r
4805       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4806           char *tags;\r
4807           tags = PGNTags(&gameInfo);\r
4808           TagsPopUp(tags, CmailMsg());\r
4809           free(tags);\r
4810       }\r
4811       SAY("computer starts playing black");\r
4812       break;\r
4813 \r
4814     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4815       if(gameMode != BeginningOfGame) break; // allow menu item to remain enabled for better mode highligting\r
4816       matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)\r
4817       appData.matchGames = appData.defaultMatchGames;\r
4818       matchGame = 1;\r
4819 \r
4820     case IDM_TwoMachines:\r
4821       TwoMachinesEvent();\r
4822       /*\r
4823        * refresh the tags dialog only if it's visible\r
4824        */\r
4825       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4826           char *tags;\r
4827           tags = PGNTags(&gameInfo);\r
4828           TagsPopUp(tags, CmailMsg());\r
4829           free(tags);\r
4830       }\r
4831       SAY("computer starts playing both sides");\r
4832       break;\r
4833 \r
4834     case IDM_AnalysisMode:\r
4835       if (!first.analysisSupport) {\r
4836         sprintf(buf, _("%s does not support analysis"), first.tidy);\r
4837         DisplayError(buf, 0);\r
4838       } else {\r
4839         SAY("analyzing current position");\r
4840         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4841         if (appData.icsActive) {\r
4842                if (gameMode != IcsObserving) {\r
4843                        sprintf(buf, "You are not observing a game");\r
4844                        DisplayError(buf, 0);\r
4845                        /* secure check */\r
4846                        if (appData.icsEngineAnalyze) {\r
4847                                if (appData.debugMode) \r
4848                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4849                                ExitAnalyzeMode();\r
4850                                ModeHighlight();\r
4851                                break;\r
4852                        }\r
4853                        break;\r
4854                } else {\r
4855                        /* if enable, user want disable icsEngineAnalyze */\r
4856                        if (appData.icsEngineAnalyze) {\r
4857                                ExitAnalyzeMode();\r
4858                                ModeHighlight();\r
4859                                break;\r
4860                        }\r
4861                        appData.icsEngineAnalyze = TRUE;\r
4862                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4863                }\r
4864         } \r
4865         if (!appData.showThinking) ToggleShowThinking();\r
4866         AnalyzeModeEvent();\r
4867       }\r
4868       break;\r
4869 \r
4870     case IDM_AnalyzeFile:\r
4871       if (!first.analysisSupport) {\r
4872         char buf[MSG_SIZ];\r
4873         sprintf(buf, _("%s does not support analysis"), first.tidy);\r
4874         DisplayError(buf, 0);\r
4875       } else {\r
4876         if (!appData.showThinking) ToggleShowThinking();\r
4877         AnalyzeFileEvent();\r
4878         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4879         AnalysisPeriodicEvent(1);\r
4880       }\r
4881       break;\r
4882 \r
4883     case IDM_IcsClient:\r
4884       IcsClientEvent();\r
4885       break;\r
4886 \r
4887     case IDM_EditGame:\r
4888       EditGameEvent();\r
4889       SAY("edit game");\r
4890       break;\r
4891 \r
4892     case IDM_EditPosition:\r
4893       EditPositionEvent();\r
4894       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4895       break;\r
4896 \r
4897     case IDM_Training:\r
4898       TrainingEvent();\r
4899       break;\r
4900 \r
4901     case IDM_ShowGameList:\r
4902       ShowGameListProc();\r
4903       break;\r
4904 \r
4905     case IDM_EditTags:\r
4906       EditTagsProc();\r
4907       break;\r
4908 \r
4909     case IDM_EditComment:\r
4910       if (commentUp && editComment) {\r
4911         CommentPopDown();\r
4912       } else {\r
4913         EditCommentEvent();\r
4914       }\r
4915       break;\r
4916 \r
4917     case IDM_Pause:\r
4918       PauseEvent();\r
4919       break;\r
4920 \r
4921     case IDM_Accept:\r
4922       AcceptEvent();\r
4923       break;\r
4924 \r
4925     case IDM_Decline:\r
4926       DeclineEvent();\r
4927       break;\r
4928 \r
4929     case IDM_Rematch:\r
4930       RematchEvent();\r
4931       break;\r
4932 \r
4933     case IDM_CallFlag:\r
4934       CallFlagEvent();\r
4935       break;\r
4936 \r
4937     case IDM_Draw:\r
4938       DrawEvent();\r
4939       break;\r
4940 \r
4941     case IDM_Adjourn:\r
4942       AdjournEvent();\r
4943       break;\r
4944 \r
4945     case IDM_Abort:\r
4946       AbortEvent();\r
4947       break;\r
4948 \r
4949     case IDM_Resign:\r
4950       ResignEvent();\r
4951       break;\r
4952 \r
4953     case IDM_StopObserving:\r
4954       StopObservingEvent();\r
4955       break;\r
4956 \r
4957     case IDM_StopExamining:\r
4958       StopExaminingEvent();\r
4959       break;\r
4960 \r
4961     case IDM_Upload:\r
4962       UploadGameEvent();\r
4963       break;\r
4964 \r
4965     case IDM_TypeInMove:\r
4966       PopUpMoveDialog('\000');\r
4967       break;\r
4968 \r
4969     case IDM_TypeInName:\r
4970       PopUpNameDialog('\000');\r
4971       break;\r
4972 \r
4973     case IDM_Backward:\r
4974       BackwardEvent();\r
4975       SetFocus(hwndMain);\r
4976       break;\r
4977 \r
4978     JAWS_MENU_ITEMS\r
4979 \r
4980     case IDM_Forward:\r
4981       ForwardEvent();\r
4982       SetFocus(hwndMain);\r
4983       break;\r
4984 \r
4985     case IDM_ToStart:\r
4986       ToStartEvent();\r
4987       SetFocus(hwndMain);\r
4988       break;\r
4989 \r
4990     case IDM_ToEnd:\r
4991       ToEndEvent();\r
4992       SetFocus(hwndMain);\r
4993       break;\r
4994 \r
4995     case IDM_Revert:\r
4996       RevertEvent(FALSE);\r
4997       break;\r
4998 \r
4999     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5000       RevertEvent(TRUE);\r
5001       break;\r
5002 \r
5003     case IDM_TruncateGame:\r
5004       TruncateGameEvent();\r
5005       break;\r
5006 \r
5007     case IDM_MoveNow:\r
5008       MoveNowEvent();\r
5009       break;\r
5010 \r
5011     case IDM_RetractMove:\r
5012       RetractMoveEvent();\r
5013       break;\r
5014 \r
5015     case IDM_FlipView:\r
5016       flipView = !flipView;\r
5017       DrawPosition(FALSE, NULL);\r
5018       break;\r
5019 \r
5020     case IDM_FlipClock:\r
5021       flipClock = !flipClock;\r
5022       DisplayBothClocks();\r
5023       DrawPosition(FALSE, NULL);\r
5024       break;\r
5025 \r
5026     case IDM_MuteSounds:\r
5027       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5028       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5029                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5030       break;\r
5031 \r
5032     case IDM_GeneralOptions:\r
5033       GeneralOptionsPopup(hwnd);\r
5034       DrawPosition(TRUE, NULL);\r
5035       break;\r
5036 \r
5037     case IDM_BoardOptions:\r
5038       BoardOptionsPopup(hwnd);\r
5039       break;\r
5040 \r
5041     case IDM_EnginePlayOptions:\r
5042       EnginePlayOptionsPopup(hwnd);\r
5043       break;\r
5044 \r
5045     case IDM_Engine1Options:\r
5046       EngineOptionsPopup(hwnd, &first);\r
5047       break;\r
5048 \r
5049     case IDM_Engine2Options:\r
5050       EngineOptionsPopup(hwnd, &second);\r
5051       break;\r
5052 \r
5053     case IDM_OptionsUCI:\r
5054       UciOptionsPopup(hwnd);\r
5055       break;\r
5056 \r
5057     case IDM_IcsOptions:\r
5058       IcsOptionsPopup(hwnd);\r
5059       break;\r
5060 \r
5061     case IDM_Fonts:\r
5062       FontsOptionsPopup(hwnd);\r
5063       break;\r
5064 \r
5065     case IDM_Sounds:\r
5066       SoundOptionsPopup(hwnd);\r
5067       break;\r
5068 \r
5069     case IDM_CommPort:\r
5070       CommPortOptionsPopup(hwnd);\r
5071       break;\r
5072 \r
5073     case IDM_LoadOptions:\r
5074       LoadOptionsPopup(hwnd);\r
5075       break;\r
5076 \r
5077     case IDM_SaveOptions:\r
5078       SaveOptionsPopup(hwnd);\r
5079       break;\r
5080 \r
5081     case IDM_TimeControl:\r
5082       TimeControlOptionsPopup(hwnd);\r
5083       break;\r
5084 \r
5085     case IDM_SaveSettings:\r
5086       SaveSettings(settingsFileName);\r
5087       break;\r
5088 \r
5089     case IDM_SaveSettingsOnExit:\r
5090       saveSettingsOnExit = !saveSettingsOnExit;\r
5091       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5092                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5093                                          MF_CHECKED : MF_UNCHECKED));\r
5094       break;\r
5095 \r
5096     case IDM_Hint:\r
5097       HintEvent();\r
5098       break;\r
5099 \r
5100     case IDM_Book:\r
5101       BookEvent();\r
5102       break;\r
5103 \r
5104     case IDM_AboutGame:\r
5105       AboutGameEvent();\r
5106       break;\r
5107 \r
5108     case IDM_Debug:\r
5109       appData.debugMode = !appData.debugMode;\r
5110       if (appData.debugMode) {\r
5111         char dir[MSG_SIZ];\r
5112         GetCurrentDirectory(MSG_SIZ, dir);\r
5113         SetCurrentDirectory(installDir);\r
5114         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5115         SetCurrentDirectory(dir);\r
5116         setbuf(debugFP, NULL);\r
5117       } else {\r
5118         fclose(debugFP);\r
5119         debugFP = NULL;\r
5120       }\r
5121       break;\r
5122 \r
5123     case IDM_HELPCONTENTS:\r
5124       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5125           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5126           MessageBox (GetFocus(),\r
5127                     _("Unable to activate help"),\r
5128                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5129       }\r
5130       break;\r
5131 \r
5132     case IDM_HELPSEARCH:\r
5133         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5134             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5135         MessageBox (GetFocus(),\r
5136                     _("Unable to activate help"),\r
5137                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5138       }\r
5139       break;\r
5140 \r
5141     case IDM_HELPHELP:\r
5142       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 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_ABOUT:\r
5150       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5151       DialogBox(hInst, \r
5152         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5153         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5154       FreeProcInstance(lpProc);\r
5155       break;\r
5156 \r
5157     case IDM_DirectCommand1:\r
5158       AskQuestionEvent(_("Direct Command"),\r
5159                        _("Send to chess program:"), "", "1");\r
5160       break;\r
5161     case IDM_DirectCommand2:\r
5162       AskQuestionEvent(_("Direct Command"),\r
5163                        _("Send to second chess program:"), "", "2");\r
5164       break;\r
5165 \r
5166     case EP_WhitePawn:\r
5167       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5168       fromX = fromY = -1;\r
5169       break;\r
5170 \r
5171     case EP_WhiteKnight:\r
5172       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5173       fromX = fromY = -1;\r
5174       break;\r
5175 \r
5176     case EP_WhiteBishop:\r
5177       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5178       fromX = fromY = -1;\r
5179       break;\r
5180 \r
5181     case EP_WhiteRook:\r
5182       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5183       fromX = fromY = -1;\r
5184       break;\r
5185 \r
5186     case EP_WhiteQueen:\r
5187       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5188       fromX = fromY = -1;\r
5189       break;\r
5190 \r
5191     case EP_WhiteFerz:\r
5192       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5193       fromX = fromY = -1;\r
5194       break;\r
5195 \r
5196     case EP_WhiteWazir:\r
5197       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5198       fromX = fromY = -1;\r
5199       break;\r
5200 \r
5201     case EP_WhiteAlfil:\r
5202       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5203       fromX = fromY = -1;\r
5204       break;\r
5205 \r
5206     case EP_WhiteCannon:\r
5207       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5208       fromX = fromY = -1;\r
5209       break;\r
5210 \r
5211     case EP_WhiteCardinal:\r
5212       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5213       fromX = fromY = -1;\r
5214       break;\r
5215 \r
5216     case EP_WhiteMarshall:\r
5217       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5218       fromX = fromY = -1;\r
5219       break;\r
5220 \r
5221     case EP_WhiteKing:\r
5222       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5223       fromX = fromY = -1;\r
5224       break;\r
5225 \r
5226     case EP_BlackPawn:\r
5227       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5228       fromX = fromY = -1;\r
5229       break;\r
5230 \r
5231     case EP_BlackKnight:\r
5232       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5233       fromX = fromY = -1;\r
5234       break;\r
5235 \r
5236     case EP_BlackBishop:\r
5237       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5238       fromX = fromY = -1;\r
5239       break;\r
5240 \r
5241     case EP_BlackRook:\r
5242       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5243       fromX = fromY = -1;\r
5244       break;\r
5245 \r
5246     case EP_BlackQueen:\r
5247       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5248       fromX = fromY = -1;\r
5249       break;\r
5250 \r
5251     case EP_BlackFerz:\r
5252       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5253       fromX = fromY = -1;\r
5254       break;\r
5255 \r
5256     case EP_BlackWazir:\r
5257       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5258       fromX = fromY = -1;\r
5259       break;\r
5260 \r
5261     case EP_BlackAlfil:\r
5262       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5263       fromX = fromY = -1;\r
5264       break;\r
5265 \r
5266     case EP_BlackCannon:\r
5267       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5268       fromX = fromY = -1;\r
5269       break;\r
5270 \r
5271     case EP_BlackCardinal:\r
5272       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5273       fromX = fromY = -1;\r
5274       break;\r
5275 \r
5276     case EP_BlackMarshall:\r
5277       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5278       fromX = fromY = -1;\r
5279       break;\r
5280 \r
5281     case EP_BlackKing:\r
5282       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5283       fromX = fromY = -1;\r
5284       break;\r
5285 \r
5286     case EP_EmptySquare:\r
5287       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5288       fromX = fromY = -1;\r
5289       break;\r
5290 \r
5291     case EP_ClearBoard:\r
5292       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5293       fromX = fromY = -1;\r
5294       break;\r
5295 \r
5296     case EP_White:\r
5297       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5298       fromX = fromY = -1;\r
5299       break;\r
5300 \r
5301     case EP_Black:\r
5302       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5303       fromX = fromY = -1;\r
5304       break;\r
5305 \r
5306     case EP_Promote:\r
5307       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5308       fromX = fromY = -1;\r
5309       break;\r
5310 \r
5311     case EP_Demote:\r
5312       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5313       fromX = fromY = -1;\r
5314       break;\r
5315 \r
5316     case DP_Pawn:\r
5317       DropMenuEvent(WhitePawn, fromX, fromY);\r
5318       fromX = fromY = -1;\r
5319       break;\r
5320 \r
5321     case DP_Knight:\r
5322       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5323       fromX = fromY = -1;\r
5324       break;\r
5325 \r
5326     case DP_Bishop:\r
5327       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5328       fromX = fromY = -1;\r
5329       break;\r
5330 \r
5331     case DP_Rook:\r
5332       DropMenuEvent(WhiteRook, fromX, fromY);\r
5333       fromX = fromY = -1;\r
5334       break;\r
5335 \r
5336     case DP_Queen:\r
5337       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5338       fromX = fromY = -1;\r
5339       break;\r
5340 \r
5341     case IDM_English:\r
5342       barbaric = 0;\r
5343       TranslateMenus(0);\r
5344       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5345       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5346       lastChecked = wmId;\r
5347       break;\r
5348 \r
5349     default:\r
5350       if(wmId > IDM_English && wmId < IDM_English+5) {\r
5351           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5352           TranslateMenus(0);\r
5353           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5354           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5355           lastChecked = wmId;\r
5356           break;\r
5357       }\r
5358       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5359     }\r
5360     break;\r
5361 \r
5362   case WM_TIMER:\r
5363     switch (wParam) {\r
5364     case CLOCK_TIMER_ID:\r
5365       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5366       clockTimerEvent = 0;\r
5367       DecrementClocks(); /* call into back end */\r
5368       break;\r
5369     case LOAD_GAME_TIMER_ID:\r
5370       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5371       loadGameTimerEvent = 0;\r
5372       AutoPlayGameLoop(); /* call into back end */\r
5373       break;\r
5374     case ANALYSIS_TIMER_ID:\r
5375       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5376                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5377         AnalysisPeriodicEvent(0);\r
5378       } else {\r
5379         KillTimer(hwnd, analysisTimerEvent);\r
5380         analysisTimerEvent = 0;\r
5381       }\r
5382       break;\r
5383     case DELAYED_TIMER_ID:\r
5384       KillTimer(hwnd, delayedTimerEvent);\r
5385       delayedTimerEvent = 0;\r
5386       delayedTimerCallback();\r
5387       break;\r
5388     }\r
5389     break;\r
5390 \r
5391   case WM_USER_Input:\r
5392     InputEvent(hwnd, message, wParam, lParam);\r
5393     break;\r
5394 \r
5395   /* [AS] Also move "attached" child windows */\r
5396   case WM_WINDOWPOSCHANGING:\r
5397 \r
5398     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5399         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5400 \r
5401         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5402             /* Window is moving */\r
5403             RECT rcMain;\r
5404 \r
5405 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5406             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5407             rcMain.right  = wpMain.x + wpMain.width;\r
5408             rcMain.top    = wpMain.y;\r
5409             rcMain.bottom = wpMain.y + wpMain.height;\r
5410             \r
5411             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5412             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5413             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5414             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5415             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5416             wpMain.x = lpwp->x;\r
5417             wpMain.y = lpwp->y;\r
5418         }\r
5419     }\r
5420     break;\r
5421 \r
5422   /* [AS] Snapping */\r
5423   case WM_ENTERSIZEMOVE:\r
5424     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5425     if (hwnd == hwndMain) {\r
5426       doingSizing = TRUE;\r
5427       lastSizing = 0;\r
5428     }\r
5429     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5430     break;\r
5431 \r
5432   case WM_SIZING:\r
5433     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5434     if (hwnd == hwndMain) {\r
5435       lastSizing = wParam;\r
5436     }\r
5437     break;\r
5438 \r
5439   case WM_MOVING:\r
5440     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5441       return OnMoving( &sd, hwnd, wParam, lParam );\r
5442 \r
5443   case WM_EXITSIZEMOVE:\r
5444     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5445     if (hwnd == hwndMain) {\r
5446       RECT client;\r
5447       doingSizing = FALSE;\r
5448       InvalidateRect(hwnd, &boardRect, FALSE);\r
5449       GetClientRect(hwnd, &client);\r
5450       ResizeBoard(client.right, client.bottom, lastSizing);\r
5451       lastSizing = 0;\r
5452       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5453     }\r
5454     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5455     break;\r
5456 \r
5457   case WM_DESTROY: /* message: window being destroyed */\r
5458     PostQuitMessage(0);\r
5459     break;\r
5460 \r
5461   case WM_CLOSE:\r
5462     if (hwnd == hwndMain) {\r
5463       ExitEvent(0);\r
5464     }\r
5465     break;\r
5466 \r
5467   default:      /* Passes it on if unprocessed */\r
5468     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5469   }\r
5470   return 0;\r
5471 }\r
5472 \r
5473 /*---------------------------------------------------------------------------*\\r
5474  *\r
5475  * Misc utility routines\r
5476  *\r
5477 \*---------------------------------------------------------------------------*/\r
5478 \r
5479 /*\r
5480  * Decent random number generator, at least not as bad as Windows\r
5481  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5482  */\r
5483 unsigned int randstate;\r
5484 \r
5485 int\r
5486 myrandom(void)\r
5487 {\r
5488   randstate = randstate * 1664525 + 1013904223;\r
5489   return (int) randstate & 0x7fffffff;\r
5490 }\r
5491 \r
5492 void\r
5493 mysrandom(unsigned int seed)\r
5494 {\r
5495   randstate = seed;\r
5496 }\r
5497 \r
5498 \r
5499 /* \r
5500  * returns TRUE if user selects a different color, FALSE otherwise \r
5501  */\r
5502 \r
5503 BOOL\r
5504 ChangeColor(HWND hwnd, COLORREF *which)\r
5505 {\r
5506   static BOOL firstTime = TRUE;\r
5507   static DWORD customColors[16];\r
5508   CHOOSECOLOR cc;\r
5509   COLORREF newcolor;\r
5510   int i;\r
5511   ColorClass ccl;\r
5512 \r
5513   if (firstTime) {\r
5514     /* Make initial colors in use available as custom colors */\r
5515     /* Should we put the compiled-in defaults here instead? */\r
5516     i = 0;\r
5517     customColors[i++] = lightSquareColor & 0xffffff;\r
5518     customColors[i++] = darkSquareColor & 0xffffff;\r
5519     customColors[i++] = whitePieceColor & 0xffffff;\r
5520     customColors[i++] = blackPieceColor & 0xffffff;\r
5521     customColors[i++] = highlightSquareColor & 0xffffff;\r
5522     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5523 \r
5524     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5525       customColors[i++] = textAttribs[ccl].color;\r
5526     }\r
5527     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5528     firstTime = FALSE;\r
5529   }\r
5530 \r
5531   cc.lStructSize = sizeof(cc);\r
5532   cc.hwndOwner = hwnd;\r
5533   cc.hInstance = NULL;\r
5534   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5535   cc.lpCustColors = (LPDWORD) customColors;\r
5536   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5537 \r
5538   if (!ChooseColor(&cc)) return FALSE;\r
5539 \r
5540   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5541   if (newcolor == *which) return FALSE;\r
5542   *which = newcolor;\r
5543   return TRUE;\r
5544 \r
5545   /*\r
5546   InitDrawingColors();\r
5547   InvalidateRect(hwnd, &boardRect, FALSE);\r
5548   */\r
5549 }\r
5550 \r
5551 BOOLEAN\r
5552 MyLoadSound(MySound *ms)\r
5553 {\r
5554   BOOL ok = FALSE;\r
5555   struct stat st;\r
5556   FILE *f;\r
5557 \r
5558   if (ms->data) free(ms->data);\r
5559   ms->data = NULL;\r
5560 \r
5561   switch (ms->name[0]) {\r
5562   case NULLCHAR:\r
5563     /* Silence */\r
5564     ok = TRUE;\r
5565     break;\r
5566   case '$':\r
5567     /* System sound from Control Panel.  Don't preload here. */\r
5568     ok = TRUE;\r
5569     break;\r
5570   case '!':\r
5571     if (ms->name[1] == NULLCHAR) {\r
5572       /* "!" alone = silence */\r
5573       ok = TRUE;\r
5574     } else {\r
5575       /* Builtin wave resource.  Error if not found. */\r
5576       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5577       if (h == NULL) break;\r
5578       ms->data = (void *)LoadResource(hInst, h);\r
5579       if (h == NULL) break;\r
5580       ok = TRUE;\r
5581     }\r
5582     break;\r
5583   default:\r
5584     /* .wav file.  Error if not found. */\r
5585     f = fopen(ms->name, "rb");\r
5586     if (f == NULL) break;\r
5587     if (fstat(fileno(f), &st) < 0) break;\r
5588     ms->data = malloc(st.st_size);\r
5589     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5590     fclose(f);\r
5591     ok = TRUE;\r
5592     break;\r
5593   }\r
5594   if (!ok) {\r
5595     char buf[MSG_SIZ];\r
5596     sprintf(buf, _("Error loading sound %s"), ms->name);\r
5597     DisplayError(buf, GetLastError());\r
5598   }\r
5599   return ok;\r
5600 }\r
5601 \r
5602 BOOLEAN\r
5603 MyPlaySound(MySound *ms)\r
5604 {\r
5605   BOOLEAN ok = FALSE;\r
5606 \r
5607   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5608   switch (ms->name[0]) {\r
5609   case NULLCHAR:\r
5610         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5611     /* Silence */\r
5612     ok = TRUE;\r
5613     break;\r
5614   case '$':\r
5615     /* System sound from Control Panel (deprecated feature).\r
5616        "$" alone or an unset sound name gets default beep (still in use). */\r
5617     if (ms->name[1]) {\r
5618       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5619     }\r
5620     if (!ok) ok = MessageBeep(MB_OK);\r
5621     break; \r
5622   case '!':\r
5623     /* Builtin wave resource, or "!" alone for silence */\r
5624     if (ms->name[1]) {\r
5625       if (ms->data == NULL) return FALSE;\r
5626       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5627     } else {\r
5628       ok = TRUE;\r
5629     }\r
5630     break;\r
5631   default:\r
5632     /* .wav file.  Error if not found. */\r
5633     if (ms->data == NULL) return FALSE;\r
5634     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5635     break;\r
5636   }\r
5637   /* Don't print an error: this can happen innocently if the sound driver\r
5638      is busy; for instance, if another instance of WinBoard is playing\r
5639      a sound at about the same time. */\r
5640   return ok;\r
5641 }\r
5642 \r
5643 \r
5644 LRESULT CALLBACK\r
5645 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5646 {\r
5647   BOOL ok;\r
5648   OPENFILENAME *ofn;\r
5649   static UINT *number; /* gross that this is static */\r
5650 \r
5651   switch (message) {\r
5652   case WM_INITDIALOG: /* message: initialize dialog box */\r
5653     /* Center the dialog over the application window */\r
5654     ofn = (OPENFILENAME *) lParam;\r
5655     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5656       number = (UINT *) ofn->lCustData;\r
5657       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5658     } else {\r
5659       number = NULL;\r
5660     }\r
5661     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5662     Translate(hDlg, 1536);\r
5663     return FALSE;  /* Allow for further processing */\r
5664 \r
5665   case WM_COMMAND:\r
5666     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5667       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5668     }\r
5669     return FALSE;  /* Allow for further processing */\r
5670   }\r
5671   return FALSE;\r
5672 }\r
5673 \r
5674 UINT APIENTRY\r
5675 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5676 {\r
5677   static UINT *number;\r
5678   OPENFILENAME *ofname;\r
5679   OFNOTIFY *ofnot;\r
5680   switch (uiMsg) {\r
5681   case WM_INITDIALOG:\r
5682     Translate(hdlg, DLG_IndexNumber);\r
5683     ofname = (OPENFILENAME *)lParam;\r
5684     number = (UINT *)(ofname->lCustData);\r
5685     break;\r
5686   case WM_NOTIFY:\r
5687     ofnot = (OFNOTIFY *)lParam;\r
5688     if (ofnot->hdr.code == CDN_FILEOK) {\r
5689       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5690     }\r
5691     break;\r
5692   }\r
5693   return 0;\r
5694 }\r
5695 \r
5696 \r
5697 FILE *\r
5698 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5699                char *nameFilt, char *dlgTitle, UINT *number,\r
5700                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5701 {\r
5702   OPENFILENAME openFileName;\r
5703   char buf1[MSG_SIZ];\r
5704   FILE *f;\r
5705 \r
5706   if (fileName == NULL) fileName = buf1;\r
5707   if (defName == NULL) {\r
5708     strcpy(fileName, "*.");\r
5709     strcat(fileName, defExt);\r
5710   } else {\r
5711     strcpy(fileName, defName);\r
5712   }\r
5713   if (fileTitle) strcpy(fileTitle, "");\r
5714   if (number) *number = 0;\r
5715 \r
5716   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5717   openFileName.hwndOwner         = hwnd;\r
5718   openFileName.hInstance         = (HANDLE) hInst;\r
5719   openFileName.lpstrFilter       = nameFilt;\r
5720   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5721   openFileName.nMaxCustFilter    = 0L;\r
5722   openFileName.nFilterIndex      = 1L;\r
5723   openFileName.lpstrFile         = fileName;\r
5724   openFileName.nMaxFile          = MSG_SIZ;\r
5725   openFileName.lpstrFileTitle    = fileTitle;\r
5726   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5727   openFileName.lpstrInitialDir   = NULL;\r
5728   openFileName.lpstrTitle        = dlgTitle;\r
5729   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5730     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5731     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5732     | (oldDialog ? 0 : OFN_EXPLORER);\r
5733   openFileName.nFileOffset       = 0;\r
5734   openFileName.nFileExtension    = 0;\r
5735   openFileName.lpstrDefExt       = defExt;\r
5736   openFileName.lCustData         = (LONG) number;\r
5737   openFileName.lpfnHook          = oldDialog ?\r
5738     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5739   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5740 \r
5741   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5742                         GetOpenFileName(&openFileName)) {\r
5743     /* open the file */\r
5744     f = fopen(openFileName.lpstrFile, write);\r
5745     if (f == NULL) {\r
5746       MessageBox(hwnd, _("File open failed"), NULL,\r
5747                  MB_OK|MB_ICONEXCLAMATION);\r
5748       return NULL;\r
5749     }\r
5750   } else {\r
5751     int err = CommDlgExtendedError();\r
5752     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5753     return FALSE;\r
5754   }\r
5755   return f;\r
5756 }\r
5757 \r
5758 \r
5759 \r
5760 VOID APIENTRY\r
5761 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5762 {\r
5763   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5764 \r
5765   /*\r
5766    * Get the first pop-up menu in the menu template. This is the\r
5767    * menu that TrackPopupMenu displays.\r
5768    */\r
5769   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5770 \r
5771   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5772 \r
5773   /*\r
5774    * TrackPopup uses screen coordinates, so convert the\r
5775    * coordinates of the mouse click to screen coordinates.\r
5776    */\r
5777   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5778 \r
5779   /* Draw and track the floating pop-up menu. */\r
5780   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5781                  pt.x, pt.y, 0, hwnd, NULL);\r
5782 \r
5783   /* Destroy the menu.*/\r
5784   DestroyMenu(hmenu);\r
5785 }\r
5786    \r
5787 typedef struct {\r
5788   HWND hDlg, hText;\r
5789   int sizeX, sizeY, newSizeX, newSizeY;\r
5790   HDWP hdwp;\r
5791 } ResizeEditPlusButtonsClosure;\r
5792 \r
5793 BOOL CALLBACK\r
5794 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5795 {\r
5796   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5797   RECT rect;\r
5798   POINT pt;\r
5799 \r
5800   if (hChild == cl->hText) return TRUE;\r
5801   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5802   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5803   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5804   ScreenToClient(cl->hDlg, &pt);\r
5805   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5806     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5807   return TRUE;\r
5808 }\r
5809 \r
5810 /* Resize a dialog that has a (rich) edit field filling most of\r
5811    the top, with a row of buttons below */\r
5812 VOID\r
5813 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5814 {\r
5815   RECT rectText;\r
5816   int newTextHeight, newTextWidth;\r
5817   ResizeEditPlusButtonsClosure cl;\r
5818   \r
5819   /*if (IsIconic(hDlg)) return;*/\r
5820   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5821   \r
5822   cl.hdwp = BeginDeferWindowPos(8);\r
5823 \r
5824   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5825   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5826   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5827   if (newTextHeight < 0) {\r
5828     newSizeY += -newTextHeight;\r
5829     newTextHeight = 0;\r
5830   }\r
5831   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5832     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5833 \r
5834   cl.hDlg = hDlg;\r
5835   cl.hText = hText;\r
5836   cl.sizeX = sizeX;\r
5837   cl.sizeY = sizeY;\r
5838   cl.newSizeX = newSizeX;\r
5839   cl.newSizeY = newSizeY;\r
5840   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5841 \r
5842   EndDeferWindowPos(cl.hdwp);\r
5843 }\r
5844 \r
5845 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5846 {\r
5847     RECT    rChild, rParent;\r
5848     int     wChild, hChild, wParent, hParent;\r
5849     int     wScreen, hScreen, xNew, yNew;\r
5850     HDC     hdc;\r
5851 \r
5852     /* Get the Height and Width of the child window */\r
5853     GetWindowRect (hwndChild, &rChild);\r
5854     wChild = rChild.right - rChild.left;\r
5855     hChild = rChild.bottom - rChild.top;\r
5856 \r
5857     /* Get the Height and Width of the parent window */\r
5858     GetWindowRect (hwndParent, &rParent);\r
5859     wParent = rParent.right - rParent.left;\r
5860     hParent = rParent.bottom - rParent.top;\r
5861 \r
5862     /* Get the display limits */\r
5863     hdc = GetDC (hwndChild);\r
5864     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5865     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5866     ReleaseDC(hwndChild, hdc);\r
5867 \r
5868     /* Calculate new X position, then adjust for screen */\r
5869     xNew = rParent.left + ((wParent - wChild) /2);\r
5870     if (xNew < 0) {\r
5871         xNew = 0;\r
5872     } else if ((xNew+wChild) > wScreen) {\r
5873         xNew = wScreen - wChild;\r
5874     }\r
5875 \r
5876     /* Calculate new Y position, then adjust for screen */\r
5877     if( mode == 0 ) {\r
5878         yNew = rParent.top  + ((hParent - hChild) /2);\r
5879     }\r
5880     else {\r
5881         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5882     }\r
5883 \r
5884     if (yNew < 0) {\r
5885         yNew = 0;\r
5886     } else if ((yNew+hChild) > hScreen) {\r
5887         yNew = hScreen - hChild;\r
5888     }\r
5889 \r
5890     /* Set it, and return */\r
5891     return SetWindowPos (hwndChild, NULL,\r
5892                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5893 }\r
5894 \r
5895 /* Center one window over another */\r
5896 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5897 {\r
5898     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5899 }\r
5900 \r
5901 /*---------------------------------------------------------------------------*\\r
5902  *\r
5903  * Startup Dialog functions\r
5904  *\r
5905 \*---------------------------------------------------------------------------*/\r
5906 void\r
5907 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5908 {\r
5909   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5910 \r
5911   while (*cd != NULL) {\r
5912     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5913     cd++;\r
5914   }\r
5915 }\r
5916 \r
5917 void\r
5918 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5919 {\r
5920   char buf1[MAX_ARG_LEN];\r
5921   int len;\r
5922 \r
5923   if (str[0] == '@') {\r
5924     FILE* f = fopen(str + 1, "r");\r
5925     if (f == NULL) {\r
5926       DisplayFatalError(str + 1, errno, 2);\r
5927       return;\r
5928     }\r
5929     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5930     fclose(f);\r
5931     buf1[len] = NULLCHAR;\r
5932     str = buf1;\r
5933   }\r
5934 \r
5935   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5936 \r
5937   for (;;) {\r
5938     char buf[MSG_SIZ];\r
5939     char *end = strchr(str, '\n');\r
5940     if (end == NULL) return;\r
5941     memcpy(buf, str, end - str);\r
5942     buf[end - str] = NULLCHAR;\r
5943     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5944     str = end + 1;\r
5945   }\r
5946 }\r
5947 \r
5948 void\r
5949 SetStartupDialogEnables(HWND hDlg)\r
5950 {\r
5951   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
5952     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5953     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
5954   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5955     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
5956   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
5957     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
5958   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
5959     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
5960   EnableWindow(GetDlgItem(hDlg, IDOK),\r
5961     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5962     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
5963     IsDlgButtonChecked(hDlg, OPT_View));\r
5964 }\r
5965 \r
5966 char *\r
5967 QuoteForFilename(char *filename)\r
5968 {\r
5969   int dquote, space;\r
5970   dquote = strchr(filename, '"') != NULL;\r
5971   space = strchr(filename, ' ') != NULL;\r
5972   if (dquote || space) {\r
5973     if (dquote) {\r
5974       return "'";\r
5975     } else {\r
5976       return "\"";\r
5977     }\r
5978   } else {\r
5979     return "";\r
5980   }\r
5981 }\r
5982 \r
5983 VOID\r
5984 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
5985 {\r
5986   char buf[MSG_SIZ];\r
5987   char *q;\r
5988 \r
5989   InitComboStringsFromOption(hwndCombo, nthnames);\r
5990   q = QuoteForFilename(nthcp);\r
5991   sprintf(buf, "%s%s%s", q, nthcp, q);\r
5992   if (*nthdir != NULLCHAR) {\r
5993     q = QuoteForFilename(nthdir);\r
5994     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
5995   }\r
5996   if (*nthcp == NULLCHAR) {\r
5997     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
5998   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
5999     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6000     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6001   }\r
6002 }\r
6003 \r
6004 LRESULT CALLBACK\r
6005 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6006 {\r
6007   char buf[MSG_SIZ];\r
6008   HANDLE hwndCombo;\r
6009   char *p;\r
6010 \r
6011   switch (message) {\r
6012   case WM_INITDIALOG:\r
6013     /* Center the dialog */\r
6014     CenterWindow (hDlg, GetDesktopWindow());\r
6015     Translate(hDlg, DLG_Startup);\r
6016     /* Initialize the dialog items */\r
6017     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6018                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6019                   firstChessProgramNames);\r
6020     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6021                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6022                   secondChessProgramNames);\r
6023     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6024     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6025     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6026     if (*appData.icsHelper != NULLCHAR) {\r
6027       char *q = QuoteForFilename(appData.icsHelper);\r
6028       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6029     }\r
6030     if (*appData.icsHost == NULLCHAR) {\r
6031       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6032       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6033     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6034       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6035       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6036     }\r
6037 \r
6038     if (appData.icsActive) {\r
6039       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6040     }\r
6041     else if (appData.noChessProgram) {\r
6042       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6043     }\r
6044     else {\r
6045       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6046     }\r
6047 \r
6048     SetStartupDialogEnables(hDlg);\r
6049     return TRUE;\r
6050 \r
6051   case WM_COMMAND:\r
6052     switch (LOWORD(wParam)) {\r
6053     case IDOK:\r
6054       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6055         strcpy(buf, "/fcp=");\r
6056         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6057         p = buf;\r
6058         ParseArgs(StringGet, &p);\r
6059         strcpy(buf, "/scp=");\r
6060         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6061         p = buf;\r
6062         ParseArgs(StringGet, &p);\r
6063         appData.noChessProgram = FALSE;\r
6064         appData.icsActive = FALSE;\r
6065       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6066         strcpy(buf, "/ics /icshost=");\r
6067         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6068         p = buf;\r
6069         ParseArgs(StringGet, &p);\r
6070         if (appData.zippyPlay) {\r
6071           strcpy(buf, "/fcp=");\r
6072           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6073           p = buf;\r
6074           ParseArgs(StringGet, &p);\r
6075         }\r
6076       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6077         appData.noChessProgram = TRUE;\r
6078         appData.icsActive = FALSE;\r
6079       } else {\r
6080         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6081                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6082         return TRUE;\r
6083       }\r
6084       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6085         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6086         p = buf;\r
6087         ParseArgs(StringGet, &p);\r
6088       }\r
6089       EndDialog(hDlg, TRUE);\r
6090       return TRUE;\r
6091 \r
6092     case IDCANCEL:\r
6093       ExitEvent(0);\r
6094       return TRUE;\r
6095 \r
6096     case IDM_HELPCONTENTS:\r
6097       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6098         MessageBox (GetFocus(),\r
6099                     _("Unable to activate help"),\r
6100                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6101       }\r
6102       break;\r
6103 \r
6104     default:\r
6105       SetStartupDialogEnables(hDlg);\r
6106       break;\r
6107     }\r
6108     break;\r
6109   }\r
6110   return FALSE;\r
6111 }\r
6112 \r
6113 /*---------------------------------------------------------------------------*\\r
6114  *\r
6115  * About box dialog functions\r
6116  *\r
6117 \*---------------------------------------------------------------------------*/\r
6118 \r
6119 /* Process messages for "About" dialog box */\r
6120 LRESULT CALLBACK\r
6121 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6122 {\r
6123   switch (message) {\r
6124   case WM_INITDIALOG: /* message: initialize dialog box */\r
6125     /* Center the dialog over the application window */\r
6126     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6127     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6128     Translate(hDlg, ABOUTBOX);\r
6129     JAWS_COPYRIGHT\r
6130     return (TRUE);\r
6131 \r
6132   case WM_COMMAND: /* message: received a command */\r
6133     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6134         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6135       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6136       return (TRUE);\r
6137     }\r
6138     break;\r
6139   }\r
6140   return (FALSE);\r
6141 }\r
6142 \r
6143 /*---------------------------------------------------------------------------*\\r
6144  *\r
6145  * Comment Dialog functions\r
6146  *\r
6147 \*---------------------------------------------------------------------------*/\r
6148 \r
6149 LRESULT CALLBACK\r
6150 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6151 {\r
6152   static HANDLE hwndText = NULL;\r
6153   int len, newSizeX, newSizeY, flags;\r
6154   static int sizeX, sizeY;\r
6155   char *str;\r
6156   RECT rect;\r
6157   MINMAXINFO *mmi;\r
6158 \r
6159   switch (message) {\r
6160   case WM_INITDIALOG: /* message: initialize dialog box */\r
6161     /* Initialize the dialog items */\r
6162     Translate(hDlg, DLG_EditComment);\r
6163     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6164     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6165     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6166     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6167     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6168     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6169     SetWindowText(hDlg, commentTitle);\r
6170     if (editComment) {\r
6171       SetFocus(hwndText);\r
6172     } else {\r
6173       SetFocus(GetDlgItem(hDlg, IDOK));\r
6174     }\r
6175     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6176                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6177                 MAKELPARAM(FALSE, 0));\r
6178     /* Size and position the dialog */\r
6179     if (!commentDialog) {\r
6180       commentDialog = hDlg;\r
6181       flags = SWP_NOZORDER;\r
6182       GetClientRect(hDlg, &rect);\r
6183       sizeX = rect.right;\r
6184       sizeY = rect.bottom;\r
6185       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6186           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6187         WINDOWPLACEMENT wp;\r
6188         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6189         wp.length = sizeof(WINDOWPLACEMENT);\r
6190         wp.flags = 0;\r
6191         wp.showCmd = SW_SHOW;\r
6192         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6193         wp.rcNormalPosition.left = wpComment.x;\r
6194         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6195         wp.rcNormalPosition.top = wpComment.y;\r
6196         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6197         SetWindowPlacement(hDlg, &wp);\r
6198 \r
6199         GetClientRect(hDlg, &rect);\r
6200         newSizeX = rect.right;\r
6201         newSizeY = rect.bottom;\r
6202         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6203                               newSizeX, newSizeY);\r
6204         sizeX = newSizeX;\r
6205         sizeY = newSizeY;\r
6206       }\r
6207     }\r
6208     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );\r
6209     return FALSE;\r
6210 \r
6211   case WM_COMMAND: /* message: received a command */\r
6212     switch (LOWORD(wParam)) {\r
6213     case IDOK:\r
6214       if (editComment) {\r
6215         char *p, *q;\r
6216         /* Read changed options from the dialog box */\r
6217         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6218         len = GetWindowTextLength(hwndText);\r
6219         str = (char *) malloc(len + 1);\r
6220         GetWindowText(hwndText, str, len + 1);\r
6221         p = q = str;\r
6222         while (*q) {\r
6223           if (*q == '\r')\r
6224             q++;\r
6225           else\r
6226             *p++ = *q++;\r
6227         }\r
6228         *p = NULLCHAR;\r
6229         ReplaceComment(commentIndex, str);\r
6230         free(str);\r
6231       }\r
6232       CommentPopDown();\r
6233       return TRUE;\r
6234 \r
6235     case IDCANCEL:\r
6236     case OPT_CancelComment:\r
6237       CommentPopDown();\r
6238       return TRUE;\r
6239 \r
6240     case OPT_ClearComment:\r
6241       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6242       break;\r
6243 \r
6244     case OPT_EditComment:\r
6245       EditCommentEvent();\r
6246       return TRUE;\r
6247 \r
6248     default:\r
6249       break;\r
6250     }\r
6251     break;\r
6252 \r
6253   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6254         if( wParam == OPT_CommentText ) {\r
6255             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6256 \r
6257             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {\r
6258                 POINTL pt;\r
6259                 LRESULT index;\r
6260 \r
6261                 pt.x = LOWORD( lpMF->lParam );\r
6262                 pt.y = HIWORD( lpMF->lParam );\r
6263 \r
6264                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6265 \r
6266                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6267                 len = GetWindowTextLength(hwndText);\r
6268                 str = (char *) malloc(len + 1);\r
6269                 GetWindowText(hwndText, str, len + 1);\r
6270                 ReplaceComment(commentIndex, str);\r
6271                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6272                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6273                 free(str);\r
6274 \r
6275                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6276                 lpMF->msg = WM_USER;\r
6277 \r
6278                 return TRUE;\r
6279             }\r
6280         }\r
6281         break;\r
6282 \r
6283   case WM_SIZE:\r
6284     newSizeX = LOWORD(lParam);\r
6285     newSizeY = HIWORD(lParam);\r
6286     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6287     sizeX = newSizeX;\r
6288     sizeY = newSizeY;\r
6289     break;\r
6290 \r
6291   case WM_GETMINMAXINFO:\r
6292     /* Prevent resizing window too small */\r
6293     mmi = (MINMAXINFO *) lParam;\r
6294     mmi->ptMinTrackSize.x = 100;\r
6295     mmi->ptMinTrackSize.y = 100;\r
6296     break;\r
6297   }\r
6298   return FALSE;\r
6299 }\r
6300 \r
6301 VOID\r
6302 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6303 {\r
6304   FARPROC lpProc;\r
6305   char *p, *q;\r
6306 \r
6307   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6308 \r
6309   if (str == NULL) str = "";\r
6310   p = (char *) malloc(2 * strlen(str) + 2);\r
6311   q = p;\r
6312   while (*str) {\r
6313     if (*str == '\n') *q++ = '\r';\r
6314     *q++ = *str++;\r
6315   }\r
6316   *q = NULLCHAR;\r
6317   if (commentText != NULL) free(commentText);\r
6318 \r
6319   commentIndex = index;\r
6320   commentTitle = title;\r
6321   commentText = p;\r
6322   editComment = edit;\r
6323 \r
6324   if (commentDialog) {\r
6325     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6326     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6327   } else {\r
6328     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6329     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6330                  hwndMain, (DLGPROC)lpProc);\r
6331     FreeProcInstance(lpProc);\r
6332   }\r
6333   commentUp = TRUE;\r
6334 }\r
6335 \r
6336 \r
6337 /*---------------------------------------------------------------------------*\\r
6338  *\r
6339  * Type-in move dialog functions\r
6340  * \r
6341 \*---------------------------------------------------------------------------*/\r
6342 \r
6343 LRESULT CALLBACK\r
6344 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6345 {\r
6346   char move[MSG_SIZ];\r
6347   HWND hInput;\r
6348   ChessMove moveType;\r
6349   int fromX, fromY, toX, toY;\r
6350   char promoChar;\r
6351 \r
6352   switch (message) {\r
6353   case WM_INITDIALOG:\r
6354     move[0] = (char) lParam;\r
6355     move[1] = NULLCHAR;\r
6356     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6357     Translate(hDlg, DLG_TypeInMove);\r
6358     hInput = GetDlgItem(hDlg, OPT_Move);\r
6359     SetWindowText(hInput, move);\r
6360     SetFocus(hInput);\r
6361     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6362     return FALSE;\r
6363 \r
6364   case WM_COMMAND:\r
6365     switch (LOWORD(wParam)) {\r
6366     case IDOK:\r
6367       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6368       { int n; Board board;\r
6369         // [HGM] FENedit\r
6370         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
6371                 EditPositionPasteFEN(move);\r
6372                 EndDialog(hDlg, TRUE);\r
6373                 return TRUE;\r
6374         }\r
6375         // [HGM] movenum: allow move number to be typed in any mode\r
6376         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
6377           ToNrEvent(2*n-1);\r
6378           EndDialog(hDlg, TRUE);\r
6379           return TRUE;\r
6380         }\r
6381       }\r
6382       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6383         gameMode != Training) {\r
6384         DisplayMoveError(_("Displayed move is not current"));\r
6385       } else {\r
6386 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
6387         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6388           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
6389         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
6390         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6391           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6392           if (gameMode != Training)\r
6393               forwardMostMove = currentMove;\r
6394           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6395         } else {\r
6396           DisplayMoveError(_("Could not parse move"));\r
6397         }\r
6398       }\r
6399       EndDialog(hDlg, TRUE);\r
6400       return TRUE;\r
6401     case IDCANCEL:\r
6402       EndDialog(hDlg, FALSE);\r
6403       return TRUE;\r
6404     default:\r
6405       break;\r
6406     }\r
6407     break;\r
6408   }\r
6409   return FALSE;\r
6410 }\r
6411 \r
6412 VOID\r
6413 PopUpMoveDialog(char firstchar)\r
6414 {\r
6415     FARPROC lpProc;\r
6416     \r
6417     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6418         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6419         gameMode == AnalyzeMode || gameMode == EditGame || \r
6420         gameMode == EditPosition || gameMode == IcsExamining ||\r
6421         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6422         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
6423                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
6424                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
6425         gameMode == Training) {\r
6426       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6427       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6428         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6429       FreeProcInstance(lpProc);\r
6430     }\r
6431 }\r
6432 \r
6433 /*---------------------------------------------------------------------------*\\r
6434  *\r
6435  * Type-in name dialog functions\r
6436  * \r
6437 \*---------------------------------------------------------------------------*/\r
6438 \r
6439 LRESULT CALLBACK\r
6440 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6441 {\r
6442   char move[MSG_SIZ];\r
6443   HWND hInput;\r
6444 \r
6445   switch (message) {\r
6446   case WM_INITDIALOG:\r
6447     move[0] = (char) lParam;\r
6448     move[1] = NULLCHAR;\r
6449     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6450     Translate(hDlg, DLG_TypeInName);\r
6451     hInput = GetDlgItem(hDlg, OPT_Name);\r
6452     SetWindowText(hInput, move);\r
6453     SetFocus(hInput);\r
6454     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6455     return FALSE;\r
6456 \r
6457   case WM_COMMAND:\r
6458     switch (LOWORD(wParam)) {\r
6459     case IDOK:\r
6460       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6461       appData.userName = strdup(move);\r
6462       SetUserLogo();\r
6463       SetGameInfo();\r
6464       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6465         sprintf(move, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6466         DisplayTitle(move);\r
6467       }\r
6468 \r
6469 \r
6470       EndDialog(hDlg, TRUE);\r
6471       return TRUE;\r
6472     case IDCANCEL:\r
6473       EndDialog(hDlg, FALSE);\r
6474       return TRUE;\r
6475     default:\r
6476       break;\r
6477     }\r
6478     break;\r
6479   }\r
6480   return FALSE;\r
6481 }\r
6482 \r
6483 VOID\r
6484 PopUpNameDialog(char firstchar)\r
6485 {\r
6486     FARPROC lpProc;\r
6487     \r
6488       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6489       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6490         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6491       FreeProcInstance(lpProc);\r
6492 }\r
6493 \r
6494 /*---------------------------------------------------------------------------*\\r
6495  *\r
6496  *  Error dialogs\r
6497  * \r
6498 \*---------------------------------------------------------------------------*/\r
6499 \r
6500 /* Nonmodal error box */\r
6501 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6502                              WPARAM wParam, LPARAM lParam);\r
6503 \r
6504 VOID\r
6505 ErrorPopUp(char *title, char *content)\r
6506 {\r
6507   FARPROC lpProc;\r
6508   char *p, *q;\r
6509   BOOLEAN modal = hwndMain == NULL;\r
6510 \r
6511   p = content;\r
6512   q = errorMessage;\r
6513   while (*p) {\r
6514     if (*p == '\n') {\r
6515       if (modal) {\r
6516         *q++ = ' ';\r
6517         p++;\r
6518       } else {\r
6519         *q++ = '\r';\r
6520         *q++ = *p++;\r
6521       }\r
6522     } else {\r
6523       *q++ = *p++;\r
6524     }\r
6525   }\r
6526   *q = NULLCHAR;\r
6527   strncpy(errorTitle, title, sizeof(errorTitle));\r
6528   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6529   \r
6530   if (modal) {\r
6531     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6532   } else {\r
6533     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6534     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6535                  hwndMain, (DLGPROC)lpProc);\r
6536     FreeProcInstance(lpProc);\r
6537   }\r
6538 }\r
6539 \r
6540 VOID\r
6541 ErrorPopDown()\r
6542 {\r
6543   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6544   if (errorDialog == NULL) return;\r
6545   DestroyWindow(errorDialog);\r
6546   errorDialog = NULL;\r
6547   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6548 }\r
6549 \r
6550 LRESULT CALLBACK\r
6551 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6552 {\r
6553   HANDLE hwndText;\r
6554   RECT rChild;\r
6555 \r
6556   switch (message) {\r
6557   case WM_INITDIALOG:\r
6558     GetWindowRect(hDlg, &rChild);\r
6559 \r
6560     /*\r
6561     SetWindowPos(hDlg, NULL, rChild.left,\r
6562       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6563       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6564     */\r
6565 \r
6566     /* \r
6567         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6568         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6569         and it doesn't work when you resize the dialog.\r
6570         For now, just give it a default position.\r
6571     */\r
6572     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6573     Translate(hDlg, DLG_Error);\r
6574 \r
6575     errorDialog = hDlg;\r
6576     SetWindowText(hDlg, errorTitle);\r
6577     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6578     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6579     return FALSE;\r
6580 \r
6581   case WM_COMMAND:\r
6582     switch (LOWORD(wParam)) {\r
6583     case IDOK:\r
6584     case IDCANCEL:\r
6585       if (errorDialog == hDlg) errorDialog = NULL;\r
6586       DestroyWindow(hDlg);\r
6587       return TRUE;\r
6588 \r
6589     default:\r
6590       break;\r
6591     }\r
6592     break;\r
6593   }\r
6594   return FALSE;\r
6595 }\r
6596 \r
6597 #ifdef GOTHIC\r
6598 HWND gothicDialog = NULL;\r
6599 \r
6600 LRESULT CALLBACK\r
6601 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6602 {\r
6603   HANDLE hwndText;\r
6604   RECT rChild;\r
6605   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6606 \r
6607   switch (message) {\r
6608   case WM_INITDIALOG:\r
6609     GetWindowRect(hDlg, &rChild);\r
6610 \r
6611     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6612                                                              SWP_NOZORDER);\r
6613 \r
6614     /* \r
6615         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6616         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6617         and it doesn't work when you resize the dialog.\r
6618         For now, just give it a default position.\r
6619     */\r
6620     gothicDialog = hDlg;\r
6621     SetWindowText(hDlg, errorTitle);\r
6622     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6623     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6624     return FALSE;\r
6625 \r
6626   case WM_COMMAND:\r
6627     switch (LOWORD(wParam)) {\r
6628     case IDOK:\r
6629     case IDCANCEL:\r
6630       if (errorDialog == hDlg) errorDialog = NULL;\r
6631       DestroyWindow(hDlg);\r
6632       return TRUE;\r
6633 \r
6634     default:\r
6635       break;\r
6636     }\r
6637     break;\r
6638   }\r
6639   return FALSE;\r
6640 }\r
6641 \r
6642 VOID\r
6643 GothicPopUp(char *title, VariantClass variant)\r
6644 {\r
6645   FARPROC lpProc;\r
6646   static char *lastTitle;\r
6647 \r
6648   strncpy(errorTitle, title, sizeof(errorTitle));\r
6649   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6650 \r
6651   if(lastTitle != title && gothicDialog != NULL) {\r
6652     DestroyWindow(gothicDialog);\r
6653     gothicDialog = NULL;\r
6654   }\r
6655   if(variant != VariantNormal && gothicDialog == NULL) {\r
6656     title = lastTitle;\r
6657     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6658     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6659                  hwndMain, (DLGPROC)lpProc);\r
6660     FreeProcInstance(lpProc);\r
6661   }\r
6662 }\r
6663 #endif\r
6664 \r
6665 /*---------------------------------------------------------------------------*\\r
6666  *\r
6667  *  Ics Interaction console functions\r
6668  *\r
6669 \*---------------------------------------------------------------------------*/\r
6670 \r
6671 #define HISTORY_SIZE 64\r
6672 static char *history[HISTORY_SIZE];\r
6673 int histIn = 0, histP = 0;\r
6674 \r
6675 VOID\r
6676 SaveInHistory(char *cmd)\r
6677 {\r
6678   if (history[histIn] != NULL) {\r
6679     free(history[histIn]);\r
6680     history[histIn] = NULL;\r
6681   }\r
6682   if (*cmd == NULLCHAR) return;\r
6683   history[histIn] = StrSave(cmd);\r
6684   histIn = (histIn + 1) % HISTORY_SIZE;\r
6685   if (history[histIn] != NULL) {\r
6686     free(history[histIn]);\r
6687     history[histIn] = NULL;\r
6688   }\r
6689   histP = histIn;\r
6690 }\r
6691 \r
6692 char *\r
6693 PrevInHistory(char *cmd)\r
6694 {\r
6695   int newhp;\r
6696   if (histP == histIn) {\r
6697     if (history[histIn] != NULL) free(history[histIn]);\r
6698     history[histIn] = StrSave(cmd);\r
6699   }\r
6700   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6701   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6702   histP = newhp;\r
6703   return history[histP];\r
6704 }\r
6705 \r
6706 char *\r
6707 NextInHistory()\r
6708 {\r
6709   if (histP == histIn) return NULL;\r
6710   histP = (histP + 1) % HISTORY_SIZE;\r
6711   return history[histP];   \r
6712 }\r
6713 \r
6714 HMENU\r
6715 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6716 {\r
6717   HMENU hmenu, h;\r
6718   int i = 0;\r
6719   hmenu = LoadMenu(hInst, "TextMenu");\r
6720   h = GetSubMenu(hmenu, 0);\r
6721   while (e->item) {\r
6722     if (strcmp(e->item, "-") == 0) {\r
6723       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6724     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6725       int flags = MF_STRING, j = 0;\r
6726       if (e->item[0] == '|') {\r
6727         flags |= MF_MENUBARBREAK;\r
6728         j++;\r
6729       }\r
6730       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6731       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6732     }\r
6733     e++;\r
6734     i++;\r
6735   } \r
6736   return hmenu;\r
6737 }\r
6738 \r
6739 WNDPROC consoleTextWindowProc;\r
6740 \r
6741 void\r
6742 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6743 {\r
6744   char buf[MSG_SIZ], name[MSG_SIZ];\r
6745   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6746   CHARRANGE sel;\r
6747 \r
6748   if (!getname) {\r
6749     SetWindowText(hInput, command);\r
6750     if (immediate) {\r
6751       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6752     } else {\r
6753       sel.cpMin = 999999;\r
6754       sel.cpMax = 999999;\r
6755       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6756       SetFocus(hInput);\r
6757     }\r
6758     return;\r
6759   }    \r
6760   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6761   if (sel.cpMin == sel.cpMax) {\r
6762     /* Expand to surrounding word */\r
6763     TEXTRANGE tr;\r
6764     do {\r
6765       tr.chrg.cpMax = sel.cpMin;\r
6766       tr.chrg.cpMin = --sel.cpMin;\r
6767       if (sel.cpMin < 0) break;\r
6768       tr.lpstrText = name;\r
6769       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6770     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6771     sel.cpMin++;\r
6772 \r
6773     do {\r
6774       tr.chrg.cpMin = sel.cpMax;\r
6775       tr.chrg.cpMax = ++sel.cpMax;\r
6776       tr.lpstrText = name;\r
6777       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6778     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6779     sel.cpMax--;\r
6780 \r
6781     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6782       MessageBeep(MB_ICONEXCLAMATION);\r
6783       return;\r
6784     }\r
6785     tr.chrg = sel;\r
6786     tr.lpstrText = name;\r
6787     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6788   } else {\r
6789     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6790       MessageBeep(MB_ICONEXCLAMATION);\r
6791       return;\r
6792     }\r
6793     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6794   }\r
6795   if (immediate) {\r
6796     sprintf(buf, "%s %s", command, name);\r
6797     SetWindowText(hInput, buf);\r
6798     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6799   } else {\r
6800     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6801     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
6802     SetWindowText(hInput, buf);\r
6803     sel.cpMin = 999999;\r
6804     sel.cpMax = 999999;\r
6805     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6806     SetFocus(hInput);\r
6807   }\r
6808 }\r
6809 \r
6810 LRESULT CALLBACK \r
6811 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6812 {\r
6813   HWND hInput;\r
6814   CHARRANGE sel;\r
6815 \r
6816   switch (message) {\r
6817   case WM_KEYDOWN:\r
6818     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6819     switch (wParam) {\r
6820     case VK_PRIOR:\r
6821       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6822       return 0;\r
6823     case VK_NEXT:\r
6824       sel.cpMin = 999999;\r
6825       sel.cpMax = 999999;\r
6826       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6827       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6828       return 0;\r
6829     }\r
6830     break;\r
6831   case WM_CHAR:\r
6832    if(wParam != '\022') {\r
6833     if (wParam == '\t') {\r
6834       if (GetKeyState(VK_SHIFT) < 0) {\r
6835         /* shifted */\r
6836         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6837         if (buttonDesc[0].hwnd) {\r
6838           SetFocus(buttonDesc[0].hwnd);\r
6839         } else {\r
6840           SetFocus(hwndMain);\r
6841         }\r
6842       } else {\r
6843         /* unshifted */\r
6844         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6845       }\r
6846     } else {\r
6847       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6848       JAWS_DELETE( SetFocus(hInput); )\r
6849       SendMessage(hInput, message, wParam, lParam);\r
6850     }\r
6851     return 0;\r
6852    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
6853   case WM_RBUTTONDOWN:\r
6854     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6855       /* Move selection here if it was empty */\r
6856       POINT pt;\r
6857       pt.x = LOWORD(lParam);\r
6858       pt.y = HIWORD(lParam);\r
6859       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6860       if (sel.cpMin == sel.cpMax) {\r
6861         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6862         sel.cpMax = sel.cpMin;\r
6863         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6864       }\r
6865       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6866 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6867       POINT pt;\r
6868       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6869       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6870       if (sel.cpMin == sel.cpMax) {\r
6871         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6872         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6873       }\r
6874       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6875         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6876       }\r
6877       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6878       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6879       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6880       MenuPopup(hwnd, pt, hmenu, -1);\r
6881 }\r
6882     }\r
6883     return 0;\r
6884   case WM_RBUTTONUP:\r
6885     if (GetKeyState(VK_SHIFT) & ~1) {\r
6886       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6887         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6888     }\r
6889     return 0;\r
6890   case WM_PASTE:\r
6891     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6892     SetFocus(hInput);\r
6893     return SendMessage(hInput, message, wParam, lParam);\r
6894   case WM_MBUTTONDOWN:\r
6895     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6896   case WM_COMMAND:\r
6897     switch (LOWORD(wParam)) {\r
6898     case IDM_QuickPaste:\r
6899       {\r
6900         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6901         if (sel.cpMin == sel.cpMax) {\r
6902           MessageBeep(MB_ICONEXCLAMATION);\r
6903           return 0;\r
6904         }\r
6905         SendMessage(hwnd, WM_COPY, 0, 0);\r
6906         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6907         SendMessage(hInput, WM_PASTE, 0, 0);\r
6908         SetFocus(hInput);\r
6909         return 0;\r
6910       }\r
6911     case IDM_Cut:\r
6912       SendMessage(hwnd, WM_CUT, 0, 0);\r
6913       return 0;\r
6914     case IDM_Paste:\r
6915       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6916       return 0;\r
6917     case IDM_Copy:\r
6918       SendMessage(hwnd, WM_COPY, 0, 0);\r
6919       return 0;\r
6920     default:\r
6921       {\r
6922         int i = LOWORD(wParam) - IDM_CommandX;\r
6923         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6924             icsTextMenuEntry[i].command != NULL) {\r
6925           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6926                    icsTextMenuEntry[i].getname,\r
6927                    icsTextMenuEntry[i].immediate);\r
6928           return 0;\r
6929         }\r
6930       }\r
6931       break;\r
6932     }\r
6933     break;\r
6934   }\r
6935   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6936 }\r
6937 \r
6938 WNDPROC consoleInputWindowProc;\r
6939 \r
6940 LRESULT CALLBACK\r
6941 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6942 {\r
6943   char buf[MSG_SIZ];\r
6944   char *p;\r
6945   static BOOL sendNextChar = FALSE;\r
6946   static BOOL quoteNextChar = FALSE;\r
6947   InputSource *is = consoleInputSource;\r
6948   CHARFORMAT cf;\r
6949   CHARRANGE sel;\r
6950 \r
6951   switch (message) {\r
6952   case WM_CHAR:\r
6953     if (!appData.localLineEditing || sendNextChar) {\r
6954       is->buf[0] = (CHAR) wParam;\r
6955       is->count = 1;\r
6956       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6957       sendNextChar = FALSE;\r
6958       return 0;\r
6959     }\r
6960     if (quoteNextChar) {\r
6961       buf[0] = (char) wParam;\r
6962       buf[1] = NULLCHAR;\r
6963       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
6964       quoteNextChar = FALSE;\r
6965       return 0;\r
6966     }\r
6967     switch (wParam) {\r
6968     case '\r':   /* Enter key */\r
6969       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
6970       if (consoleEcho) SaveInHistory(is->buf);\r
6971       is->buf[is->count++] = '\n';\r
6972       is->buf[is->count] = NULLCHAR;\r
6973       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6974       if (consoleEcho) {\r
6975         ConsoleOutput(is->buf, is->count, TRUE);\r
6976       } else if (appData.localLineEditing) {\r
6977         ConsoleOutput("\n", 1, TRUE);\r
6978       }\r
6979       /* fall thru */\r
6980     case '\033': /* Escape key */\r
6981       SetWindowText(hwnd, "");\r
6982       cf.cbSize = sizeof(CHARFORMAT);\r
6983       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
6984       if (consoleEcho) {\r
6985         cf.crTextColor = textAttribs[ColorNormal].color;\r
6986       } else {\r
6987         cf.crTextColor = COLOR_ECHOOFF;\r
6988       }\r
6989       cf.dwEffects = textAttribs[ColorNormal].effects;\r
6990       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
6991       return 0;\r
6992     case '\t':   /* Tab key */\r
6993       if (GetKeyState(VK_SHIFT) < 0) {\r
6994         /* shifted */\r
6995         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
6996       } else {\r
6997         /* unshifted */\r
6998         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6999         if (buttonDesc[0].hwnd) {\r
7000           SetFocus(buttonDesc[0].hwnd);\r
7001         } else {\r
7002           SetFocus(hwndMain);\r
7003         }\r
7004       }\r
7005       return 0;\r
7006     case '\023': /* Ctrl+S */\r
7007       sendNextChar = TRUE;\r
7008       return 0;\r
7009     case '\021': /* Ctrl+Q */\r
7010       quoteNextChar = TRUE;\r
7011       return 0;\r
7012     JAWS_REPLAY\r
7013     default:\r
7014       break;\r
7015     }\r
7016     break;\r
7017   case WM_KEYDOWN:\r
7018     switch (wParam) {\r
7019     case VK_UP:\r
7020       GetWindowText(hwnd, buf, MSG_SIZ);\r
7021       p = PrevInHistory(buf);\r
7022       if (p != NULL) {\r
7023         SetWindowText(hwnd, p);\r
7024         sel.cpMin = 999999;\r
7025         sel.cpMax = 999999;\r
7026         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7027         return 0;\r
7028       }\r
7029       break;\r
7030     case VK_DOWN:\r
7031       p = NextInHistory();\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_HOME:\r
7041     case VK_END:\r
7042       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7043       /* fall thru */\r
7044     case VK_PRIOR:\r
7045     case VK_NEXT:\r
7046       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7047       return 0;\r
7048     }\r
7049     break;\r
7050   case WM_MBUTTONDOWN:\r
7051     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7052       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7053     break;\r
7054   case WM_RBUTTONUP:\r
7055     if (GetKeyState(VK_SHIFT) & ~1) {\r
7056       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7057         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7058     } else {\r
7059       POINT pt;\r
7060       HMENU hmenu;\r
7061       hmenu = LoadMenu(hInst, "InputMenu");\r
7062       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7063       if (sel.cpMin == sel.cpMax) {\r
7064         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7065         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7066       }\r
7067       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7068         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7069       }\r
7070       pt.x = LOWORD(lParam);\r
7071       pt.y = HIWORD(lParam);\r
7072       MenuPopup(hwnd, pt, hmenu, -1);\r
7073     }\r
7074     return 0;\r
7075   case WM_COMMAND:\r
7076     switch (LOWORD(wParam)) { \r
7077     case IDM_Undo:\r
7078       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7079       return 0;\r
7080     case IDM_SelectAll:\r
7081       sel.cpMin = 0;\r
7082       sel.cpMax = -1; /*999999?*/\r
7083       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7084       return 0;\r
7085     case IDM_Cut:\r
7086       SendMessage(hwnd, WM_CUT, 0, 0);\r
7087       return 0;\r
7088     case IDM_Paste:\r
7089       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7090       return 0;\r
7091     case IDM_Copy:\r
7092       SendMessage(hwnd, WM_COPY, 0, 0);\r
7093       return 0;\r
7094     }\r
7095     break;\r
7096   }\r
7097   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7098 }\r
7099 \r
7100 #define CO_MAX  100000\r
7101 #define CO_TRIM   1000\r
7102 \r
7103 LRESULT CALLBACK\r
7104 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7105 {\r
7106   static SnapData sd;\r
7107   HWND hText, hInput;\r
7108   RECT rect;\r
7109   static int sizeX, sizeY;\r
7110   int newSizeX, newSizeY;\r
7111   MINMAXINFO *mmi;\r
7112   WORD wMask;\r
7113 \r
7114   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7115   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7116 \r
7117   switch (message) {\r
7118   case WM_NOTIFY:\r
7119     if (((NMHDR*)lParam)->code == EN_LINK)\r
7120     {\r
7121       ENLINK *pLink = (ENLINK*)lParam;\r
7122       if (pLink->msg == WM_LBUTTONUP)\r
7123       {\r
7124         TEXTRANGE tr;\r
7125 \r
7126         tr.chrg = pLink->chrg;\r
7127         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7128         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7129         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7130         free(tr.lpstrText);\r
7131       }\r
7132     }\r
7133     break;\r
7134   case WM_INITDIALOG: /* message: initialize dialog box */\r
7135     hwndConsole = hDlg;\r
7136     SetFocus(hInput);\r
7137     consoleTextWindowProc = (WNDPROC)\r
7138       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7139     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7140     consoleInputWindowProc = (WNDPROC)\r
7141       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7142     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7143     Colorize(ColorNormal, TRUE);\r
7144     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7145     ChangedConsoleFont();\r
7146     GetClientRect(hDlg, &rect);\r
7147     sizeX = rect.right;\r
7148     sizeY = rect.bottom;\r
7149     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7150         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7151       WINDOWPLACEMENT wp;\r
7152       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7153       wp.length = sizeof(WINDOWPLACEMENT);\r
7154       wp.flags = 0;\r
7155       wp.showCmd = SW_SHOW;\r
7156       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7157       wp.rcNormalPosition.left = wpConsole.x;\r
7158       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7159       wp.rcNormalPosition.top = wpConsole.y;\r
7160       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7161       SetWindowPlacement(hDlg, &wp);\r
7162     }\r
7163 \r
7164    // [HGM] Chessknight's change 2004-07-13\r
7165    else { /* Determine Defaults */\r
7166        WINDOWPLACEMENT wp;\r
7167        wpConsole.x = wpMain.width + 1;\r
7168        wpConsole.y = wpMain.y;\r
7169        wpConsole.width = screenWidth -  wpMain.width;\r
7170        wpConsole.height = wpMain.height;\r
7171        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7172        wp.length = sizeof(WINDOWPLACEMENT);\r
7173        wp.flags = 0;\r
7174        wp.showCmd = SW_SHOW;\r
7175        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7176        wp.rcNormalPosition.left = wpConsole.x;\r
7177        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7178        wp.rcNormalPosition.top = wpConsole.y;\r
7179        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7180        SetWindowPlacement(hDlg, &wp);\r
7181     }\r
7182 \r
7183    // Allow hText to highlight URLs and send notifications on them\r
7184    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7185    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7186    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7187    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
7188 \r
7189     return FALSE;\r
7190 \r
7191   case WM_SETFOCUS:\r
7192     SetFocus(hInput);\r
7193     return 0;\r
7194 \r
7195   case WM_CLOSE:\r
7196     ExitEvent(0);\r
7197     /* not reached */\r
7198     break;\r
7199 \r
7200   case WM_SIZE:\r
7201     if (IsIconic(hDlg)) break;\r
7202     newSizeX = LOWORD(lParam);\r
7203     newSizeY = HIWORD(lParam);\r
7204     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7205       RECT rectText, rectInput;\r
7206       POINT pt;\r
7207       int newTextHeight, newTextWidth;\r
7208       GetWindowRect(hText, &rectText);\r
7209       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7210       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7211       if (newTextHeight < 0) {\r
7212         newSizeY += -newTextHeight;\r
7213         newTextHeight = 0;\r
7214       }\r
7215       SetWindowPos(hText, NULL, 0, 0,\r
7216         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7217       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7218       pt.x = rectInput.left;\r
7219       pt.y = rectInput.top + newSizeY - sizeY;\r
7220       ScreenToClient(hDlg, &pt);\r
7221       SetWindowPos(hInput, NULL, \r
7222         pt.x, pt.y, /* needs client coords */   \r
7223         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7224         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7225     }\r
7226     sizeX = newSizeX;\r
7227     sizeY = newSizeY;\r
7228     break;\r
7229 \r
7230   case WM_GETMINMAXINFO:\r
7231     /* Prevent resizing window too small */\r
7232     mmi = (MINMAXINFO *) lParam;\r
7233     mmi->ptMinTrackSize.x = 100;\r
7234     mmi->ptMinTrackSize.y = 100;\r
7235     break;\r
7236 \r
7237   /* [AS] Snapping */\r
7238   case WM_ENTERSIZEMOVE:\r
7239     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7240 \r
7241   case WM_SIZING:\r
7242     return OnSizing( &sd, hDlg, wParam, lParam );\r
7243 \r
7244   case WM_MOVING:\r
7245     return OnMoving( &sd, hDlg, wParam, lParam );\r
7246 \r
7247   case WM_EXITSIZEMOVE:\r
7248         UpdateICSWidth(hText);\r
7249     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7250   }\r
7251 \r
7252   return DefWindowProc(hDlg, message, wParam, lParam);\r
7253 }\r
7254 \r
7255 \r
7256 VOID\r
7257 ConsoleCreate()\r
7258 {\r
7259   HWND hCons;\r
7260   if (hwndConsole) return;\r
7261   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7262   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7263 }\r
7264 \r
7265 \r
7266 VOID\r
7267 ConsoleOutput(char* data, int length, int forceVisible)\r
7268 {\r
7269   HWND hText;\r
7270   int trim, exlen;\r
7271   char *p, *q;\r
7272   char buf[CO_MAX+1];\r
7273   POINT pEnd;\r
7274   RECT rect;\r
7275   static int delayLF = 0;\r
7276   CHARRANGE savesel, sel;\r
7277 \r
7278   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7279   p = data;\r
7280   q = buf;\r
7281   if (delayLF) {\r
7282     *q++ = '\r';\r
7283     *q++ = '\n';\r
7284     delayLF = 0;\r
7285   }\r
7286   while (length--) {\r
7287     if (*p == '\n') {\r
7288       if (*++p) {\r
7289         *q++ = '\r';\r
7290         *q++ = '\n';\r
7291       } else {\r
7292         delayLF = 1;\r
7293       }\r
7294     } else if (*p == '\007') {\r
7295        MyPlaySound(&sounds[(int)SoundBell]);\r
7296        p++;\r
7297     } else {\r
7298       *q++ = *p++;\r
7299     }\r
7300   }\r
7301   *q = NULLCHAR;\r
7302   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7303   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7304   /* Save current selection */\r
7305   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7306   exlen = GetWindowTextLength(hText);\r
7307   /* Find out whether current end of text is visible */\r
7308   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7309   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7310   /* Trim existing text if it's too long */\r
7311   if (exlen + (q - buf) > CO_MAX) {\r
7312     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7313     sel.cpMin = 0;\r
7314     sel.cpMax = trim;\r
7315     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7316     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7317     exlen -= trim;\r
7318     savesel.cpMin -= trim;\r
7319     savesel.cpMax -= trim;\r
7320     if (exlen < 0) exlen = 0;\r
7321     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7322     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7323   }\r
7324   /* Append the new text */\r
7325   sel.cpMin = exlen;\r
7326   sel.cpMax = exlen;\r
7327   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7328   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7329   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7330   if (forceVisible || exlen == 0 ||\r
7331       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7332        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7333     /* Scroll to make new end of text visible if old end of text\r
7334        was visible or new text is an echo of user typein */\r
7335     sel.cpMin = 9999999;\r
7336     sel.cpMax = 9999999;\r
7337     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7338     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7339     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7340     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7341   }\r
7342   if (savesel.cpMax == exlen || forceVisible) {\r
7343     /* Move insert point to new end of text if it was at the old\r
7344        end of text or if the 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   } else {\r
7349     /* Restore previous selection */\r
7350     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7351   }\r
7352   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7353 }\r
7354 \r
7355 /*---------*/\r
7356 \r
7357 \r
7358 void\r
7359 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7360 {\r
7361   char buf[100];\r
7362   char *str;\r
7363   COLORREF oldFg, oldBg;\r
7364   HFONT oldFont;\r
7365   RECT rect;\r
7366 \r
7367   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
7368 \r
7369   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7370   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7371   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7372 \r
7373   rect.left = x;\r
7374   rect.right = x + squareSize;\r
7375   rect.top  = y;\r
7376   rect.bottom = y + squareSize;\r
7377   str = buf;\r
7378 \r
7379   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7380                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7381              y, ETO_CLIPPED|ETO_OPAQUE,\r
7382              &rect, str, strlen(str), NULL);\r
7383 \r
7384   (void) SetTextColor(hdc, oldFg);\r
7385   (void) SetBkColor(hdc, oldBg);\r
7386   (void) SelectObject(hdc, oldFont);\r
7387 }\r
7388 \r
7389 void\r
7390 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7391               RECT *rect, char *color, char *flagFell)\r
7392 {\r
7393   char buf[100];\r
7394   char *str;\r
7395   COLORREF oldFg, oldBg;\r
7396   HFONT oldFont;\r
7397 \r
7398   if (appData.clockMode) {\r
7399     if (tinyLayout)\r
7400       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7401     else\r
7402       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7403     str = buf;\r
7404   } else {\r
7405     str = color;\r
7406   }\r
7407 \r
7408   if (highlight) {\r
7409     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7410     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7411   } else {\r
7412     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7413     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7414   }\r
7415   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7416 \r
7417   JAWS_SILENCE\r
7418 \r
7419   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7420              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7421              rect, str, strlen(str), NULL);\r
7422   if(logoHeight > 0 && appData.clockMode) {\r
7423       RECT r;\r
7424       sprintf(buf, "%s %s", buf+7, flagFell);\r
7425       r.top = rect->top + logoHeight/2;\r
7426       r.left = rect->left;\r
7427       r.right = rect->right;\r
7428       r.bottom = rect->bottom;\r
7429       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7430                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7431                  &r, str, strlen(str), NULL);\r
7432   }\r
7433   (void) SetTextColor(hdc, oldFg);\r
7434   (void) SetBkColor(hdc, oldBg);\r
7435   (void) SelectObject(hdc, oldFont);\r
7436 }\r
7437 \r
7438 \r
7439 int\r
7440 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7441            OVERLAPPED *ovl)\r
7442 {\r
7443   int ok, err;\r
7444 \r
7445   /* [AS]  */\r
7446   if( count <= 0 ) {\r
7447     if (appData.debugMode) {\r
7448       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7449     }\r
7450 \r
7451     return ERROR_INVALID_USER_BUFFER;\r
7452   }\r
7453 \r
7454   ResetEvent(ovl->hEvent);\r
7455   ovl->Offset = ovl->OffsetHigh = 0;\r
7456   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7457   if (ok) {\r
7458     err = NO_ERROR;\r
7459   } else {\r
7460     err = GetLastError();\r
7461     if (err == ERROR_IO_PENDING) {\r
7462       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7463       if (ok)\r
7464         err = NO_ERROR;\r
7465       else\r
7466         err = GetLastError();\r
7467     }\r
7468   }\r
7469   return err;\r
7470 }\r
7471 \r
7472 int\r
7473 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7474             OVERLAPPED *ovl)\r
7475 {\r
7476   int ok, err;\r
7477 \r
7478   ResetEvent(ovl->hEvent);\r
7479   ovl->Offset = ovl->OffsetHigh = 0;\r
7480   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7481   if (ok) {\r
7482     err = NO_ERROR;\r
7483   } else {\r
7484     err = GetLastError();\r
7485     if (err == ERROR_IO_PENDING) {\r
7486       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7487       if (ok)\r
7488         err = NO_ERROR;\r
7489       else\r
7490         err = GetLastError();\r
7491     }\r
7492   }\r
7493   return err;\r
7494 }\r
7495 \r
7496 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7497 void CheckForInputBufferFull( InputSource * is )\r
7498 {\r
7499     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7500         /* Look for end of line */\r
7501         char * p = is->buf;\r
7502         \r
7503         while( p < is->next && *p != '\n' ) {\r
7504             p++;\r
7505         }\r
7506 \r
7507         if( p >= is->next ) {\r
7508             if (appData.debugMode) {\r
7509                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7510             }\r
7511 \r
7512             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7513             is->count = (DWORD) -1;\r
7514             is->next = is->buf;\r
7515         }\r
7516     }\r
7517 }\r
7518 \r
7519 DWORD\r
7520 InputThread(LPVOID arg)\r
7521 {\r
7522   InputSource *is;\r
7523   OVERLAPPED ovl;\r
7524 \r
7525   is = (InputSource *) arg;\r
7526   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7527   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7528   while (is->hThread != NULL) {\r
7529     is->error = DoReadFile(is->hFile, is->next,\r
7530                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7531                            &is->count, &ovl);\r
7532     if (is->error == NO_ERROR) {\r
7533       is->next += is->count;\r
7534     } else {\r
7535       if (is->error == ERROR_BROKEN_PIPE) {\r
7536         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7537         is->count = 0;\r
7538       } else {\r
7539         is->count = (DWORD) -1;\r
7540         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7541         break; \r
7542       }\r
7543     }\r
7544 \r
7545     CheckForInputBufferFull( is );\r
7546 \r
7547     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7548 \r
7549     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7550 \r
7551     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7552   }\r
7553 \r
7554   CloseHandle(ovl.hEvent);\r
7555   CloseHandle(is->hFile);\r
7556 \r
7557   if (appData.debugMode) {\r
7558     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7559   }\r
7560 \r
7561   return 0;\r
7562 }\r
7563 \r
7564 \r
7565 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7566 DWORD\r
7567 NonOvlInputThread(LPVOID arg)\r
7568 {\r
7569   InputSource *is;\r
7570   char *p, *q;\r
7571   int i;\r
7572   char prev;\r
7573 \r
7574   is = (InputSource *) arg;\r
7575   while (is->hThread != NULL) {\r
7576     is->error = ReadFile(is->hFile, is->next,\r
7577                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7578                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7579     if (is->error == NO_ERROR) {\r
7580       /* Change CRLF to LF */\r
7581       if (is->next > is->buf) {\r
7582         p = is->next - 1;\r
7583         i = is->count + 1;\r
7584       } else {\r
7585         p = is->next;\r
7586         i = is->count;\r
7587       }\r
7588       q = p;\r
7589       prev = NULLCHAR;\r
7590       while (i > 0) {\r
7591         if (prev == '\r' && *p == '\n') {\r
7592           *(q-1) = '\n';\r
7593           is->count--;\r
7594         } else { \r
7595           *q++ = *p;\r
7596         }\r
7597         prev = *p++;\r
7598         i--;\r
7599       }\r
7600       *q = NULLCHAR;\r
7601       is->next = q;\r
7602     } else {\r
7603       if (is->error == ERROR_BROKEN_PIPE) {\r
7604         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7605         is->count = 0; \r
7606       } else {\r
7607         is->count = (DWORD) -1;\r
7608       }\r
7609     }\r
7610 \r
7611     CheckForInputBufferFull( is );\r
7612 \r
7613     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7614 \r
7615     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7616 \r
7617     if (is->count < 0) break;  /* Quit on error */\r
7618   }\r
7619   CloseHandle(is->hFile);\r
7620   return 0;\r
7621 }\r
7622 \r
7623 DWORD\r
7624 SocketInputThread(LPVOID arg)\r
7625 {\r
7626   InputSource *is;\r
7627 \r
7628   is = (InputSource *) arg;\r
7629   while (is->hThread != NULL) {\r
7630     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7631     if ((int)is->count == SOCKET_ERROR) {\r
7632       is->count = (DWORD) -1;\r
7633       is->error = WSAGetLastError();\r
7634     } else {\r
7635       is->error = NO_ERROR;\r
7636       is->next += is->count;\r
7637       if (is->count == 0 && is->second == is) {\r
7638         /* End of file on stderr; quit with no message */\r
7639         break;\r
7640       }\r
7641     }\r
7642     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7643 \r
7644     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7645 \r
7646     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7647   }\r
7648   return 0;\r
7649 }\r
7650 \r
7651 VOID\r
7652 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7653 {\r
7654   InputSource *is;\r
7655 \r
7656   is = (InputSource *) lParam;\r
7657   if (is->lineByLine) {\r
7658     /* Feed in lines one by one */\r
7659     char *p = is->buf;\r
7660     char *q = p;\r
7661     while (q < is->next) {\r
7662       if (*q++ == '\n') {\r
7663         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7664         p = q;\r
7665       }\r
7666     }\r
7667     \r
7668     /* Move any partial line to the start of the buffer */\r
7669     q = is->buf;\r
7670     while (p < is->next) {\r
7671       *q++ = *p++;\r
7672     }\r
7673     is->next = q;\r
7674 \r
7675     if (is->error != NO_ERROR || is->count == 0) {\r
7676       /* Notify backend of the error.  Note: If there was a partial\r
7677          line at the end, it is not flushed through. */\r
7678       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7679     }\r
7680   } else {\r
7681     /* Feed in the whole chunk of input at once */\r
7682     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7683     is->next = is->buf;\r
7684   }\r
7685 }\r
7686 \r
7687 /*---------------------------------------------------------------------------*\\r
7688  *\r
7689  *  Menu enables. Used when setting various modes.\r
7690  *\r
7691 \*---------------------------------------------------------------------------*/\r
7692 \r
7693 typedef struct {\r
7694   int item;\r
7695   int flags;\r
7696 } Enables;\r
7697 \r
7698 VOID\r
7699 GreyRevert(Boolean grey)\r
7700 { // [HGM] vari: for retracting variations in local mode\r
7701   HMENU hmenu = GetMenu(hwndMain);\r
7702   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7703   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7704 }\r
7705 \r
7706 VOID\r
7707 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7708 {\r
7709   while (enab->item > 0) {\r
7710     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7711     enab++;\r
7712   }\r
7713 }\r
7714 \r
7715 Enables gnuEnables[] = {\r
7716   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7717   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7718   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7719   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7720   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7721   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7722   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7723   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7724   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7725   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7726   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7727   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7728   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7729   { -1, -1 }\r
7730 };\r
7731 \r
7732 Enables icsEnables[] = {\r
7733   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7734   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7735   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7736   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7737   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7738   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7739   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7740   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7741   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7742   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7743   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7744   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7745   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7746   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7747   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7748   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7749   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7750   { -1, -1 }\r
7751 };\r
7752 \r
7753 #if ZIPPY\r
7754 Enables zippyEnables[] = {\r
7755   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7756   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7757   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7758   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7759   { -1, -1 }\r
7760 };\r
7761 #endif\r
7762 \r
7763 Enables ncpEnables[] = {\r
7764   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7765   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7766   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7767   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7768   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7769   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7770   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7771   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7772   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7773   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7774   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7775   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7776   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7777   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7778   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7779   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7780   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7781   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7782   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7783   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7784   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7785   { -1, -1 }\r
7786 };\r
7787 \r
7788 Enables trainingOnEnables[] = {\r
7789   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7790   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7791   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7792   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7793   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7794   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7795   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7796   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7797   { -1, -1 }\r
7798 };\r
7799 \r
7800 Enables trainingOffEnables[] = {\r
7801   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7802   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7803   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7804   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7805   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7806   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7807   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7808   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7809   { -1, -1 }\r
7810 };\r
7811 \r
7812 /* These modify either ncpEnables or gnuEnables */\r
7813 Enables cmailEnables[] = {\r
7814   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7815   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7816   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7817   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7818   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7819   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7820   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7821   { -1, -1 }\r
7822 };\r
7823 \r
7824 Enables machineThinkingEnables[] = {\r
7825   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7826   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7827   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7828   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7829   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7830   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7831   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7832   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7833   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7834   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7835   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7836   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7837   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7838   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7839   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7840   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7841   { -1, -1 }\r
7842 };\r
7843 \r
7844 Enables userThinkingEnables[] = {\r
7845   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7846   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7847   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7848   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7849   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7850   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7851   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7852   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7853   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7854   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7855   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7856   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7857   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7858   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7859   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7860   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7861   { -1, -1 }\r
7862 };\r
7863 \r
7864 /*---------------------------------------------------------------------------*\\r
7865  *\r
7866  *  Front-end interface functions exported by XBoard.\r
7867  *  Functions appear in same order as prototypes in frontend.h.\r
7868  * \r
7869 \*---------------------------------------------------------------------------*/\r
7870 VOID\r
7871 ModeHighlight()\r
7872 {\r
7873   static UINT prevChecked = 0;\r
7874   static int prevPausing = 0;\r
7875   UINT nowChecked;\r
7876 \r
7877   if (pausing != prevPausing) {\r
7878     prevPausing = pausing;\r
7879     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7880                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7881     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7882   }\r
7883 \r
7884   switch (gameMode) {\r
7885   case BeginningOfGame:\r
7886     if (appData.icsActive)\r
7887       nowChecked = IDM_IcsClient;\r
7888     else if (appData.noChessProgram)\r
7889       nowChecked = IDM_EditGame;\r
7890     else\r
7891       nowChecked = IDM_MachineBlack;\r
7892     break;\r
7893   case MachinePlaysBlack:\r
7894     nowChecked = IDM_MachineBlack;\r
7895     break;\r
7896   case MachinePlaysWhite:\r
7897     nowChecked = IDM_MachineWhite;\r
7898     break;\r
7899   case TwoMachinesPlay:\r
7900     nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match\r
7901     break;\r
7902   case AnalyzeMode:\r
7903     nowChecked = IDM_AnalysisMode;\r
7904     break;\r
7905   case AnalyzeFile:\r
7906     nowChecked = IDM_AnalyzeFile;\r
7907     break;\r
7908   case EditGame:\r
7909     nowChecked = IDM_EditGame;\r
7910     break;\r
7911   case PlayFromGameFile:\r
7912     nowChecked = IDM_LoadGame;\r
7913     break;\r
7914   case EditPosition:\r
7915     nowChecked = IDM_EditPosition;\r
7916     break;\r
7917   case Training:\r
7918     nowChecked = IDM_Training;\r
7919     break;\r
7920   case IcsPlayingWhite:\r
7921   case IcsPlayingBlack:\r
7922   case IcsObserving:\r
7923   case IcsIdle:\r
7924     nowChecked = IDM_IcsClient;\r
7925     break;\r
7926   default:\r
7927   case EndOfGame:\r
7928     nowChecked = 0;\r
7929     break;\r
7930   }\r
7931   if (prevChecked != 0)\r
7932     (void) CheckMenuItem(GetMenu(hwndMain),\r
7933                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7934   if (nowChecked != 0)\r
7935     (void) CheckMenuItem(GetMenu(hwndMain),\r
7936                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
7937 \r
7938   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7939     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7940                           MF_BYCOMMAND|MF_ENABLED);\r
7941   } else {\r
7942     (void) EnableMenuItem(GetMenu(hwndMain), \r
7943                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7944   }\r
7945 \r
7946   prevChecked = nowChecked;\r
7947 \r
7948   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
7949   if (appData.icsActive) {\r
7950        if (appData.icsEngineAnalyze) {\r
7951                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7952                        MF_BYCOMMAND|MF_CHECKED);\r
7953        } else {\r
7954                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7955                        MF_BYCOMMAND|MF_UNCHECKED);\r
7956        }\r
7957   }\r
7958 }\r
7959 \r
7960 VOID\r
7961 SetICSMode()\r
7962 {\r
7963   HMENU hmenu = GetMenu(hwndMain);\r
7964   SetMenuEnables(hmenu, icsEnables);\r
7965   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
7966     MF_BYPOSITION|MF_ENABLED);\r
7967 #if ZIPPY\r
7968   if (appData.zippyPlay) {\r
7969     SetMenuEnables(hmenu, zippyEnables);\r
7970     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
7971          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7972           MF_BYCOMMAND|MF_ENABLED);\r
7973   }\r
7974 #endif\r
7975 }\r
7976 \r
7977 VOID\r
7978 SetGNUMode()\r
7979 {\r
7980   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
7981 }\r
7982 \r
7983 VOID\r
7984 SetNCPMode()\r
7985 {\r
7986   HMENU hmenu = GetMenu(hwndMain);\r
7987   SetMenuEnables(hmenu, ncpEnables);\r
7988   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
7989     MF_BYPOSITION|MF_GRAYED);\r
7990     DrawMenuBar(hwndMain);\r
7991 }\r
7992 \r
7993 VOID\r
7994 SetCmailMode()\r
7995 {\r
7996   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
7997 }\r
7998 \r
7999 VOID \r
8000 SetTrainingModeOn()\r
8001 {\r
8002   int i;\r
8003   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8004   for (i = 0; i < N_BUTTONS; i++) {\r
8005     if (buttonDesc[i].hwnd != NULL)\r
8006       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8007   }\r
8008   CommentPopDown();\r
8009 }\r
8010 \r
8011 VOID SetTrainingModeOff()\r
8012 {\r
8013   int i;\r
8014   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8015   for (i = 0; i < N_BUTTONS; i++) {\r
8016     if (buttonDesc[i].hwnd != NULL)\r
8017       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8018   }\r
8019 }\r
8020 \r
8021 \r
8022 VOID\r
8023 SetUserThinkingEnables()\r
8024 {\r
8025   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8026 }\r
8027 \r
8028 VOID\r
8029 SetMachineThinkingEnables()\r
8030 {\r
8031   HMENU hMenu = GetMenu(hwndMain);\r
8032   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8033 \r
8034   SetMenuEnables(hMenu, machineThinkingEnables);\r
8035 \r
8036   if (gameMode == MachinePlaysBlack) {\r
8037     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8038   } else if (gameMode == MachinePlaysWhite) {\r
8039     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8040   } else if (gameMode == TwoMachinesPlay) {\r
8041     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8042   }\r
8043 }\r
8044 \r
8045 \r
8046 VOID\r
8047 DisplayTitle(char *str)\r
8048 {\r
8049   char title[MSG_SIZ], *host;\r
8050   if (str[0] != NULLCHAR) {\r
8051     strcpy(title, str);\r
8052   } else if (appData.icsActive) {\r
8053     if (appData.icsCommPort[0] != NULLCHAR)\r
8054       host = "ICS";\r
8055     else \r
8056       host = appData.icsHost;\r
8057     sprintf(title, "%s: %s", szTitle, host);\r
8058   } else if (appData.noChessProgram) {\r
8059     strcpy(title, szTitle);\r
8060   } else {\r
8061     strcpy(title, szTitle);\r
8062     strcat(title, ": ");\r
8063     strcat(title, first.tidy);\r
8064   }\r
8065   SetWindowText(hwndMain, title);\r
8066 }\r
8067 \r
8068 \r
8069 VOID\r
8070 DisplayMessage(char *str1, char *str2)\r
8071 {\r
8072   HDC hdc;\r
8073   HFONT oldFont;\r
8074   int remain = MESSAGE_TEXT_MAX - 1;\r
8075   int len;\r
8076 \r
8077   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8078   messageText[0] = NULLCHAR;\r
8079   if (*str1) {\r
8080     len = strlen(str1);\r
8081     if (len > remain) len = remain;\r
8082     strncpy(messageText, str1, len);\r
8083     messageText[len] = NULLCHAR;\r
8084     remain -= len;\r
8085   }\r
8086   if (*str2 && remain >= 2) {\r
8087     if (*str1) {\r
8088       strcat(messageText, "  ");\r
8089       remain -= 2;\r
8090     }\r
8091     len = strlen(str2);\r
8092     if (len > remain) len = remain;\r
8093     strncat(messageText, str2, len);\r
8094   }\r
8095   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8096 \r
8097   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8098 \r
8099   SAYMACHINEMOVE();\r
8100 \r
8101   hdc = GetDC(hwndMain);\r
8102   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8103   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8104              &messageRect, messageText, strlen(messageText), NULL);\r
8105   (void) SelectObject(hdc, oldFont);\r
8106   (void) ReleaseDC(hwndMain, hdc);\r
8107 }\r
8108 \r
8109 VOID\r
8110 DisplayError(char *str, int error)\r
8111 {\r
8112   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8113   int len;\r
8114 \r
8115   if (error == 0) {\r
8116     strcpy(buf, str);\r
8117   } else {\r
8118     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8119                         NULL, error, LANG_NEUTRAL,\r
8120                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8121     if (len > 0) {\r
8122       sprintf(buf, "%s:\n%s", str, buf2);\r
8123     } else {\r
8124       ErrorMap *em = errmap;\r
8125       while (em->err != 0 && em->err != error) em++;\r
8126       if (em->err != 0) {\r
8127         sprintf(buf, "%s:\n%s", str, em->msg);\r
8128       } else {\r
8129         sprintf(buf, "%s:\nError code %d", str, error);\r
8130       }\r
8131     }\r
8132   }\r
8133   \r
8134   ErrorPopUp(_("Error"), buf);\r
8135 }\r
8136 \r
8137 \r
8138 VOID\r
8139 DisplayMoveError(char *str)\r
8140 {\r
8141   fromX = fromY = -1;\r
8142   ClearHighlights();\r
8143   DrawPosition(FALSE, NULL);\r
8144   if (appData.popupMoveErrors) {\r
8145     ErrorPopUp(_("Error"), str);\r
8146   } else {\r
8147     DisplayMessage(str, "");\r
8148     moveErrorMessageUp = TRUE;\r
8149   }\r
8150 }\r
8151 \r
8152 VOID\r
8153 DisplayFatalError(char *str, int error, int exitStatus)\r
8154 {\r
8155   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8156   int len;\r
8157   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8158 \r
8159   if (error != 0) {\r
8160     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8161                         NULL, error, LANG_NEUTRAL,\r
8162                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8163     if (len > 0) {\r
8164       sprintf(buf, "%s:\n%s", str, buf2);\r
8165     } else {\r
8166       ErrorMap *em = errmap;\r
8167       while (em->err != 0 && em->err != error) em++;\r
8168       if (em->err != 0) {\r
8169         sprintf(buf, "%s:\n%s", str, em->msg);\r
8170       } else {\r
8171         sprintf(buf, "%s:\nError code %d", str, error);\r
8172       }\r
8173     }\r
8174     str = buf;\r
8175   }\r
8176   if (appData.debugMode) {\r
8177     fprintf(debugFP, "%s: %s\n", label, str);\r
8178   }\r
8179   if (appData.popupExitMessage) {\r
8180     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8181                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8182   }\r
8183   ExitEvent(exitStatus);\r
8184 }\r
8185 \r
8186 \r
8187 VOID\r
8188 DisplayInformation(char *str)\r
8189 {\r
8190   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8191 }\r
8192 \r
8193 \r
8194 VOID\r
8195 DisplayNote(char *str)\r
8196 {\r
8197   ErrorPopUp(_("Note"), str);\r
8198 }\r
8199 \r
8200 \r
8201 typedef struct {\r
8202   char *title, *question, *replyPrefix;\r
8203   ProcRef pr;\r
8204 } QuestionParams;\r
8205 \r
8206 LRESULT CALLBACK\r
8207 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8208 {\r
8209   static QuestionParams *qp;\r
8210   char reply[MSG_SIZ];\r
8211   int len, err;\r
8212 \r
8213   switch (message) {\r
8214   case WM_INITDIALOG:\r
8215     qp = (QuestionParams *) lParam;\r
8216     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8217     Translate(hDlg, DLG_Question);\r
8218     SetWindowText(hDlg, qp->title);\r
8219     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8220     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8221     return FALSE;\r
8222 \r
8223   case WM_COMMAND:\r
8224     switch (LOWORD(wParam)) {\r
8225     case IDOK:\r
8226       strcpy(reply, qp->replyPrefix);\r
8227       if (*reply) strcat(reply, " ");\r
8228       len = strlen(reply);\r
8229       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8230       strcat(reply, "\n");\r
8231       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8232       EndDialog(hDlg, TRUE);\r
8233       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8234       return TRUE;\r
8235     case IDCANCEL:\r
8236       EndDialog(hDlg, FALSE);\r
8237       return TRUE;\r
8238     default:\r
8239       break;\r
8240     }\r
8241     break;\r
8242   }\r
8243   return FALSE;\r
8244 }\r
8245 \r
8246 VOID\r
8247 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8248 {\r
8249     QuestionParams qp;\r
8250     FARPROC lpProc;\r
8251     \r
8252     qp.title = title;\r
8253     qp.question = question;\r
8254     qp.replyPrefix = replyPrefix;\r
8255     qp.pr = pr;\r
8256     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8257     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8258       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8259     FreeProcInstance(lpProc);\r
8260 }\r
8261 \r
8262 /* [AS] Pick FRC position */\r
8263 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8264 {\r
8265     static int * lpIndexFRC;\r
8266     BOOL index_is_ok;\r
8267     char buf[16];\r
8268 \r
8269     switch( message )\r
8270     {\r
8271     case WM_INITDIALOG:\r
8272         lpIndexFRC = (int *) lParam;\r
8273 \r
8274         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8275         Translate(hDlg, DLG_NewGameFRC);\r
8276 \r
8277         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8278         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8279         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8280         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8281 \r
8282         break;\r
8283 \r
8284     case WM_COMMAND:\r
8285         switch( LOWORD(wParam) ) {\r
8286         case IDOK:\r
8287             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8288             EndDialog( hDlg, 0 );\r
8289             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8290             return TRUE;\r
8291         case IDCANCEL:\r
8292             EndDialog( hDlg, 1 );   \r
8293             return TRUE;\r
8294         case IDC_NFG_Edit:\r
8295             if( HIWORD(wParam) == EN_CHANGE ) {\r
8296                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8297 \r
8298                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8299             }\r
8300             return TRUE;\r
8301         case IDC_NFG_Random:\r
8302             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8303             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8304             return TRUE;\r
8305         }\r
8306 \r
8307         break;\r
8308     }\r
8309 \r
8310     return FALSE;\r
8311 }\r
8312 \r
8313 int NewGameFRC()\r
8314 {\r
8315     int result;\r
8316     int index = appData.defaultFrcPosition;\r
8317     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8318 \r
8319     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8320 \r
8321     if( result == 0 ) {\r
8322         appData.defaultFrcPosition = index;\r
8323     }\r
8324 \r
8325     return result;\r
8326 }\r
8327 \r
8328 /* [AS] Game list options. Refactored by HGM */\r
8329 \r
8330 HWND gameListOptionsDialog;\r
8331 \r
8332 // low-level front-end: clear text edit / list widget\r
8333 void\r
8334 GLT_ClearList()\r
8335 {\r
8336     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8337 }\r
8338 \r
8339 // low-level front-end: clear text edit / list widget\r
8340 void\r
8341 GLT_DeSelectList()\r
8342 {\r
8343     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8344 }\r
8345 \r
8346 // low-level front-end: append line to text edit / list widget\r
8347 void\r
8348 GLT_AddToList( char *name )\r
8349 {\r
8350     if( name != 0 ) {\r
8351             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8352     }\r
8353 }\r
8354 \r
8355 // low-level front-end: get line from text edit / list widget\r
8356 Boolean\r
8357 GLT_GetFromList( int index, char *name )\r
8358 {\r
8359     if( name != 0 ) {\r
8360             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8361                 return TRUE;\r
8362     }\r
8363     return FALSE;\r
8364 }\r
8365 \r
8366 void GLT_MoveSelection( HWND hDlg, int delta )\r
8367 {\r
8368     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8369     int idx2 = idx1 + delta;\r
8370     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8371 \r
8372     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8373         char buf[128];\r
8374 \r
8375         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8376         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8377         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8378         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8379     }\r
8380 }\r
8381 \r
8382 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8383 {\r
8384     switch( message )\r
8385     {\r
8386     case WM_INITDIALOG:\r
8387         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8388         \r
8389         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8390         Translate(hDlg, DLG_GameListOptions);\r
8391 \r
8392         /* Initialize list */\r
8393         GLT_TagsToList( lpUserGLT );\r
8394 \r
8395         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8396 \r
8397         break;\r
8398 \r
8399     case WM_COMMAND:\r
8400         switch( LOWORD(wParam) ) {\r
8401         case IDOK:\r
8402             GLT_ParseList();\r
8403             EndDialog( hDlg, 0 );\r
8404             return TRUE;\r
8405         case IDCANCEL:\r
8406             EndDialog( hDlg, 1 );\r
8407             return TRUE;\r
8408 \r
8409         case IDC_GLT_Default:\r
8410             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8411             return TRUE;\r
8412 \r
8413         case IDC_GLT_Restore:\r
8414             GLT_TagsToList( appData.gameListTags );\r
8415             return TRUE;\r
8416 \r
8417         case IDC_GLT_Up:\r
8418             GLT_MoveSelection( hDlg, -1 );\r
8419             return TRUE;\r
8420 \r
8421         case IDC_GLT_Down:\r
8422             GLT_MoveSelection( hDlg, +1 );\r
8423             return TRUE;\r
8424         }\r
8425 \r
8426         break;\r
8427     }\r
8428 \r
8429     return FALSE;\r
8430 }\r
8431 \r
8432 int GameListOptions()\r
8433 {\r
8434     int result;\r
8435     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8436 \r
8437     strcpy( lpUserGLT, appData.gameListTags );\r
8438 \r
8439     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8440 \r
8441     if( result == 0 ) {\r
8442         /* [AS] Memory leak here! */\r
8443         appData.gameListTags = strdup( lpUserGLT ); \r
8444     }\r
8445 \r
8446     return result;\r
8447 }\r
8448 \r
8449 VOID\r
8450 DisplayIcsInteractionTitle(char *str)\r
8451 {\r
8452   char consoleTitle[MSG_SIZ];\r
8453 \r
8454   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
8455   SetWindowText(hwndConsole, consoleTitle);\r
8456 }\r
8457 \r
8458 void\r
8459 DrawPosition(int fullRedraw, Board board)\r
8460 {\r
8461   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8462 }\r
8463 \r
8464 void NotifyFrontendLogin()\r
8465 {\r
8466         if (hwndConsole)\r
8467                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8468 }\r
8469 \r
8470 VOID\r
8471 ResetFrontEnd()\r
8472 {\r
8473   fromX = fromY = -1;\r
8474   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8475     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8476     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8477     dragInfo.lastpos = dragInfo.pos;\r
8478     dragInfo.start.x = dragInfo.start.y = -1;\r
8479     dragInfo.from = dragInfo.start;\r
8480     ReleaseCapture();\r
8481     DrawPosition(TRUE, NULL);\r
8482   }\r
8483   TagsPopDown();\r
8484 }\r
8485 \r
8486 \r
8487 VOID\r
8488 CommentPopUp(char *title, char *str)\r
8489 {\r
8490   HWND hwnd = GetActiveWindow();\r
8491   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8492   SAY(str);\r
8493   SetActiveWindow(hwnd);\r
8494 }\r
8495 \r
8496 VOID\r
8497 CommentPopDown(void)\r
8498 {\r
8499   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
8500   if (commentDialog) {\r
8501     ShowWindow(commentDialog, SW_HIDE);\r
8502   }\r
8503   commentUp = FALSE;\r
8504 }\r
8505 \r
8506 VOID\r
8507 EditCommentPopUp(int index, char *title, char *str)\r
8508 {\r
8509   EitherCommentPopUp(index, title, str, TRUE);\r
8510 }\r
8511 \r
8512 \r
8513 VOID\r
8514 RingBell()\r
8515 {\r
8516   MyPlaySound(&sounds[(int)SoundMove]);\r
8517 }\r
8518 \r
8519 VOID PlayIcsWinSound()\r
8520 {\r
8521   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8522 }\r
8523 \r
8524 VOID PlayIcsLossSound()\r
8525 {\r
8526   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8527 }\r
8528 \r
8529 VOID PlayIcsDrawSound()\r
8530 {\r
8531   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8532 }\r
8533 \r
8534 VOID PlayIcsUnfinishedSound()\r
8535 {\r
8536   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8537 }\r
8538 \r
8539 VOID\r
8540 PlayAlarmSound()\r
8541 {\r
8542   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8543 }\r
8544 \r
8545 \r
8546 VOID\r
8547 EchoOn()\r
8548 {\r
8549   HWND hInput;\r
8550   consoleEcho = TRUE;\r
8551   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8552   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8553   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8554 }\r
8555 \r
8556 \r
8557 VOID\r
8558 EchoOff()\r
8559 {\r
8560   CHARFORMAT cf;\r
8561   HWND hInput;\r
8562   consoleEcho = FALSE;\r
8563   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8564   /* This works OK: set text and background both to the same color */\r
8565   cf = consoleCF;\r
8566   cf.crTextColor = COLOR_ECHOOFF;\r
8567   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8568   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8569 }\r
8570 \r
8571 /* No Raw()...? */\r
8572 \r
8573 void Colorize(ColorClass cc, int continuation)\r
8574 {\r
8575   currentColorClass = cc;\r
8576   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8577   consoleCF.crTextColor = textAttribs[cc].color;\r
8578   consoleCF.dwEffects = textAttribs[cc].effects;\r
8579   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8580 }\r
8581 \r
8582 char *\r
8583 UserName()\r
8584 {\r
8585   static char buf[MSG_SIZ];\r
8586   DWORD bufsiz = MSG_SIZ;\r
8587 \r
8588   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8589         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8590   }\r
8591   if (!GetUserName(buf, &bufsiz)) {\r
8592     /*DisplayError("Error getting user name", GetLastError());*/\r
8593     strcpy(buf, _("User"));\r
8594   }\r
8595   return buf;\r
8596 }\r
8597 \r
8598 char *\r
8599 HostName()\r
8600 {\r
8601   static char buf[MSG_SIZ];\r
8602   DWORD bufsiz = MSG_SIZ;\r
8603 \r
8604   if (!GetComputerName(buf, &bufsiz)) {\r
8605     /*DisplayError("Error getting host name", GetLastError());*/\r
8606     strcpy(buf, _("Unknown"));\r
8607   }\r
8608   return buf;\r
8609 }\r
8610 \r
8611 \r
8612 int\r
8613 ClockTimerRunning()\r
8614 {\r
8615   return clockTimerEvent != 0;\r
8616 }\r
8617 \r
8618 int\r
8619 StopClockTimer()\r
8620 {\r
8621   if (clockTimerEvent == 0) return FALSE;\r
8622   KillTimer(hwndMain, clockTimerEvent);\r
8623   clockTimerEvent = 0;\r
8624   return TRUE;\r
8625 }\r
8626 \r
8627 void\r
8628 StartClockTimer(long millisec)\r
8629 {\r
8630   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8631                              (UINT) millisec, NULL);\r
8632 }\r
8633 \r
8634 void\r
8635 DisplayWhiteClock(long timeRemaining, int highlight)\r
8636 {\r
8637   HDC hdc;\r
8638   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8639 \r
8640   if(appData.noGUI) return;\r
8641   hdc = GetDC(hwndMain);\r
8642   if (!IsIconic(hwndMain)) {\r
8643     DisplayAClock(hdc, timeRemaining, highlight, \r
8644                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8645   }\r
8646   if (highlight && iconCurrent == iconBlack) {\r
8647     iconCurrent = iconWhite;\r
8648     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8649     if (IsIconic(hwndMain)) {\r
8650       DrawIcon(hdc, 2, 2, iconCurrent);\r
8651     }\r
8652   }\r
8653   (void) ReleaseDC(hwndMain, hdc);\r
8654   if (hwndConsole)\r
8655     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8656 }\r
8657 \r
8658 void\r
8659 DisplayBlackClock(long timeRemaining, int highlight)\r
8660 {\r
8661   HDC hdc;\r
8662   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8663 \r
8664   if(appData.noGUI) return;\r
8665   hdc = GetDC(hwndMain);\r
8666   if (!IsIconic(hwndMain)) {\r
8667     DisplayAClock(hdc, timeRemaining, highlight, \r
8668                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8669   }\r
8670   if (highlight && iconCurrent == iconWhite) {\r
8671     iconCurrent = iconBlack;\r
8672     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8673     if (IsIconic(hwndMain)) {\r
8674       DrawIcon(hdc, 2, 2, iconCurrent);\r
8675     }\r
8676   }\r
8677   (void) ReleaseDC(hwndMain, hdc);\r
8678   if (hwndConsole)\r
8679     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8680 }\r
8681 \r
8682 \r
8683 int\r
8684 LoadGameTimerRunning()\r
8685 {\r
8686   return loadGameTimerEvent != 0;\r
8687 }\r
8688 \r
8689 int\r
8690 StopLoadGameTimer()\r
8691 {\r
8692   if (loadGameTimerEvent == 0) return FALSE;\r
8693   KillTimer(hwndMain, loadGameTimerEvent);\r
8694   loadGameTimerEvent = 0;\r
8695   return TRUE;\r
8696 }\r
8697 \r
8698 void\r
8699 StartLoadGameTimer(long millisec)\r
8700 {\r
8701   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8702                                 (UINT) millisec, NULL);\r
8703 }\r
8704 \r
8705 void\r
8706 AutoSaveGame()\r
8707 {\r
8708   char *defName;\r
8709   FILE *f;\r
8710   char fileTitle[MSG_SIZ];\r
8711 \r
8712   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8713   f = OpenFileDialog(hwndMain, "a", defName,\r
8714                      appData.oldSaveStyle ? "gam" : "pgn",\r
8715                      GAME_FILT, \r
8716                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8717   if (f != NULL) {\r
8718     SaveGame(f, 0, "");\r
8719     fclose(f);\r
8720   }\r
8721 }\r
8722 \r
8723 \r
8724 void\r
8725 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8726 {\r
8727   if (delayedTimerEvent != 0) {\r
8728     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8729       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8730     }\r
8731     KillTimer(hwndMain, delayedTimerEvent);\r
8732     delayedTimerEvent = 0;\r
8733     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8734     delayedTimerCallback();\r
8735   }\r
8736   delayedTimerCallback = cb;\r
8737   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8738                                 (UINT) millisec, NULL);\r
8739 }\r
8740 \r
8741 DelayedEventCallback\r
8742 GetDelayedEvent()\r
8743 {\r
8744   if (delayedTimerEvent) {\r
8745     return delayedTimerCallback;\r
8746   } else {\r
8747     return NULL;\r
8748   }\r
8749 }\r
8750 \r
8751 void\r
8752 CancelDelayedEvent()\r
8753 {\r
8754   if (delayedTimerEvent) {\r
8755     KillTimer(hwndMain, delayedTimerEvent);\r
8756     delayedTimerEvent = 0;\r
8757   }\r
8758 }\r
8759 \r
8760 DWORD GetWin32Priority(int nice)\r
8761 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8762 /*\r
8763 REALTIME_PRIORITY_CLASS     0x00000100\r
8764 HIGH_PRIORITY_CLASS         0x00000080\r
8765 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8766 NORMAL_PRIORITY_CLASS       0x00000020\r
8767 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8768 IDLE_PRIORITY_CLASS         0x00000040\r
8769 */\r
8770         if (nice < -15) return 0x00000080;\r
8771         if (nice < 0)   return 0x00008000;\r
8772         if (nice == 0)  return 0x00000020;\r
8773         if (nice < 15)  return 0x00004000;\r
8774         return 0x00000040;\r
8775 }\r
8776 \r
8777 /* Start a child process running the given program.\r
8778    The process's standard output can be read from "from", and its\r
8779    standard input can be written to "to".\r
8780    Exit with fatal error if anything goes wrong.\r
8781    Returns an opaque pointer that can be used to destroy the process\r
8782    later.\r
8783 */\r
8784 int\r
8785 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8786 {\r
8787 #define BUFSIZE 4096\r
8788 \r
8789   HANDLE hChildStdinRd, hChildStdinWr,\r
8790     hChildStdoutRd, hChildStdoutWr;\r
8791   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8792   SECURITY_ATTRIBUTES saAttr;\r
8793   BOOL fSuccess;\r
8794   PROCESS_INFORMATION piProcInfo;\r
8795   STARTUPINFO siStartInfo;\r
8796   ChildProc *cp;\r
8797   char buf[MSG_SIZ];\r
8798   DWORD err;\r
8799 \r
8800   if (appData.debugMode) {\r
8801     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8802   }\r
8803 \r
8804   *pr = NoProc;\r
8805 \r
8806   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8807   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8808   saAttr.bInheritHandle = TRUE;\r
8809   saAttr.lpSecurityDescriptor = NULL;\r
8810 \r
8811   /*\r
8812    * The steps for redirecting child's STDOUT:\r
8813    *     1. Create anonymous pipe to be STDOUT for child.\r
8814    *     2. Create a noninheritable duplicate of read handle,\r
8815    *         and close the inheritable read handle.\r
8816    */\r
8817 \r
8818   /* Create a pipe for the child's STDOUT. */\r
8819   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8820     return GetLastError();\r
8821   }\r
8822 \r
8823   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8824   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8825                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8826                              FALSE,     /* not inherited */\r
8827                              DUPLICATE_SAME_ACCESS);\r
8828   if (! fSuccess) {\r
8829     return GetLastError();\r
8830   }\r
8831   CloseHandle(hChildStdoutRd);\r
8832 \r
8833   /*\r
8834    * The steps for redirecting child's STDIN:\r
8835    *     1. Create anonymous pipe to be STDIN for child.\r
8836    *     2. Create a noninheritable duplicate of write handle,\r
8837    *         and close the inheritable write handle.\r
8838    */\r
8839 \r
8840   /* Create a pipe for the child's STDIN. */\r
8841   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8842     return GetLastError();\r
8843   }\r
8844 \r
8845   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8846   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8847                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8848                              FALSE,     /* not inherited */\r
8849                              DUPLICATE_SAME_ACCESS);\r
8850   if (! fSuccess) {\r
8851     return GetLastError();\r
8852   }\r
8853   CloseHandle(hChildStdinWr);\r
8854 \r
8855   /* Arrange to (1) look in dir for the child .exe file, and\r
8856    * (2) have dir be the child's working directory.  Interpret\r
8857    * dir relative to the directory WinBoard loaded from. */\r
8858   GetCurrentDirectory(MSG_SIZ, buf);\r
8859   SetCurrentDirectory(installDir);\r
8860   SetCurrentDirectory(dir);\r
8861 \r
8862   /* Now create the child process. */\r
8863 \r
8864   siStartInfo.cb = sizeof(STARTUPINFO);\r
8865   siStartInfo.lpReserved = NULL;\r
8866   siStartInfo.lpDesktop = NULL;\r
8867   siStartInfo.lpTitle = NULL;\r
8868   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8869   siStartInfo.cbReserved2 = 0;\r
8870   siStartInfo.lpReserved2 = NULL;\r
8871   siStartInfo.hStdInput = hChildStdinRd;\r
8872   siStartInfo.hStdOutput = hChildStdoutWr;\r
8873   siStartInfo.hStdError = hChildStdoutWr;\r
8874 \r
8875   fSuccess = CreateProcess(NULL,\r
8876                            cmdLine,        /* command line */\r
8877                            NULL,           /* process security attributes */\r
8878                            NULL,           /* primary thread security attrs */\r
8879                            TRUE,           /* handles are inherited */\r
8880                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8881                            NULL,           /* use parent's environment */\r
8882                            NULL,\r
8883                            &siStartInfo, /* STARTUPINFO pointer */\r
8884                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8885 \r
8886   err = GetLastError();\r
8887   SetCurrentDirectory(buf); /* return to prev directory */\r
8888   if (! fSuccess) {\r
8889     return err;\r
8890   }\r
8891 \r
8892   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8893     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8894     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8895   }\r
8896 \r
8897   /* Close the handles we don't need in the parent */\r
8898   CloseHandle(piProcInfo.hThread);\r
8899   CloseHandle(hChildStdinRd);\r
8900   CloseHandle(hChildStdoutWr);\r
8901 \r
8902   /* Prepare return value */\r
8903   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8904   cp->kind = CPReal;\r
8905   cp->hProcess = piProcInfo.hProcess;\r
8906   cp->pid = piProcInfo.dwProcessId;\r
8907   cp->hFrom = hChildStdoutRdDup;\r
8908   cp->hTo = hChildStdinWrDup;\r
8909 \r
8910   *pr = (void *) cp;\r
8911 \r
8912   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8913      2000 where engines sometimes don't see the initial command(s)\r
8914      from WinBoard and hang.  I don't understand how that can happen,\r
8915      but the Sleep is harmless, so I've put it in.  Others have also\r
8916      reported what may be the same problem, so hopefully this will fix\r
8917      it for them too.  */\r
8918   Sleep(500);\r
8919 \r
8920   return NO_ERROR;\r
8921 }\r
8922 \r
8923 \r
8924 void\r
8925 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8926 {\r
8927   ChildProc *cp; int result;\r
8928 \r
8929   cp = (ChildProc *) pr;\r
8930   if (cp == NULL) return;\r
8931 \r
8932   switch (cp->kind) {\r
8933   case CPReal:\r
8934     /* TerminateProcess is considered harmful, so... */\r
8935     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8936     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8937     /* The following doesn't work because the chess program\r
8938        doesn't "have the same console" as WinBoard.  Maybe\r
8939        we could arrange for this even though neither WinBoard\r
8940        nor the chess program uses a console for stdio? */\r
8941     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8942 \r
8943     /* [AS] Special termination modes for misbehaving programs... */\r
8944     if( signal == 9 ) { \r
8945         result = TerminateProcess( cp->hProcess, 0 );\r
8946 \r
8947         if ( appData.debugMode) {\r
8948             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
8949         }\r
8950     }\r
8951     else if( signal == 10 ) {\r
8952         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
8953 \r
8954         if( dw != WAIT_OBJECT_0 ) {\r
8955             result = TerminateProcess( cp->hProcess, 0 );\r
8956 \r
8957             if ( appData.debugMode) {\r
8958                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
8959             }\r
8960 \r
8961         }\r
8962     }\r
8963 \r
8964     CloseHandle(cp->hProcess);\r
8965     break;\r
8966 \r
8967   case CPComm:\r
8968     if (cp->hFrom) CloseHandle(cp->hFrom);\r
8969     break;\r
8970 \r
8971   case CPSock:\r
8972     closesocket(cp->sock);\r
8973     WSACleanup();\r
8974     break;\r
8975 \r
8976   case CPRcmd:\r
8977     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
8978     closesocket(cp->sock);\r
8979     closesocket(cp->sock2);\r
8980     WSACleanup();\r
8981     break;\r
8982   }\r
8983   free(cp);\r
8984 }\r
8985 \r
8986 void\r
8987 InterruptChildProcess(ProcRef pr)\r
8988 {\r
8989   ChildProc *cp;\r
8990 \r
8991   cp = (ChildProc *) pr;\r
8992   if (cp == NULL) return;\r
8993   switch (cp->kind) {\r
8994   case CPReal:\r
8995     /* The following doesn't work because the chess program\r
8996        doesn't "have the same console" as WinBoard.  Maybe\r
8997        we could arrange for this even though neither WinBoard\r
8998        nor the chess program uses a console for stdio */\r
8999     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9000     break;\r
9001 \r
9002   case CPComm:\r
9003   case CPSock:\r
9004     /* Can't interrupt */\r
9005     break;\r
9006 \r
9007   case CPRcmd:\r
9008     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9009     break;\r
9010   }\r
9011 }\r
9012 \r
9013 \r
9014 int\r
9015 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9016 {\r
9017   char cmdLine[MSG_SIZ];\r
9018 \r
9019   if (port[0] == NULLCHAR) {\r
9020     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9021   } else {\r
9022     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9023   }\r
9024   return StartChildProcess(cmdLine, "", pr);\r
9025 }\r
9026 \r
9027 \r
9028 /* Code to open TCP sockets */\r
9029 \r
9030 int\r
9031 OpenTCP(char *host, char *port, ProcRef *pr)\r
9032 {\r
9033   ChildProc *cp;\r
9034   int err;\r
9035   SOCKET s;\r
9036   struct sockaddr_in sa, mysa;\r
9037   struct hostent FAR *hp;\r
9038   unsigned short uport;\r
9039   WORD wVersionRequested;\r
9040   WSADATA wsaData;\r
9041 \r
9042   /* Initialize socket DLL */\r
9043   wVersionRequested = MAKEWORD(1, 1);\r
9044   err = WSAStartup(wVersionRequested, &wsaData);\r
9045   if (err != 0) return err;\r
9046 \r
9047   /* Make socket */\r
9048   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9049     err = WSAGetLastError();\r
9050     WSACleanup();\r
9051     return err;\r
9052   }\r
9053 \r
9054   /* Bind local address using (mostly) don't-care values.\r
9055    */\r
9056   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9057   mysa.sin_family = AF_INET;\r
9058   mysa.sin_addr.s_addr = INADDR_ANY;\r
9059   uport = (unsigned short) 0;\r
9060   mysa.sin_port = htons(uport);\r
9061   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9062       == SOCKET_ERROR) {\r
9063     err = WSAGetLastError();\r
9064     WSACleanup();\r
9065     return err;\r
9066   }\r
9067 \r
9068   /* Resolve remote host name */\r
9069   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9070   if (!(hp = gethostbyname(host))) {\r
9071     unsigned int b0, b1, b2, b3;\r
9072 \r
9073     err = WSAGetLastError();\r
9074 \r
9075     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9076       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9077       hp->h_addrtype = AF_INET;\r
9078       hp->h_length = 4;\r
9079       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9080       hp->h_addr_list[0] = (char *) malloc(4);\r
9081       hp->h_addr_list[0][0] = (char) b0;\r
9082       hp->h_addr_list[0][1] = (char) b1;\r
9083       hp->h_addr_list[0][2] = (char) b2;\r
9084       hp->h_addr_list[0][3] = (char) b3;\r
9085     } else {\r
9086       WSACleanup();\r
9087       return err;\r
9088     }\r
9089   }\r
9090   sa.sin_family = hp->h_addrtype;\r
9091   uport = (unsigned short) atoi(port);\r
9092   sa.sin_port = htons(uport);\r
9093   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9094 \r
9095   /* Make connection */\r
9096   if (connect(s, (struct sockaddr *) &sa,\r
9097               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9098     err = WSAGetLastError();\r
9099     WSACleanup();\r
9100     return err;\r
9101   }\r
9102 \r
9103   /* Prepare return value */\r
9104   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9105   cp->kind = CPSock;\r
9106   cp->sock = s;\r
9107   *pr = (ProcRef *) cp;\r
9108 \r
9109   return NO_ERROR;\r
9110 }\r
9111 \r
9112 int\r
9113 OpenCommPort(char *name, ProcRef *pr)\r
9114 {\r
9115   HANDLE h;\r
9116   COMMTIMEOUTS ct;\r
9117   ChildProc *cp;\r
9118   char fullname[MSG_SIZ];\r
9119 \r
9120   if (*name != '\\')\r
9121     sprintf(fullname, "\\\\.\\%s", name);\r
9122   else\r
9123     strcpy(fullname, name);\r
9124 \r
9125   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9126                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9127   if (h == (HANDLE) -1) {\r
9128     return GetLastError();\r
9129   }\r
9130   hCommPort = h;\r
9131 \r
9132   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9133 \r
9134   /* Accumulate characters until a 100ms pause, then parse */\r
9135   ct.ReadIntervalTimeout = 100;\r
9136   ct.ReadTotalTimeoutMultiplier = 0;\r
9137   ct.ReadTotalTimeoutConstant = 0;\r
9138   ct.WriteTotalTimeoutMultiplier = 0;\r
9139   ct.WriteTotalTimeoutConstant = 0;\r
9140   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9141 \r
9142   /* Prepare return value */\r
9143   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9144   cp->kind = CPComm;\r
9145   cp->hFrom = h;\r
9146   cp->hTo = h;\r
9147   *pr = (ProcRef *) cp;\r
9148 \r
9149   return NO_ERROR;\r
9150 }\r
9151 \r
9152 int\r
9153 OpenLoopback(ProcRef *pr)\r
9154 {\r
9155   DisplayFatalError(_("Not implemented"), 0, 1);\r
9156   return NO_ERROR;\r
9157 }\r
9158 \r
9159 \r
9160 int\r
9161 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9162 {\r
9163   ChildProc *cp;\r
9164   int err;\r
9165   SOCKET s, s2, s3;\r
9166   struct sockaddr_in sa, mysa;\r
9167   struct hostent FAR *hp;\r
9168   unsigned short uport;\r
9169   WORD wVersionRequested;\r
9170   WSADATA wsaData;\r
9171   int fromPort;\r
9172   char stderrPortStr[MSG_SIZ];\r
9173 \r
9174   /* Initialize socket DLL */\r
9175   wVersionRequested = MAKEWORD(1, 1);\r
9176   err = WSAStartup(wVersionRequested, &wsaData);\r
9177   if (err != 0) return err;\r
9178 \r
9179   /* Resolve remote host name */\r
9180   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9181   if (!(hp = gethostbyname(host))) {\r
9182     unsigned int b0, b1, b2, b3;\r
9183 \r
9184     err = WSAGetLastError();\r
9185 \r
9186     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9187       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9188       hp->h_addrtype = AF_INET;\r
9189       hp->h_length = 4;\r
9190       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9191       hp->h_addr_list[0] = (char *) malloc(4);\r
9192       hp->h_addr_list[0][0] = (char) b0;\r
9193       hp->h_addr_list[0][1] = (char) b1;\r
9194       hp->h_addr_list[0][2] = (char) b2;\r
9195       hp->h_addr_list[0][3] = (char) b3;\r
9196     } else {\r
9197       WSACleanup();\r
9198       return err;\r
9199     }\r
9200   }\r
9201   sa.sin_family = hp->h_addrtype;\r
9202   uport = (unsigned short) 514;\r
9203   sa.sin_port = htons(uport);\r
9204   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9205 \r
9206   /* Bind local socket to unused "privileged" port address\r
9207    */\r
9208   s = INVALID_SOCKET;\r
9209   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9210   mysa.sin_family = AF_INET;\r
9211   mysa.sin_addr.s_addr = INADDR_ANY;\r
9212   for (fromPort = 1023;; fromPort--) {\r
9213     if (fromPort < 0) {\r
9214       WSACleanup();\r
9215       return WSAEADDRINUSE;\r
9216     }\r
9217     if (s == INVALID_SOCKET) {\r
9218       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9219         err = WSAGetLastError();\r
9220         WSACleanup();\r
9221         return err;\r
9222       }\r
9223     }\r
9224     uport = (unsigned short) fromPort;\r
9225     mysa.sin_port = htons(uport);\r
9226     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9227         == SOCKET_ERROR) {\r
9228       err = WSAGetLastError();\r
9229       if (err == WSAEADDRINUSE) continue;\r
9230       WSACleanup();\r
9231       return err;\r
9232     }\r
9233     if (connect(s, (struct sockaddr *) &sa,\r
9234       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9235       err = WSAGetLastError();\r
9236       if (err == WSAEADDRINUSE) {\r
9237         closesocket(s);\r
9238         s = -1;\r
9239         continue;\r
9240       }\r
9241       WSACleanup();\r
9242       return err;\r
9243     }\r
9244     break;\r
9245   }\r
9246 \r
9247   /* Bind stderr local socket to unused "privileged" port address\r
9248    */\r
9249   s2 = INVALID_SOCKET;\r
9250   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9251   mysa.sin_family = AF_INET;\r
9252   mysa.sin_addr.s_addr = INADDR_ANY;\r
9253   for (fromPort = 1023;; fromPort--) {\r
9254     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9255     if (fromPort < 0) {\r
9256       (void) closesocket(s);\r
9257       WSACleanup();\r
9258       return WSAEADDRINUSE;\r
9259     }\r
9260     if (s2 == INVALID_SOCKET) {\r
9261       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9262         err = WSAGetLastError();\r
9263         closesocket(s);\r
9264         WSACleanup();\r
9265         return err;\r
9266       }\r
9267     }\r
9268     uport = (unsigned short) fromPort;\r
9269     mysa.sin_port = htons(uport);\r
9270     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9271         == SOCKET_ERROR) {\r
9272       err = WSAGetLastError();\r
9273       if (err == WSAEADDRINUSE) continue;\r
9274       (void) closesocket(s);\r
9275       WSACleanup();\r
9276       return err;\r
9277     }\r
9278     if (listen(s2, 1) == SOCKET_ERROR) {\r
9279       err = WSAGetLastError();\r
9280       if (err == WSAEADDRINUSE) {\r
9281         closesocket(s2);\r
9282         s2 = INVALID_SOCKET;\r
9283         continue;\r
9284       }\r
9285       (void) closesocket(s);\r
9286       (void) closesocket(s2);\r
9287       WSACleanup();\r
9288       return err;\r
9289     }\r
9290     break;\r
9291   }\r
9292   prevStderrPort = fromPort; // remember port used\r
9293   sprintf(stderrPortStr, "%d", fromPort);\r
9294 \r
9295   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9296     err = WSAGetLastError();\r
9297     (void) closesocket(s);\r
9298     (void) closesocket(s2);\r
9299     WSACleanup();\r
9300     return err;\r
9301   }\r
9302 \r
9303   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9304     err = WSAGetLastError();\r
9305     (void) closesocket(s);\r
9306     (void) closesocket(s2);\r
9307     WSACleanup();\r
9308     return err;\r
9309   }\r
9310   if (*user == NULLCHAR) user = UserName();\r
9311   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9312     err = WSAGetLastError();\r
9313     (void) closesocket(s);\r
9314     (void) closesocket(s2);\r
9315     WSACleanup();\r
9316     return err;\r
9317   }\r
9318   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9319     err = WSAGetLastError();\r
9320     (void) closesocket(s);\r
9321     (void) closesocket(s2);\r
9322     WSACleanup();\r
9323     return err;\r
9324   }\r
9325 \r
9326   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9327     err = WSAGetLastError();\r
9328     (void) closesocket(s);\r
9329     (void) closesocket(s2);\r
9330     WSACleanup();\r
9331     return err;\r
9332   }\r
9333   (void) closesocket(s2);  /* Stop listening */\r
9334 \r
9335   /* Prepare return value */\r
9336   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9337   cp->kind = CPRcmd;\r
9338   cp->sock = s;\r
9339   cp->sock2 = s3;\r
9340   *pr = (ProcRef *) cp;\r
9341 \r
9342   return NO_ERROR;\r
9343 }\r
9344 \r
9345 \r
9346 InputSourceRef\r
9347 AddInputSource(ProcRef pr, int lineByLine,\r
9348                InputCallback func, VOIDSTAR closure)\r
9349 {\r
9350   InputSource *is, *is2 = NULL;\r
9351   ChildProc *cp = (ChildProc *) pr;\r
9352 \r
9353   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9354   is->lineByLine = lineByLine;\r
9355   is->func = func;\r
9356   is->closure = closure;\r
9357   is->second = NULL;\r
9358   is->next = is->buf;\r
9359   if (pr == NoProc) {\r
9360     is->kind = CPReal;\r
9361     consoleInputSource = is;\r
9362   } else {\r
9363     is->kind = cp->kind;\r
9364     /* \r
9365         [AS] Try to avoid a race condition if the thread is given control too early:\r
9366         we create all threads suspended so that the is->hThread variable can be\r
9367         safely assigned, then let the threads start with ResumeThread.\r
9368     */\r
9369     switch (cp->kind) {\r
9370     case CPReal:\r
9371       is->hFile = cp->hFrom;\r
9372       cp->hFrom = NULL; /* now owned by InputThread */\r
9373       is->hThread =\r
9374         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9375                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9376       break;\r
9377 \r
9378     case CPComm:\r
9379       is->hFile = cp->hFrom;\r
9380       cp->hFrom = NULL; /* now owned by InputThread */\r
9381       is->hThread =\r
9382         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9383                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9384       break;\r
9385 \r
9386     case CPSock:\r
9387       is->sock = cp->sock;\r
9388       is->hThread =\r
9389         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9390                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9391       break;\r
9392 \r
9393     case CPRcmd:\r
9394       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9395       *is2 = *is;\r
9396       is->sock = cp->sock;\r
9397       is->second = is2;\r
9398       is2->sock = cp->sock2;\r
9399       is2->second = is2;\r
9400       is->hThread =\r
9401         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9402                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9403       is2->hThread =\r
9404         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9405                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9406       break;\r
9407     }\r
9408 \r
9409     if( is->hThread != NULL ) {\r
9410         ResumeThread( is->hThread );\r
9411     }\r
9412 \r
9413     if( is2 != NULL && is2->hThread != NULL ) {\r
9414         ResumeThread( is2->hThread );\r
9415     }\r
9416   }\r
9417 \r
9418   return (InputSourceRef) is;\r
9419 }\r
9420 \r
9421 void\r
9422 RemoveInputSource(InputSourceRef isr)\r
9423 {\r
9424   InputSource *is;\r
9425 \r
9426   is = (InputSource *) isr;\r
9427   is->hThread = NULL;  /* tell thread to stop */\r
9428   CloseHandle(is->hThread);\r
9429   if (is->second != NULL) {\r
9430     is->second->hThread = NULL;\r
9431     CloseHandle(is->second->hThread);\r
9432   }\r
9433 }\r
9434 \r
9435 int no_wrap(char *message, int count)\r
9436 {\r
9437     ConsoleOutput(message, count, FALSE);\r
9438     return count;\r
9439 }\r
9440 \r
9441 int\r
9442 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9443 {\r
9444   DWORD dOutCount;\r
9445   int outCount = SOCKET_ERROR;\r
9446   ChildProc *cp = (ChildProc *) pr;\r
9447   static OVERLAPPED ovl;\r
9448   static int line = 0;\r
9449 \r
9450   if (pr == NoProc)\r
9451   {\r
9452     if (appData.noJoin || !appData.useInternalWrap)\r
9453       return no_wrap(message, count);\r
9454     else\r
9455     {\r
9456       int width = get_term_width();\r
9457       int len = wrap(NULL, message, count, width, &line);\r
9458       char *msg = malloc(len);\r
9459       int dbgchk;\r
9460 \r
9461       if (!msg)\r
9462         return no_wrap(message, count);\r
9463       else\r
9464       {\r
9465         dbgchk = wrap(msg, message, count, width, &line);\r
9466         if (dbgchk != len && appData.debugMode)\r
9467             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9468         ConsoleOutput(msg, len, FALSE);\r
9469         free(msg);\r
9470         return len;\r
9471       }\r
9472     }\r
9473   }\r
9474 \r
9475   if (ovl.hEvent == NULL) {\r
9476     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9477   }\r
9478   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9479 \r
9480   switch (cp->kind) {\r
9481   case CPSock:\r
9482   case CPRcmd:\r
9483     outCount = send(cp->sock, message, count, 0);\r
9484     if (outCount == SOCKET_ERROR) {\r
9485       *outError = WSAGetLastError();\r
9486     } else {\r
9487       *outError = NO_ERROR;\r
9488     }\r
9489     break;\r
9490 \r
9491   case CPReal:\r
9492     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9493                   &dOutCount, NULL)) {\r
9494       *outError = NO_ERROR;\r
9495       outCount = (int) dOutCount;\r
9496     } else {\r
9497       *outError = GetLastError();\r
9498     }\r
9499     break;\r
9500 \r
9501   case CPComm:\r
9502     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9503                             &dOutCount, &ovl);\r
9504     if (*outError == NO_ERROR) {\r
9505       outCount = (int) dOutCount;\r
9506     }\r
9507     break;\r
9508   }\r
9509   return outCount;\r
9510 }\r
9511 \r
9512 int\r
9513 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9514                        long msdelay)\r
9515 {\r
9516   /* Ignore delay, not implemented for WinBoard */\r
9517   return OutputToProcess(pr, message, count, outError);\r
9518 }\r
9519 \r
9520 \r
9521 void\r
9522 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9523                         char *buf, int count, int error)\r
9524 {\r
9525   DisplayFatalError(_("Not implemented"), 0, 1);\r
9526 }\r
9527 \r
9528 /* see wgamelist.c for Game List functions */\r
9529 /* see wedittags.c for Edit Tags functions */\r
9530 \r
9531 \r
9532 VOID\r
9533 ICSInitScript()\r
9534 {\r
9535   FILE *f;\r
9536   char buf[MSG_SIZ];\r
9537   char *dummy;\r
9538 \r
9539   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9540     f = fopen(buf, "r");\r
9541     if (f != NULL) {\r
9542       ProcessICSInitScript(f);\r
9543       fclose(f);\r
9544     }\r
9545   }\r
9546 }\r
9547 \r
9548 \r
9549 VOID\r
9550 StartAnalysisClock()\r
9551 {\r
9552   if (analysisTimerEvent) return;\r
9553   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9554                                         (UINT) 2000, NULL);\r
9555 }\r
9556 \r
9557 VOID\r
9558 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9559 {\r
9560   highlightInfo.sq[0].x = fromX;\r
9561   highlightInfo.sq[0].y = fromY;\r
9562   highlightInfo.sq[1].x = toX;\r
9563   highlightInfo.sq[1].y = toY;\r
9564 }\r
9565 \r
9566 VOID\r
9567 ClearHighlights()\r
9568 {\r
9569   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9570     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9571 }\r
9572 \r
9573 VOID\r
9574 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9575 {\r
9576   premoveHighlightInfo.sq[0].x = fromX;\r
9577   premoveHighlightInfo.sq[0].y = fromY;\r
9578   premoveHighlightInfo.sq[1].x = toX;\r
9579   premoveHighlightInfo.sq[1].y = toY;\r
9580 }\r
9581 \r
9582 VOID\r
9583 ClearPremoveHighlights()\r
9584 {\r
9585   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9586     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9587 }\r
9588 \r
9589 VOID\r
9590 ShutDownFrontEnd()\r
9591 {\r
9592   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9593   DeleteClipboardTempFiles();\r
9594 }\r
9595 \r
9596 void\r
9597 BoardToTop()\r
9598 {\r
9599     if (IsIconic(hwndMain))\r
9600       ShowWindow(hwndMain, SW_RESTORE);\r
9601 \r
9602     SetActiveWindow(hwndMain);\r
9603 }\r
9604 \r
9605 /*\r
9606  * Prototypes for animation support routines\r
9607  */\r
9608 static void ScreenSquare(int column, int row, POINT * pt);\r
9609 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9610      POINT frames[], int * nFrames);\r
9611 \r
9612 \r
9613 void\r
9614 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
9615 {       // [HGM] atomic: animate blast wave\r
9616         int i;\r
9617 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
9618         explodeInfo.fromX = fromX;\r
9619         explodeInfo.fromY = fromY;\r
9620         explodeInfo.toX = toX;\r
9621         explodeInfo.toY = toY;\r
9622         for(i=1; i<nFrames; i++) {\r
9623             explodeInfo.radius = (i*180)/(nFrames-1);\r
9624             DrawPosition(FALSE, NULL);\r
9625             Sleep(appData.animSpeed);\r
9626         }\r
9627         explodeInfo.radius = 0;\r
9628         DrawPosition(TRUE, NULL);\r
9629 }\r
9630 \r
9631 #define kFactor 4\r
9632 \r
9633 void\r
9634 AnimateMove(board, fromX, fromY, toX, toY)\r
9635      Board board;\r
9636      int fromX;\r
9637      int fromY;\r
9638      int toX;\r
9639      int toY;\r
9640 {\r
9641   ChessSquare piece;\r
9642   POINT start, finish, mid;\r
9643   POINT frames[kFactor * 2 + 1];\r
9644   int nFrames, n;\r
9645 \r
9646   if (!appData.animate) return;\r
9647   if (doingSizing) return;\r
9648   if (fromY < 0 || fromX < 0) return;\r
9649   piece = board[fromY][fromX];\r
9650   if (piece >= EmptySquare) return;\r
9651 \r
9652   ScreenSquare(fromX, fromY, &start);\r
9653   ScreenSquare(toX, toY, &finish);\r
9654 \r
9655   /* All pieces except knights move in straight line */\r
9656   if (piece != WhiteKnight && piece != BlackKnight) {\r
9657     mid.x = start.x + (finish.x - start.x) / 2;\r
9658     mid.y = start.y + (finish.y - start.y) / 2;\r
9659   } else {\r
9660     /* Knight: make diagonal movement then straight */\r
9661     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9662        mid.x = start.x + (finish.x - start.x) / 2;\r
9663        mid.y = finish.y;\r
9664      } else {\r
9665        mid.x = finish.x;\r
9666        mid.y = start.y + (finish.y - start.y) / 2;\r
9667      }\r
9668   }\r
9669   \r
9670   /* Don't use as many frames for very short moves */\r
9671   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9672     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9673   else\r
9674     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9675 \r
9676   animInfo.from.x = fromX;\r
9677   animInfo.from.y = fromY;\r
9678   animInfo.to.x = toX;\r
9679   animInfo.to.y = toY;\r
9680   animInfo.lastpos = start;\r
9681   animInfo.piece = piece;\r
9682   for (n = 0; n < nFrames; n++) {\r
9683     animInfo.pos = frames[n];\r
9684     DrawPosition(FALSE, NULL);\r
9685     animInfo.lastpos = animInfo.pos;\r
9686     Sleep(appData.animSpeed);\r
9687   }\r
9688   animInfo.pos = finish;\r
9689   DrawPosition(FALSE, NULL);\r
9690   animInfo.piece = EmptySquare;\r
9691   if(gameInfo.variant == VariantAtomic && \r
9692      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
9693         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
9694 }\r
9695 \r
9696 /*      Convert board position to corner of screen rect and color       */\r
9697 \r
9698 static void\r
9699 ScreenSquare(column, row, pt)\r
9700      int column; int row; POINT * pt;\r
9701 {\r
9702   if (flipView) {\r
9703     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9704     pt->y = lineGap + row * (squareSize + lineGap);\r
9705   } else {\r
9706     pt->x = lineGap + column * (squareSize + lineGap);\r
9707     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9708   }\r
9709 }\r
9710 \r
9711 /*      Generate a series of frame coords from start->mid->finish.\r
9712         The movement rate doubles until the half way point is\r
9713         reached, then halves back down to the final destination,\r
9714         which gives a nice slow in/out effect. The algorithmn\r
9715         may seem to generate too many intermediates for short\r
9716         moves, but remember that the purpose is to attract the\r
9717         viewers attention to the piece about to be moved and\r
9718         then to where it ends up. Too few frames would be less\r
9719         noticeable.                                             */\r
9720 \r
9721 static void\r
9722 Tween(start, mid, finish, factor, frames, nFrames)\r
9723      POINT * start; POINT * mid;\r
9724      POINT * finish; int factor;\r
9725      POINT frames[]; int * nFrames;\r
9726 {\r
9727   int n, fraction = 1, count = 0;\r
9728 \r
9729   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9730   for (n = 0; n < factor; n++)\r
9731     fraction *= 2;\r
9732   for (n = 0; n < factor; n++) {\r
9733     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9734     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9735     count ++;\r
9736     fraction = fraction / 2;\r
9737   }\r
9738   \r
9739   /* Midpoint */\r
9740   frames[count] = *mid;\r
9741   count ++;\r
9742   \r
9743   /* Slow out, stepping 1/2, then 1/4, ... */\r
9744   fraction = 2;\r
9745   for (n = 0; n < factor; n++) {\r
9746     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9747     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9748     count ++;\r
9749     fraction = fraction * 2;\r
9750   }\r
9751   *nFrames = count;\r
9752 }\r
9753 \r
9754 void\r
9755 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9756 {\r
9757     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9758 \r
9759     EvalGraphSet( first, last, current, pvInfoList );\r
9760 }\r