Fix WinBoard compile errors
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts.\r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free\r
9  * Software Foundation, Inc.\r
10  *\r
11  * Enhancements Copyright 2005 Alessandro Scotti\r
12  *\r
13  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
14  * which was written and is copyrighted by Wayne Christopher.\r
15  *\r
16  * The following terms apply to Digital Equipment Corporation's copyright\r
17  * interest in XBoard:\r
18  * ------------------------------------------------------------------------\r
19  * All Rights Reserved\r
20  *\r
21  * Permission to use, copy, modify, and distribute this software and its\r
22  * documentation for any purpose and without fee is hereby granted,\r
23  * provided that the above copyright notice appear in all copies and that\r
24  * both that copyright notice and this permission notice appear in\r
25  * supporting documentation, and that the name of Digital not be\r
26  * used in advertising or publicity pertaining to distribution of the\r
27  * software without specific, written prior permission.\r
28  *\r
29  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
30  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
31  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
32  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
33  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
34  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
35  * SOFTWARE.\r
36  * ------------------------------------------------------------------------\r
37  *\r
38  * The following terms apply to the enhanced version of XBoard\r
39  * distributed by the Free Software Foundation:\r
40  * ------------------------------------------------------------------------\r
41  *\r
42  * GNU XBoard is free software: you can redistribute it and/or modify\r
43  * it under the terms of the GNU General Public License as published by\r
44  * the Free Software Foundation, either version 3 of the License, or (at\r
45  * your option) any later version.\r
46  *\r
47  * GNU XBoard is distributed in the hope that it will be useful, but\r
48  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
49  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
50  * General Public License for more details.\r
51  *\r
52  * You should have received a copy of the GNU General Public License\r
53  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
54  *\r
55  *------------------------------------------------------------------------\r
56  ** See the file ChangeLog for a revision history.  */\r
57 \r
58 #include "config.h"\r
59 \r
60 #include <windows.h>\r
61 #include <winuser.h>\r
62 #include <winsock.h>\r
63 #include <commctrl.h>\r
64 \r
65 #include <stdio.h>\r
66 #include <stdlib.h>\r
67 #include <time.h>\r
68 #include <malloc.h>\r
69 #include <sys/stat.h>\r
70 #include <fcntl.h>\r
71 #include <math.h>\r
72 #include <commdlg.h>\r
73 #include <dlgs.h>\r
74 #include <richedit.h>\r
75 #include <mmsystem.h>\r
76 #include <ctype.h>\r
77 #include <io.h>\r
78 \r
79 #if __GNUC__\r
80 #include <errno.h>\r
81 #include <string.h>\r
82 #endif\r
83 \r
84 #include "common.h"\r
85 #include "frontend.h"\r
86 #include "backend.h"\r
87 #include "winboard.h"\r
88 #include "moves.h"\r
89 #include "wclipbrd.h"\r
90 #include "woptions.h"\r
91 #include "wsockerr.h"\r
92 #include "defaults.h"\r
93 #include "help.h"\r
94 #include "wsnap.h"\r
95 \r
96 #define SLASH '/'\r
97 #define DATADIR "~~"\r
98 \r
99 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
100 \r
101   int myrandom(void);\r
102   void mysrandom(unsigned int seed);\r
103 \r
104 extern int whiteFlag, blackFlag;\r
105 Boolean flipClock = FALSE;\r
106 extern HANDLE chatHandle[];\r
107 extern enum ICS_TYPE ics_type;\r
108 \r
109 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
110 int  MyGetFullPathName P((char *name, char *fullname));\r
111 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
112 VOID NewVariantPopup(HWND hwnd);\r
113 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
114                    /*char*/int promoChar));\r
115 void DisplayMove P((int moveNumber));\r
116 void ChatPopUp P((char *s));\r
117 typedef struct {\r
118   ChessSquare piece;  \r
119   POINT pos;      /* window coordinates of current pos */\r
120   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
121   POINT from;     /* board coordinates of the piece's orig pos */\r
122   POINT to;       /* board coordinates of the piece's new pos */\r
123 } AnimInfo;\r
124 \r
125 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
126 \r
127 typedef struct {\r
128   POINT start;    /* window coordinates of start pos */\r
129   POINT pos;      /* window coordinates of current pos */\r
130   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
131   POINT from;     /* board coordinates of the piece's orig pos */\r
132   ChessSquare piece;\r
133 } DragInfo;\r
134 \r
135 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
136 \r
137 typedef struct {\r
138   POINT sq[2];    /* board coordinates of from, to squares */\r
139 } HighlightInfo;\r
140 \r
141 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
142 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
143 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
144 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
145 \r
146 typedef struct { // [HGM] atomic\r
147   int fromX, fromY, toX, toY, radius;\r
148 } ExplodeInfo;\r
149 \r
150 static ExplodeInfo explodeInfo;\r
151 \r
152 /* Window class names */\r
153 char szAppName[] = "WinBoard";\r
154 char szConsoleName[] = "WBConsole";\r
155 \r
156 /* Title bar text */\r
157 char szTitle[] = "WinBoard";\r
158 char szConsoleTitle[] = "I C S Interaction";\r
159 \r
160 char *programName;\r
161 char *settingsFileName;\r
162 Boolean saveSettingsOnExit;\r
163 char installDir[MSG_SIZ];\r
164 int errorExitStatus;\r
165 \r
166 BoardSize boardSize;\r
167 Boolean chessProgram;\r
168 //static int boardX, boardY;\r
169 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
170 int squareSize, lineGap, minorSize;\r
171 static int winW, winH;\r
172 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
173 static int logoHeight = 0;\r
174 static char messageText[MESSAGE_TEXT_MAX];\r
175 static int clockTimerEvent = 0;\r
176 static int loadGameTimerEvent = 0;\r
177 static int analysisTimerEvent = 0;\r
178 static DelayedEventCallback delayedTimerCallback;\r
179 static int delayedTimerEvent = 0;\r
180 static int buttonCount = 2;\r
181 char *icsTextMenuString;\r
182 char *icsNames;\r
183 char *firstChessProgramNames;\r
184 char *secondChessProgramNames;\r
185 \r
186 #define PALETTESIZE 256\r
187 \r
188 HINSTANCE hInst;          /* current instance */\r
189 Boolean alwaysOnTop = FALSE;\r
190 RECT boardRect;\r
191 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
192   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
193 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };\r
194 HPALETTE hPal;\r
195 ColorClass currentColorClass;\r
196 \r
197 static HWND savedHwnd;\r
198 HWND hCommPort = NULL;    /* currently open comm port */\r
199 static HWND hwndPause;    /* pause button */\r
200 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
201 static HBRUSH lightSquareBrush, darkSquareBrush,\r
202   blackSquareBrush, /* [HGM] for band between board and holdings */\r
203   explodeBrush,     /* [HGM] atomic */\r
204   markerBrush[8],   /* [HGM] markers */\r
205   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
206 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
207 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
208 static HPEN gridPen = NULL;\r
209 static HPEN highlightPen = NULL;\r
210 static HPEN premovePen = NULL;\r
211 static NPLOGPALETTE pLogPal;\r
212 static BOOL paletteChanged = FALSE;\r
213 static HICON iconWhite, iconBlack, iconCurrent;\r
214 static int doingSizing = FALSE;\r
215 static int lastSizing = 0;\r
216 static int prevStderrPort;\r
217 static HBITMAP userLogo;\r
218 \r
219 static HBITMAP liteBackTexture = NULL;\r
220 static HBITMAP darkBackTexture = NULL;\r
221 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
222 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
223 static int backTextureSquareSize = 0;\r
224 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
225 \r
226 #if __GNUC__ && !defined(_winmajor)\r
227 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
228 #else\r
229 \r
230 #if defined(_winmajor)\r
231 #define oldDialog (_winmajor < 4)\r
232 #else\r
233 #define oldDialog 0\r
234 #endif\r
235 #endif\r
236 \r
237 #define INTERNATIONAL\r
238 \r
239 #ifdef INTERNATIONAL\r
240 #  define _(s) T_(s)\r
241 #  define N_(s) s\r
242 #else\r
243 #  define _(s) s\r
244 #  define N_(s) s\r
245 #  define T_(s) s\r
246 #  define Translate(x, y)\r
247 #  define LoadLanguageFile(s)\r
248 #endif\r
249 \r
250 #ifdef INTERNATIONAL\r
251 \r
252 Boolean barbaric; // flag indicating if translation is needed\r
253 \r
254 // list of item numbers used in each dialog (used to alter language at run time)\r
255 \r
256 #define ABOUTBOX -1  /* not sure why these are needed */\r
257 #define ABOUTBOX2 -1\r
258 \r
259 int dialogItems[][42] = {\r
260 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
261 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
262   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
263 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
264   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds,\r
265   OPT_Ranget, IDOK, IDCANCEL }, \r
266 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
267   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
268 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
269 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
270   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
271 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
272 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
273   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
274 { ABOUTBOX2, IDC_ChessBoard }, \r
275 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
276   OPT_GameListClose, IDC_GameListDoFilter }, \r
277 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
278 { DLG_Error, IDOK }, \r
279 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
280   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
281 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
282 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
283   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
284   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
285 { DLG_IndexNumber, IDC_Index }, \r
286 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
287 { DLG_TypeInName, IDOK, IDCANCEL }, \r
288 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
289   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
290 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
291   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
292   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
293   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
294   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
295   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
296   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
297 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
298   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
299   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
300   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
301   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
302   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
303   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
304   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
305   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
306 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
307   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
308   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
309   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
310   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
311   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
312   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
313   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
314 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
315   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
316   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
317   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
318   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
319   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
320   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
321   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
322   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
323 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
324   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
325   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
326   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
327   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
328 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
329 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
330   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
331 { DLG_MoveHistory }, \r
332 { DLG_EvalGraph }, \r
333 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
334 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
335 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
336   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
337   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
338   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
339 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
340   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
341   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
342 { 0 }\r
343 };\r
344 \r
345 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
346 static int lastChecked;\r
347 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
348 extern int tinyLayout;\r
349 extern char * menuBarText[][10];\r
350 \r
351 void\r
352 LoadLanguageFile(char *name)\r
353 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
354     FILE *f;\r
355     int i=0, j=0, n=0, k;\r
356     char buf[MSG_SIZ];\r
357 \r
358     if(!name || name[0] == NULLCHAR) return;\r
359       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
360     appData.language = oldLanguage;\r
361     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
362     if((f = fopen(buf, "r")) == NULL) return;\r
363     while((k = fgetc(f)) != EOF) {\r
364         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
365         languageBuf[i] = k;\r
366         if(k == '\n') {\r
367             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
368                 char *p;\r
369                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
370                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
371                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
372                         english[j] = languageBuf + n + 1; *p = 0;\r
373                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
374 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
375                     }\r
376                 }\r
377             }\r
378             n = i + 1;\r
379         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
380             switch(k) {\r
381               case 'n': k = '\n'; break;\r
382               case 'r': k = '\r'; break;\r
383               case 't': k = '\t'; break;\r
384             }\r
385             languageBuf[--i] = k;\r
386         }\r
387         i++;\r
388     }\r
389     fclose(f);\r
390     barbaric = (j != 0);\r
391     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
392 }\r
393 \r
394 char *\r
395 T_(char *s)\r
396 {   // return the translation of the given string\r
397     // efficiency can be improved a lot...\r
398     int i=0;\r
399     static char buf[MSG_SIZ];\r
400 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
401     if(!barbaric) return s;\r
402     if(!s) return ""; // sanity\r
403     while(english[i]) {\r
404         if(!strcmp(s, english[i])) return foreign[i];\r
405         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
406             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
407             return buf;\r
408         }\r
409         i++;\r
410     }\r
411     return s;\r
412 }\r
413 \r
414 void\r
415 Translate(HWND hDlg, int dialogID)\r
416 {   // translate all text items in the given dialog\r
417     int i=0, j, k;\r
418     char buf[MSG_SIZ], *s;\r
419     if(!barbaric) return;\r
420     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
421     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
422     GetWindowText( hDlg, buf, MSG_SIZ );\r
423     s = T_(buf);\r
424     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
425     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
426         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
427         if(strlen(buf) == 0) continue;\r
428         s = T_(buf);\r
429         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
430     }\r
431 }\r
432 \r
433 HMENU\r
434 TranslateOneMenu(int i, HMENU subMenu)\r
435 {\r
436     int j;\r
437     static MENUITEMINFO info;\r
438 \r
439     info.cbSize = sizeof(MENUITEMINFO);\r
440     info.fMask = MIIM_STATE | MIIM_TYPE;\r
441           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
442             char buf[MSG_SIZ];\r
443             info.dwTypeData = buf;\r
444             info.cch = sizeof(buf);\r
445             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
446             if(i < 10) {\r
447                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
448                 else menuText[i][j] = strdup(buf); // remember original on first change\r
449             }\r
450             if(buf[0] == NULLCHAR) continue;\r
451             info.dwTypeData = T_(buf);\r
452             info.cch = strlen(buf)+1;\r
453             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
454           }\r
455     return subMenu;\r
456 }\r
457 \r
458 void\r
459 TranslateMenus(int addLanguage)\r
460 {\r
461     int i;\r
462     WIN32_FIND_DATA fileData;\r
463     HANDLE hFind;\r
464 #define IDM_English 1970\r
465     if(1) {\r
466         HMENU mainMenu = GetMenu(hwndMain);\r
467         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
468           HMENU subMenu = GetSubMenu(mainMenu, i);\r
469           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
470                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
471           TranslateOneMenu(i, subMenu);\r
472         }\r
473         DrawMenuBar(hwndMain);\r
474     }\r
475 \r
476     if(!addLanguage) return;\r
477     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
478         HMENU mainMenu = GetMenu(hwndMain);\r
479         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
480         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
481         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
482         i = 0; lastChecked = IDM_English;\r
483         do {\r
484             char *p, *q = fileData.cFileName;\r
485             int checkFlag = MF_UNCHECKED;\r
486             languageFile[i] = strdup(q);\r
487             if(barbaric && !strcmp(oldLanguage, q)) {\r
488                 checkFlag = MF_CHECKED;\r
489                 lastChecked = IDM_English + i + 1;\r
490                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
491             }\r
492             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
493             p = strstr(fileData.cFileName, ".lng");\r
494             if(p) *p = 0;\r
495             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
496         } while(FindNextFile(hFind, &fileData));\r
497         FindClose(hFind);\r
498     }\r
499 }\r
500 \r
501 #endif\r
502 \r
503 #define IDM_RecentEngines 3000\r
504 \r
505 void\r
506 RecentEngineMenu (char *s)\r
507 {\r
508     if(appData.icsActive) return;\r
509     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
510         HMENU mainMenu = GetMenu(hwndMain);\r
511         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
512         int i=IDM_RecentEngines;\r
513         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
514         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
515         while(*s) {\r
516           char *p = strchr(s, '\n');\r
517           if(p == NULL) return; // malformed!\r
518           *p = NULLCHAR;\r
519           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
520           *p = '\n';\r
521           s = p+1;\r
522         }\r
523     }\r
524 }\r
525 \r
526 \r
527 typedef struct {\r
528   char *name;\r
529   int squareSize;\r
530   int lineGap;\r
531   int smallLayout;\r
532   int tinyLayout;\r
533   int cliWidth, cliHeight;\r
534 } SizeInfo;\r
535 \r
536 SizeInfo sizeInfo[] = \r
537 {\r
538   { "tiny",     21, 0, 1, 2, 0, 0 },\r
539   { "teeny",    25, 1, 1, 2, 0, 0 },\r
540   { "dinky",    29, 1, 1, 2, 0, 0 },\r
541   { "petite",   33, 1, 1, 2, 0, 0 },\r
542   { "slim",     37, 2, 1, 1, 0, 0 },\r
543   { "small",    40, 2, 1, 1, 0, 0 },\r
544   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
545   { "middling", 49, 2, 0, 0, 0, 0 },\r
546   { "average",  54, 2, 0, 0, 0, 0 },\r
547   { "moderate", 58, 3, 0, 0, 0, 0 },\r
548   { "medium",   64, 3, 0, 0, 0, 0 },\r
549   { "bulky",    72, 3, 0, 0, 0, 0 },\r
550   { "large",    80, 3, 0, 0, 0, 0 },\r
551   { "big",      87, 3, 0, 0, 0, 0 },\r
552   { "huge",     95, 3, 0, 0, 0, 0 },\r
553   { "giant",    108, 3, 0, 0, 0, 0 },\r
554   { "colossal", 116, 4, 0, 0, 0, 0 },\r
555   { "titanic",  129, 4, 0, 0, 0, 0 },\r
556   { NULL, 0, 0, 0, 0, 0, 0 }\r
557 };\r
558 \r
559 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
560 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
561 {\r
562   { 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), MF(GAMELIST_FONT_ALL) },\r
563   { 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), MF(GAMELIST_FONT_ALL) },\r
564   { 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), MF(GAMELIST_FONT_ALL) },\r
565   { 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), MF(GAMELIST_FONT_ALL) },\r
566   { 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), MF(GAMELIST_FONT_ALL) },\r
567   { 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), MF(GAMELIST_FONT_ALL) },\r
568   { 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), MF(GAMELIST_FONT_ALL) },\r
569   { 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), MF(GAMELIST_FONT_ALL) },\r
570   { 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), MF(GAMELIST_FONT_ALL) },\r
571   { 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), MF(GAMELIST_FONT_ALL) },\r
572   { 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),  MF(GAMELIST_FONT_ALL) },\r
573   { 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),  MF(GAMELIST_FONT_ALL) },\r
574   { 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),  MF(GAMELIST_FONT_ALL) },\r
575   { 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),  MF(GAMELIST_FONT_ALL) },\r
576   { 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), MF(GAMELIST_FONT_ALL) },\r
577   { 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), MF(GAMELIST_FONT_ALL) },\r
578   { 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), MF (GAMELIST_FONT_ALL) },\r
579   { 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), MF(GAMELIST_FONT_ALL) },\r
580 };\r
581 \r
582 MyFont *font[NUM_SIZES][NUM_FONTS];\r
583 \r
584 typedef struct {\r
585   char *label;\r
586   int id;\r
587   HWND hwnd;\r
588   WNDPROC wndproc;\r
589 } MyButtonDesc;\r
590 \r
591 #define BUTTON_WIDTH (tinyLayout == 2 ? 16 : 32)\r
592 #define N_BUTTONS 5\r
593 \r
594 MyButtonDesc buttonDesc[N_BUTTONS] =\r
595 {\r
596   {"<<", IDM_ToStart, NULL, NULL},\r
597   {"<", IDM_Backward, NULL, NULL},\r
598   {"P", IDM_Pause, NULL, NULL},\r
599   {">", IDM_Forward, NULL, NULL},\r
600   {">>", IDM_ToEnd, NULL, NULL},\r
601 };\r
602 \r
603 int tinyLayout = 0, smallLayout = 0;\r
604 #define MENU_BAR_ITEMS 9\r
605 char *menuBarText[3][MENU_BAR_ITEMS+1] = {\r
606   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
607   { N_("&Fil"), N_("&Ed"), N_("&Vw"), N_("&Mod"), N_("&Act"), N_("E&ng"), N_("&Opt"), N_("&Hlp"), NULL },\r
608   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
609 };\r
610 \r
611 \r
612 MySound sounds[(int)NSoundClasses];\r
613 MyTextAttribs textAttribs[(int)NColorClasses];\r
614 \r
615 MyColorizeAttribs colorizeAttribs[] = {\r
616   { (COLORREF)0, 0, N_("Shout Text") },\r
617   { (COLORREF)0, 0, N_("SShout/CShout") },\r
618   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
619   { (COLORREF)0, 0, N_("Channel Text") },\r
620   { (COLORREF)0, 0, N_("Kibitz Text") },\r
621   { (COLORREF)0, 0, N_("Tell Text") },\r
622   { (COLORREF)0, 0, N_("Challenge Text") },\r
623   { (COLORREF)0, 0, N_("Request Text") },\r
624   { (COLORREF)0, 0, N_("Seek Text") },\r
625   { (COLORREF)0, 0, N_("Normal Text") },\r
626   { (COLORREF)0, 0, N_("None") }\r
627 };\r
628 \r
629 \r
630 \r
631 static char *commentTitle;\r
632 static char *commentText;\r
633 static int commentIndex;\r
634 static Boolean editComment = FALSE;\r
635 \r
636 \r
637 char errorTitle[MSG_SIZ];\r
638 char errorMessage[2*MSG_SIZ];\r
639 HWND errorDialog = NULL;\r
640 BOOLEAN moveErrorMessageUp = FALSE;\r
641 BOOLEAN consoleEcho = TRUE;\r
642 CHARFORMAT consoleCF;\r
643 COLORREF consoleBackgroundColor;\r
644 \r
645 char *programVersion;\r
646 \r
647 #define CPReal 1\r
648 #define CPComm 2\r
649 #define CPSock 3\r
650 #define CPRcmd 4\r
651 typedef int CPKind;\r
652 \r
653 typedef struct {\r
654   CPKind kind;\r
655   HANDLE hProcess;\r
656   DWORD pid;\r
657   HANDLE hTo;\r
658   HANDLE hFrom;\r
659   SOCKET sock;\r
660   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
661 } ChildProc;\r
662 \r
663 #define INPUT_SOURCE_BUF_SIZE 4096\r
664 \r
665 typedef struct _InputSource {\r
666   CPKind kind;\r
667   HANDLE hFile;\r
668   SOCKET sock;\r
669   int lineByLine;\r
670   HANDLE hThread;\r
671   DWORD id;\r
672   char buf[INPUT_SOURCE_BUF_SIZE];\r
673   char *next;\r
674   DWORD count;\r
675   int error;\r
676   InputCallback func;\r
677   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
678   VOIDSTAR closure;\r
679 } InputSource;\r
680 \r
681 InputSource *consoleInputSource;\r
682 \r
683 DCB dcb;\r
684 \r
685 /* forward */\r
686 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
687 VOID ConsoleCreate();\r
688 LRESULT CALLBACK\r
689   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
690 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
691 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
692 VOID ParseCommSettings(char *arg, DCB *dcb);\r
693 LRESULT CALLBACK\r
694   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
695 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
696 void ParseIcsTextMenu(char *icsTextMenuString);\r
697 VOID PopUpNameDialog(char firstchar);\r
698 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
699 \r
700 /* [AS] */\r
701 int NewGameFRC();\r
702 int GameListOptions();\r
703 \r
704 int dummy; // [HGM] for obsolete args\r
705 \r
706 HWND hwndMain = NULL;        /* root window*/\r
707 HWND hwndConsole = NULL;\r
708 HWND commentDialog = NULL;\r
709 HWND moveHistoryDialog = NULL;\r
710 HWND evalGraphDialog = NULL;\r
711 HWND engineOutputDialog = NULL;\r
712 HWND gameListDialog = NULL;\r
713 HWND editTagsDialog = NULL;\r
714 \r
715 int commentUp = FALSE;\r
716 \r
717 WindowPlacement wpMain;\r
718 WindowPlacement wpConsole;\r
719 WindowPlacement wpComment;\r
720 WindowPlacement wpMoveHistory;\r
721 WindowPlacement wpEvalGraph;\r
722 WindowPlacement wpEngineOutput;\r
723 WindowPlacement wpGameList;\r
724 WindowPlacement wpTags;\r
725 \r
726 VOID EngineOptionsPopup(); // [HGM] settings\r
727 \r
728 VOID GothicPopUp(char *title, VariantClass variant);\r
729 /*\r
730  * Setting "frozen" should disable all user input other than deleting\r
731  * the window.  We do this while engines are initializing themselves.\r
732  */\r
733 static int frozen = 0;\r
734 static int oldMenuItemState[MENU_BAR_ITEMS];\r
735 void FreezeUI()\r
736 {\r
737   HMENU hmenu;\r
738   int i;\r
739 \r
740   if (frozen) return;\r
741   frozen = 1;\r
742   hmenu = GetMenu(hwndMain);\r
743   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
744     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
745   }\r
746   DrawMenuBar(hwndMain);\r
747 }\r
748 \r
749 /* Undo a FreezeUI */\r
750 void ThawUI()\r
751 {\r
752   HMENU hmenu;\r
753   int i;\r
754 \r
755   if (!frozen) return;\r
756   frozen = 0;\r
757   hmenu = GetMenu(hwndMain);\r
758   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
759     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
760   }\r
761   DrawMenuBar(hwndMain);\r
762 }\r
763 \r
764 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
765 \r
766 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
767 #ifdef JAWS\r
768 #include "jaws.c"\r
769 #else\r
770 #define JAWS_INIT\r
771 #define JAWS_ARGS\r
772 #define JAWS_ALT_INTERCEPT\r
773 #define JAWS_KBUP_NAVIGATION\r
774 #define JAWS_KBDOWN_NAVIGATION\r
775 #define JAWS_MENU_ITEMS\r
776 #define JAWS_SILENCE\r
777 #define JAWS_REPLAY\r
778 #define JAWS_ACCEL\r
779 #define JAWS_COPYRIGHT\r
780 #define JAWS_DELETE(X) X\r
781 #define SAYMACHINEMOVE()\r
782 #define SAY(X)\r
783 #endif\r
784 \r
785 /*---------------------------------------------------------------------------*\\r
786  *\r
787  * WinMain\r
788  *\r
789 \*---------------------------------------------------------------------------*/\r
790 \r
791 static void HandleMessage P((MSG *message));\r
792 static HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
793 \r
794 int APIENTRY\r
795 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
796         LPSTR lpCmdLine, int nCmdShow)\r
797 {\r
798   MSG msg;\r
799 //  INITCOMMONCONTROLSEX ex;\r
800 \r
801   debugFP = stderr;\r
802 \r
803   LoadLibrary("RICHED32.DLL");\r
804   consoleCF.cbSize = sizeof(CHARFORMAT);\r
805 \r
806   if (!InitApplication(hInstance)) {\r
807     return (FALSE);\r
808   }\r
809   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
810     return (FALSE);\r
811   }\r
812 \r
813   JAWS_INIT\r
814   TranslateMenus(1);\r
815 \r
816 //  InitCommonControlsEx(&ex);\r
817   InitCommonControls();\r
818 \r
819   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
820   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
821   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
822 \r
823   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
824 \r
825   while (GetMessage(&msg, /* message structure */\r
826                     NULL, /* handle of window receiving the message */\r
827                     0,    /* lowest message to examine */\r
828                     0))   /* highest message to examine */\r
829     {\r
830         HandleMessage(&msg);\r
831     }\r
832 \r
833 \r
834   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
835 }\r
836 \r
837 static void\r
838 HandleMessage (MSG *message)\r
839 {\r
840     MSG msg = *message;\r
841 \r
842       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
843         // [HGM] navigate: switch between all windows with tab\r
844         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
845         int i, currentElement = 0;\r
846 \r
847         // first determine what element of the chain we come from (if any)\r
848         if(appData.icsActive) {\r
849             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
850             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
851         }\r
852         if(engineOutputDialog && EngineOutputIsUp()) {\r
853             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
854             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
855         }\r
856         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
857             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
858         }\r
859         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
860         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
861         if(msg.hwnd == e1)                 currentElement = 2; else\r
862         if(msg.hwnd == e2)                 currentElement = 3; else\r
863         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
864         if(msg.hwnd == mh)                currentElement = 4; else\r
865         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
866         if(msg.hwnd == hText)  currentElement = 5; else\r
867         if(msg.hwnd == hInput) currentElement = 6; else\r
868         for (i = 0; i < N_BUTTONS; i++) {\r
869             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
870         }\r
871 \r
872         // determine where to go to\r
873         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
874           do {\r
875             currentElement = (currentElement + direction) % 7;\r
876             switch(currentElement) {\r
877                 case 0:\r
878                   h = hwndMain; break; // passing this case always makes the loop exit\r
879                 case 1:\r
880                   h = buttonDesc[0].hwnd; break; // could be NULL\r
881                 case 2:\r
882                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
883                   h = e1; break;\r
884                 case 3:\r
885                   if(!EngineOutputIsUp()) continue;\r
886                   h = e2; break;\r
887                 case 4:\r
888                   if(!MoveHistoryIsUp()) continue;\r
889                   h = mh; break;\r
890 //              case 6: // input to eval graph does not seem to get here!\r
891 //                if(!EvalGraphIsUp()) continue;\r
892 //                h = evalGraphDialog; break;\r
893                 case 5:\r
894                   if(!appData.icsActive) continue;\r
895                   SAY("display");\r
896                   h = hText; break;\r
897                 case 6:\r
898                   if(!appData.icsActive) continue;\r
899                   SAY("input");\r
900                   h = hInput; break;\r
901             }\r
902           } while(h == 0);\r
903 \r
904           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
905           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
906           SetFocus(h);\r
907 \r
908           return; // this message now has been processed\r
909         }\r
910       }\r
911 \r
912       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
913           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
914           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
915           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
916           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
917           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
918           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
919           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
920           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
921           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
922         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
923         for(i=0; i<MAX_CHAT; i++) \r
924             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
925                 done = 1; break;\r
926         }\r
927         if(done) return; // [HGM] chat: end patch\r
928         TranslateMessage(&msg); /* Translates virtual key codes */\r
929         DispatchMessage(&msg);  /* Dispatches message to window */\r
930       }\r
931 }\r
932 \r
933 void\r
934 DoEvents ()\r
935 { /* Dispatch pending messages */\r
936   MSG msg;\r
937   while (PeekMessage(&msg, /* message structure */\r
938                      NULL, /* handle of window receiving the message */\r
939                      0,    /* lowest message to examine */\r
940                      0,    /* highest message to examine */\r
941                      PM_REMOVE))\r
942     {\r
943         HandleMessage(&msg);\r
944     }\r
945 }\r
946 \r
947 /*---------------------------------------------------------------------------*\\r
948  *\r
949  * Initialization functions\r
950  *\r
951 \*---------------------------------------------------------------------------*/\r
952 \r
953 void\r
954 SetUserLogo()\r
955 {   // update user logo if necessary\r
956     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
957 \r
958     if(appData.autoLogo) {\r
959           curName = UserName();\r
960           if(strcmp(curName, oldUserName)) {\r
961                 GetCurrentDirectory(MSG_SIZ, dir);\r
962                 SetCurrentDirectory(installDir);\r
963                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
964                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
965                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
966                 if(userLogo == NULL)\r
967                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
968                 SetCurrentDirectory(dir); /* return to prev directory */\r
969           }\r
970     }\r
971 }\r
972 \r
973 BOOL\r
974 InitApplication(HINSTANCE hInstance)\r
975 {\r
976   WNDCLASS wc;\r
977 \r
978   /* Fill in window class structure with parameters that describe the */\r
979   /* main window. */\r
980 \r
981   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
982   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
983   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
984   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
985   wc.hInstance     = hInstance;         /* Owner of this class */\r
986   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
987   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
988   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
989   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
990   wc.lpszClassName = szAppName;                 /* Name to register as */\r
991 \r
992   /* Register the window class and return success/failure code. */\r
993   if (!RegisterClass(&wc)) return FALSE;\r
994 \r
995   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
996   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
997   wc.cbClsExtra    = 0;\r
998   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
999   wc.hInstance     = hInstance;\r
1000   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
1001   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
1002   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
1003   wc.lpszMenuName  = NULL;\r
1004   wc.lpszClassName = szConsoleName;\r
1005 \r
1006   if (!RegisterClass(&wc)) return FALSE;\r
1007   return TRUE;\r
1008 }\r
1009 \r
1010 \r
1011 /* Set by InitInstance, used by EnsureOnScreen */\r
1012 int screenHeight, screenWidth;\r
1013 RECT screenGeometry;\r
1014 \r
1015 void\r
1016 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
1017 {\r
1018 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
1019   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
1020   if (*x > screenGeometry.right - 32) *x = screenGeometry.left;\r
1021   if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;\r
1022   if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;\r
1023   if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;\r
1024 }\r
1025 \r
1026 VOID\r
1027 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
1028 {\r
1029   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1030   GetCurrentDirectory(MSG_SIZ, dir);\r
1031   SetCurrentDirectory(installDir);\r
1032   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1033       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1034 \r
1035       if (cps->programLogo == NULL && appData.debugMode) {\r
1036           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1037       }\r
1038   } else if(appData.autoLogo) {\r
1039       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1040         char *opponent = "";\r
1041         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1042         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1043         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1044         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1045             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1046             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1047         }\r
1048       } else\r
1049       if(appData.directory[n] && appData.directory[n][0]) {\r
1050         SetCurrentDirectory(appData.directory[n]);\r
1051         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1052       }\r
1053   }\r
1054   SetCurrentDirectory(dir); /* return to prev directory */\r
1055 }\r
1056 \r
1057 VOID\r
1058 InitTextures()\r
1059 {\r
1060   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1061   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1062   \r
1063   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1064       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1065       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1066       liteBackTextureMode = appData.liteBackTextureMode;\r
1067 \r
1068       if (liteBackTexture == NULL && appData.debugMode) {\r
1069           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1070       }\r
1071   }\r
1072   \r
1073   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1074       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1075       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1076       darkBackTextureMode = appData.darkBackTextureMode;\r
1077 \r
1078       if (darkBackTexture == NULL && appData.debugMode) {\r
1079           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1080       }\r
1081   }\r
1082 }\r
1083 \r
1084 #ifndef SM_CXVIRTUALSCREEN\r
1085 #define SM_CXVIRTUALSCREEN 78\r
1086 #endif\r
1087 #ifndef SM_CYVIRTUALSCREEN\r
1088 #define SM_CYVIRTUALSCREEN 79\r
1089 #endif\r
1090 #ifndef SM_XVIRTUALSCREEN \r
1091 #define SM_XVIRTUALSCREEN 76\r
1092 #endif\r
1093 #ifndef SM_YVIRTUALSCREEN \r
1094 #define SM_YVIRTUALSCREEN 77\r
1095 #endif\r
1096 \r
1097 VOID\r
1098 InitGeometry()\r
1099 {\r
1100   screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);\r
1101   if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1102   screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);\r
1103   if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1104   screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);\r
1105   screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);\r
1106   screenGeometry.right = screenGeometry.left + screenWidth;\r
1107   screenGeometry.bottom = screenGeometry.top + screenHeight;\r
1108 }\r
1109 \r
1110 ChessProgramState broadcast;\r
1111 \r
1112 BOOL\r
1113 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1114 {\r
1115   HWND hwnd; /* Main window handle. */\r
1116   int ibs;\r
1117   WINDOWPLACEMENT wp;\r
1118   char *filepart;\r
1119 \r
1120   hInst = hInstance;    /* Store instance handle in our global variable */\r
1121   programName = szAppName;\r
1122 \r
1123   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1124     *filepart = NULLCHAR;\r
1125     SetCurrentDirectory(installDir);\r
1126   } else {\r
1127     GetCurrentDirectory(MSG_SIZ, installDir);\r
1128   }\r
1129   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1130   InitGeometry();\r
1131   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1132   /* xboard, and older WinBoards, controlled the move sound with the\r
1133      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1134      always turn the option on (so that the backend will call us),\r
1135      then let the user turn the sound off by setting it to silence if\r
1136      desired.  To accommodate old winboard.ini files saved by old\r
1137      versions of WinBoard, we also turn off the sound if the option\r
1138      was initially set to false. [HGM] taken out of InitAppData */\r
1139   if (!appData.ringBellAfterMoves) {\r
1140     sounds[(int)SoundMove].name = strdup("");\r
1141     appData.ringBellAfterMoves = TRUE;\r
1142   }\r
1143   if (appData.debugMode) {\r
1144     char *c = appData.nameOfDebugFile;\r
1145     if(strstr(c, "///") == c) {\r
1146       broadcast.which = "broadcaster";\r
1147       broadcast.pr   = NoProc;\r
1148       broadcast.isr  = NULL;\r
1149       broadcast.program = c + 3;\r
1150       broadcast.dir  = ".";\r
1151       broadcast.host = "localhost";\r
1152       StartChessProgram(&broadcast);\r
1153       debugFP = (FILE*) _fdopen(_open_osfhandle((long)(((ChildProc*)(broadcast.pr))->hTo), _O_WRONLY), "w");\r
1154     } else\r
1155     debugFP = fopen(c, "w");\r
1156     setbuf(debugFP, NULL);\r
1157   }\r
1158 \r
1159   LoadLanguageFile(appData.language);\r
1160 \r
1161   InitBackEnd1();\r
1162 \r
1163 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1164 //  InitEngineUCI( installDir, &second );\r
1165 \r
1166   /* Create a main window for this application instance. */\r
1167   hwnd = CreateWindow(szAppName, szTitle,\r
1168                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1169                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1170                       NULL, NULL, hInstance, NULL);\r
1171   hwndMain = hwnd;\r
1172 \r
1173   /* If window could not be created, return "failure" */\r
1174   if (!hwnd) {\r
1175     return (FALSE);\r
1176   }\r
1177 \r
1178   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1179   LoadLogo(&first, 0, FALSE);\r
1180   LoadLogo(&second, 1, appData.icsActive);\r
1181 \r
1182   SetUserLogo();\r
1183 \r
1184   iconWhite = LoadIcon(hInstance, "icon_white");\r
1185   iconBlack = LoadIcon(hInstance, "icon_black");\r
1186   iconCurrent = iconWhite;\r
1187   InitDrawingColors();\r
1188 \r
1189   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1190   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1191     /* Compute window size for each board size, and use the largest\r
1192        size that fits on this screen as the default. */\r
1193     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1194     if (boardSize == (BoardSize)-1 &&\r
1195         winH <= screenHeight\r
1196            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1197         && winW <= screenWidth) {\r
1198       boardSize = (BoardSize)ibs;\r
1199     }\r
1200   }\r
1201 \r
1202   InitDrawingSizes(boardSize, 0);\r
1203   RecentEngineMenu(appData.recentEngineList);\r
1204   InitMenuChecks();\r
1205   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1206 \r
1207   /* [AS] Load textures if specified */\r
1208   InitTextures();\r
1209 \r
1210   mysrandom( (unsigned) time(NULL) );\r
1211 \r
1212   /* [AS] Restore layout */\r
1213   if( wpMoveHistory.visible ) {\r
1214       MoveHistoryPopUp();\r
1215   }\r
1216 \r
1217   if( wpEvalGraph.visible ) {\r
1218       EvalGraphPopUp();\r
1219   }\r
1220 \r
1221   if( wpEngineOutput.visible ) {\r
1222       EngineOutputPopUp();\r
1223   }\r
1224 \r
1225   /* Make the window visible; update its client area; and return "success" */\r
1226   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1227   wp.length = sizeof(WINDOWPLACEMENT);\r
1228   wp.flags = 0;\r
1229   wp.showCmd = nCmdShow;\r
1230   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1231   wp.rcNormalPosition.left = wpMain.x;\r
1232   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1233   wp.rcNormalPosition.top = wpMain.y;\r
1234   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1235   SetWindowPlacement(hwndMain, &wp);\r
1236 \r
1237   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1238 \r
1239   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1240                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1241 \r
1242   if (hwndConsole) {\r
1243 #if AOT_CONSOLE\r
1244     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1245                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1246 #endif\r
1247     ShowWindow(hwndConsole, nCmdShow);\r
1248     SetActiveWindow(hwndConsole);\r
1249   }\r
1250   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1251   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1252 \r
1253   return TRUE;\r
1254 \r
1255 }\r
1256 \r
1257 VOID\r
1258 InitMenuChecks()\r
1259 {\r
1260   HMENU hmenu = GetMenu(hwndMain);\r
1261 \r
1262   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1263                         MF_BYCOMMAND|((appData.icsActive &&\r
1264                                        *appData.icsCommPort != NULLCHAR) ?\r
1265                                       MF_ENABLED : MF_GRAYED));\r
1266   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1267                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1268                                      MF_CHECKED : MF_UNCHECKED));\r
1269   EnableMenuItem(hmenu, IDM_SaveSelected, MF_GRAYED);\r
1270 }\r
1271 \r
1272 //---------------------------------------------------------------------------------------------------------\r
1273 \r
1274 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1275 #define XBOARD FALSE\r
1276 \r
1277 #define OPTCHAR "/"\r
1278 #define SEPCHAR "="\r
1279 #define TOPLEVEL 0\r
1280 \r
1281 #include "args.h"\r
1282 \r
1283 // front-end part of option handling\r
1284 \r
1285 VOID\r
1286 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1287 {\r
1288   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1289   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1290   DeleteDC(hdc);\r
1291   lf->lfWidth = 0;\r
1292   lf->lfEscapement = 0;\r
1293   lf->lfOrientation = 0;\r
1294   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1295   lf->lfItalic = mfp->italic;\r
1296   lf->lfUnderline = mfp->underline;\r
1297   lf->lfStrikeOut = mfp->strikeout;\r
1298   lf->lfCharSet = mfp->charset;\r
1299   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1300 \r
1301 \r
1302 \r
1303   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1304   lf->lfQuality = DEFAULT_QUALITY;\r
1305   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1306     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1307 }\r
1308 \r
1309 void\r
1310 CreateFontInMF(MyFont *mf)\r
1311\r
1312   LFfromMFP(&mf->lf, &mf->mfp);\r
1313   if (mf->hf) DeleteObject(mf->hf);\r
1314   mf->hf = CreateFontIndirect(&mf->lf);\r
1315 }\r
1316 \r
1317 // [HGM] This platform-dependent table provides the location for storing the color info\r
1318 void *\r
1319 colorVariable[] = {\r
1320   &whitePieceColor, \r
1321   &blackPieceColor, \r
1322   &lightSquareColor,\r
1323   &darkSquareColor, \r
1324   &highlightSquareColor,\r
1325   &premoveHighlightColor,\r
1326   NULL,\r
1327   &consoleBackgroundColor,\r
1328   &appData.fontForeColorWhite,\r
1329   &appData.fontBackColorWhite,\r
1330   &appData.fontForeColorBlack,\r
1331   &appData.fontBackColorBlack,\r
1332   &appData.evalHistColorWhite,\r
1333   &appData.evalHistColorBlack,\r
1334   &appData.highlightArrowColor,\r
1335 };\r
1336 \r
1337 /* Command line font name parser.  NULL name means do nothing.\r
1338    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1339    For backward compatibility, syntax without the colon is also\r
1340    accepted, but font names with digits in them won't work in that case.\r
1341 */\r
1342 VOID\r
1343 ParseFontName(char *name, MyFontParams *mfp)\r
1344 {\r
1345   char *p, *q;\r
1346   if (name == NULL) return;\r
1347   p = name;\r
1348   q = strchr(p, ':');\r
1349   if (q) {\r
1350     if (q - p >= sizeof(mfp->faceName))\r
1351       ExitArgError(_("Font name too long:"), name, TRUE);\r
1352     memcpy(mfp->faceName, p, q - p);\r
1353     mfp->faceName[q - p] = NULLCHAR;\r
1354     p = q + 1;\r
1355   } else {\r
1356     q = mfp->faceName;\r
1357 \r
1358     while (*p && !isdigit(*p)) {\r
1359       *q++ = *p++;\r
1360       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1361         ExitArgError(_("Font name too long:"), name, TRUE);\r
1362     }\r
1363     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1364     *q = NULLCHAR;\r
1365   }\r
1366   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1367   mfp->pointSize = (float) atof(p);\r
1368   mfp->bold = (strchr(p, 'b') != NULL);\r
1369   mfp->italic = (strchr(p, 'i') != NULL);\r
1370   mfp->underline = (strchr(p, 'u') != NULL);\r
1371   mfp->strikeout = (strchr(p, 's') != NULL);\r
1372   mfp->charset = DEFAULT_CHARSET;\r
1373   q = strchr(p, 'c');\r
1374   if (q)\r
1375     mfp->charset = (BYTE) atoi(q+1);\r
1376 }\r
1377 \r
1378 void\r
1379 ParseFont(char *name, int number)\r
1380 { // wrapper to shield back-end from 'font'\r
1381   ParseFontName(name, &font[boardSize][number]->mfp);\r
1382 }\r
1383 \r
1384 void\r
1385 SetFontDefaults()\r
1386 { // in WB  we have a 2D array of fonts; this initializes their description\r
1387   int i, j;\r
1388   /* Point font array elements to structures and\r
1389      parse default font names */\r
1390   for (i=0; i<NUM_FONTS; i++) {\r
1391     for (j=0; j<NUM_SIZES; j++) {\r
1392       font[j][i] = &fontRec[j][i];\r
1393       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1394     }\r
1395   }\r
1396 }\r
1397 \r
1398 void\r
1399 CreateFonts()\r
1400 { // here we create the actual fonts from the selected descriptions\r
1401   int i, j;\r
1402   for (i=0; i<NUM_FONTS; i++) {\r
1403     for (j=0; j<NUM_SIZES; j++) {\r
1404       CreateFontInMF(font[j][i]);\r
1405     }\r
1406   }\r
1407 }\r
1408 /* Color name parser.\r
1409    X version accepts X color names, but this one\r
1410    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1411 COLORREF\r
1412 ParseColorName(char *name)\r
1413 {\r
1414   int red, green, blue, count;\r
1415   char buf[MSG_SIZ];\r
1416 \r
1417   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1418   if (count != 3) {\r
1419     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1420       &red, &green, &blue);\r
1421   }\r
1422   if (count != 3) {\r
1423     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1424     DisplayError(buf, 0);\r
1425     return RGB(0, 0, 0);\r
1426   }\r
1427   return PALETTERGB(red, green, blue);\r
1428 }\r
1429 \r
1430 void\r
1431 ParseColor(int n, char *name)\r
1432 { // for WinBoard the color is an int, which needs to be derived from the string\r
1433   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1434 }\r
1435 \r
1436 void\r
1437 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1438 {\r
1439   char *e = argValue;\r
1440   int eff = 0;\r
1441 \r
1442   while (*e) {\r
1443     if (*e == 'b')      eff |= CFE_BOLD;\r
1444     else if (*e == 'i') eff |= CFE_ITALIC;\r
1445     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1446     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1447     else if (*e == '#' || isdigit(*e)) break;\r
1448     e++;\r
1449   }\r
1450   *effects = eff;\r
1451   *color   = ParseColorName(e);\r
1452 }\r
1453 \r
1454 void\r
1455 ParseTextAttribs(ColorClass cc, char *s)\r
1456 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1457     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1458     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1459 }\r
1460 \r
1461 void\r
1462 ParseBoardSize(void *addr, char *name)\r
1463 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1464   BoardSize bs = SizeTiny;\r
1465   while (sizeInfo[bs].name != NULL) {\r
1466     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1467         *(BoardSize *)addr = bs;\r
1468         return;\r
1469     }\r
1470     bs++;\r
1471   }\r
1472   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1473 }\r
1474 \r
1475 void\r
1476 LoadAllSounds()\r
1477 { // [HGM] import name from appData first\r
1478   ColorClass cc;\r
1479   SoundClass sc;\r
1480   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1481     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1482     textAttribs[cc].sound.data = NULL;\r
1483     MyLoadSound(&textAttribs[cc].sound);\r
1484   }\r
1485   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1486     textAttribs[cc].sound.name = strdup("");\r
1487     textAttribs[cc].sound.data = NULL;\r
1488   }\r
1489   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1490     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1491     sounds[sc].data = NULL;\r
1492     MyLoadSound(&sounds[sc]);\r
1493   }\r
1494 }\r
1495 \r
1496 void\r
1497 SetCommPortDefaults()\r
1498 {\r
1499    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1500   dcb.DCBlength = sizeof(DCB);\r
1501   dcb.BaudRate = 9600;\r
1502   dcb.fBinary = TRUE;\r
1503   dcb.fParity = FALSE;\r
1504   dcb.fOutxCtsFlow = FALSE;\r
1505   dcb.fOutxDsrFlow = FALSE;\r
1506   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1507   dcb.fDsrSensitivity = FALSE;\r
1508   dcb.fTXContinueOnXoff = TRUE;\r
1509   dcb.fOutX = FALSE;\r
1510   dcb.fInX = FALSE;\r
1511   dcb.fNull = FALSE;\r
1512   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1513   dcb.fAbortOnError = FALSE;\r
1514   dcb.ByteSize = 7;\r
1515   dcb.Parity = SPACEPARITY;\r
1516   dcb.StopBits = ONESTOPBIT;\r
1517 }\r
1518 \r
1519 // [HGM] args: these three cases taken out to stay in front-end\r
1520 void\r
1521 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1522 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1523         // while the curent board size determines the element. This system should be ported to XBoard.\r
1524         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1525         int bs;\r
1526         for (bs=0; bs<NUM_SIZES; bs++) {\r
1527           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1528           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1529           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1530             ad->argName, mfp->faceName, mfp->pointSize,\r
1531             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1532             mfp->bold ? "b" : "",\r
1533             mfp->italic ? "i" : "",\r
1534             mfp->underline ? "u" : "",\r
1535             mfp->strikeout ? "s" : "",\r
1536             (int)mfp->charset);\r
1537         }\r
1538       }\r
1539 \r
1540 void\r
1541 ExportSounds()\r
1542 { // [HGM] copy the names from the internal WB variables to appData\r
1543   ColorClass cc;\r
1544   SoundClass sc;\r
1545   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1546     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1547   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1548     (&appData.soundMove)[sc] = sounds[sc].name;\r
1549 }\r
1550 \r
1551 void\r
1552 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1553 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1554         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1555         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1556           (ta->effects & CFE_BOLD) ? "b" : "",\r
1557           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1558           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1559           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1560           (ta->effects) ? " " : "",\r
1561           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1562       }\r
1563 \r
1564 void\r
1565 SaveColor(FILE *f, ArgDescriptor *ad)\r
1566 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1567         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1568         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1569           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1570 }\r
1571 \r
1572 void\r
1573 SaveBoardSize(FILE *f, char *name, void *addr)\r
1574 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1575   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1576 }\r
1577 \r
1578 void\r
1579 ParseCommPortSettings(char *s)\r
1580 { // wrapper to keep dcb from back-end\r
1581   ParseCommSettings(s, &dcb);\r
1582 }\r
1583 \r
1584 void\r
1585 GetWindowCoords()\r
1586 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1587   GetActualPlacement(hwndMain, &wpMain);\r
1588   GetActualPlacement(hwndConsole, &wpConsole);\r
1589   GetActualPlacement(commentDialog, &wpComment);\r
1590   GetActualPlacement(editTagsDialog, &wpTags);\r
1591   GetActualPlacement(gameListDialog, &wpGameList);\r
1592   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1593   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1594   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1595 }\r
1596 \r
1597 void\r
1598 PrintCommPortSettings(FILE *f, char *name)\r
1599 { // wrapper to shield back-end from DCB\r
1600       PrintCommSettings(f, name, &dcb);\r
1601 }\r
1602 \r
1603 int\r
1604 MySearchPath(char *installDir, char *name, char *fullname)\r
1605 {\r
1606   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1607   if(name[0]== '%') {\r
1608     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1609     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1610       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1611       *strchr(buf, '%') = 0;\r
1612       strcat(fullname, getenv(buf));\r
1613       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1614     }\r
1615     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1616     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1617     return (int) strlen(fullname);\r
1618   }\r
1619   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1620 }\r
1621 \r
1622 int\r
1623 MyGetFullPathName(char *name, char *fullname)\r
1624 {\r
1625   char *dummy;\r
1626   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1627 }\r
1628 \r
1629 int\r
1630 MainWindowUp()\r
1631 { // [HGM] args: allows testing if main window is realized from back-end\r
1632   return hwndMain != NULL;\r
1633 }\r
1634 \r
1635 void\r
1636 PopUpStartupDialog()\r
1637 {\r
1638     FARPROC lpProc;\r
1639     \r
1640     LoadLanguageFile(appData.language);\r
1641     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1642     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1643     FreeProcInstance(lpProc);\r
1644 }\r
1645 \r
1646 /*---------------------------------------------------------------------------*\\r
1647  *\r
1648  * GDI board drawing routines\r
1649  *\r
1650 \*---------------------------------------------------------------------------*/\r
1651 \r
1652 /* [AS] Draw square using background texture */\r
1653 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1654 {\r
1655     XFORM   x;\r
1656 \r
1657     if( mode == 0 ) {\r
1658         return; /* Should never happen! */\r
1659     }\r
1660 \r
1661     SetGraphicsMode( dst, GM_ADVANCED );\r
1662 \r
1663     switch( mode ) {\r
1664     case 1:\r
1665         /* Identity */\r
1666         break;\r
1667     case 2:\r
1668         /* X reflection */\r
1669         x.eM11 = -1.0;\r
1670         x.eM12 = 0;\r
1671         x.eM21 = 0;\r
1672         x.eM22 = 1.0;\r
1673         x.eDx = (FLOAT) dw + dx - 1;\r
1674         x.eDy = 0;\r
1675         dx = 0;\r
1676         SetWorldTransform( dst, &x );\r
1677         break;\r
1678     case 3:\r
1679         /* Y reflection */\r
1680         x.eM11 = 1.0;\r
1681         x.eM12 = 0;\r
1682         x.eM21 = 0;\r
1683         x.eM22 = -1.0;\r
1684         x.eDx = 0;\r
1685         x.eDy = (FLOAT) dh + dy - 1;\r
1686         dy = 0;\r
1687         SetWorldTransform( dst, &x );\r
1688         break;\r
1689     case 4:\r
1690         /* X/Y flip */\r
1691         x.eM11 = 0;\r
1692         x.eM12 = 1.0;\r
1693         x.eM21 = 1.0;\r
1694         x.eM22 = 0;\r
1695         x.eDx = (FLOAT) dx;\r
1696         x.eDy = (FLOAT) dy;\r
1697         dx = 0;\r
1698         dy = 0;\r
1699         SetWorldTransform( dst, &x );\r
1700         break;\r
1701     }\r
1702 \r
1703     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1704 \r
1705     x.eM11 = 1.0;\r
1706     x.eM12 = 0;\r
1707     x.eM21 = 0;\r
1708     x.eM22 = 1.0;\r
1709     x.eDx = 0;\r
1710     x.eDy = 0;\r
1711     SetWorldTransform( dst, &x );\r
1712 \r
1713     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1714 }\r
1715 \r
1716 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1717 enum {\r
1718     PM_WP = (int) WhitePawn, \r
1719     PM_WN = (int) WhiteKnight, \r
1720     PM_WB = (int) WhiteBishop, \r
1721     PM_WR = (int) WhiteRook, \r
1722     PM_WQ = (int) WhiteQueen, \r
1723     PM_WF = (int) WhiteFerz, \r
1724     PM_WW = (int) WhiteWazir, \r
1725     PM_WE = (int) WhiteAlfil, \r
1726     PM_WM = (int) WhiteMan, \r
1727     PM_WO = (int) WhiteCannon, \r
1728     PM_WU = (int) WhiteUnicorn, \r
1729     PM_WH = (int) WhiteNightrider, \r
1730     PM_WA = (int) WhiteAngel, \r
1731     PM_WC = (int) WhiteMarshall, \r
1732     PM_WAB = (int) WhiteCardinal, \r
1733     PM_WD = (int) WhiteDragon, \r
1734     PM_WL = (int) WhiteLance, \r
1735     PM_WS = (int) WhiteCobra, \r
1736     PM_WV = (int) WhiteFalcon, \r
1737     PM_WSG = (int) WhiteSilver, \r
1738     PM_WG = (int) WhiteGrasshopper, \r
1739     PM_WK = (int) WhiteKing,\r
1740     PM_BP = (int) BlackPawn, \r
1741     PM_BN = (int) BlackKnight, \r
1742     PM_BB = (int) BlackBishop, \r
1743     PM_BR = (int) BlackRook, \r
1744     PM_BQ = (int) BlackQueen, \r
1745     PM_BF = (int) BlackFerz, \r
1746     PM_BW = (int) BlackWazir, \r
1747     PM_BE = (int) BlackAlfil, \r
1748     PM_BM = (int) BlackMan,\r
1749     PM_BO = (int) BlackCannon, \r
1750     PM_BU = (int) BlackUnicorn, \r
1751     PM_BH = (int) BlackNightrider, \r
1752     PM_BA = (int) BlackAngel, \r
1753     PM_BC = (int) BlackMarshall, \r
1754     PM_BG = (int) BlackGrasshopper, \r
1755     PM_BAB = (int) BlackCardinal,\r
1756     PM_BD = (int) BlackDragon,\r
1757     PM_BL = (int) BlackLance,\r
1758     PM_BS = (int) BlackCobra,\r
1759     PM_BV = (int) BlackFalcon,\r
1760     PM_BSG = (int) BlackSilver,\r
1761     PM_BK = (int) BlackKing\r
1762 };\r
1763 \r
1764 static HFONT hPieceFont = NULL;\r
1765 static HBITMAP hPieceMask[(int) EmptySquare];\r
1766 static HBITMAP hPieceFace[(int) EmptySquare];\r
1767 static int fontBitmapSquareSize = 0;\r
1768 static char pieceToFontChar[(int) EmptySquare] =\r
1769                               { 'p', 'n', 'b', 'r', 'q', \r
1770                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1771                       'k', 'o', 'm', 'v', 't', 'w', \r
1772                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1773                                                               'l' };\r
1774 \r
1775 extern BOOL SetCharTable( char *table, const char * map );\r
1776 /* [HGM] moved to backend.c */\r
1777 \r
1778 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1779 {\r
1780     HBRUSH hbrush;\r
1781     BYTE r1 = GetRValue( color );\r
1782     BYTE g1 = GetGValue( color );\r
1783     BYTE b1 = GetBValue( color );\r
1784     BYTE r2 = r1 / 2;\r
1785     BYTE g2 = g1 / 2;\r
1786     BYTE b2 = b1 / 2;\r
1787     RECT rc;\r
1788 \r
1789     /* Create a uniform background first */\r
1790     hbrush = CreateSolidBrush( color );\r
1791     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1792     FillRect( hdc, &rc, hbrush );\r
1793     DeleteObject( hbrush );\r
1794     \r
1795     if( mode == 1 ) {\r
1796         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1797         int steps = squareSize / 2;\r
1798         int i;\r
1799 \r
1800         for( i=0; i<steps; i++ ) {\r
1801             BYTE r = r1 - (r1-r2) * i / steps;\r
1802             BYTE g = g1 - (g1-g2) * i / steps;\r
1803             BYTE b = b1 - (b1-b2) * i / steps;\r
1804 \r
1805             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1806             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1807             FillRect( hdc, &rc, hbrush );\r
1808             DeleteObject(hbrush);\r
1809         }\r
1810     }\r
1811     else if( mode == 2 ) {\r
1812         /* Diagonal gradient, good more or less for every piece */\r
1813         POINT triangle[3];\r
1814         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1815         HBRUSH hbrush_old;\r
1816         int steps = squareSize;\r
1817         int i;\r
1818 \r
1819         triangle[0].x = squareSize - steps;\r
1820         triangle[0].y = squareSize;\r
1821         triangle[1].x = squareSize;\r
1822         triangle[1].y = squareSize;\r
1823         triangle[2].x = squareSize;\r
1824         triangle[2].y = squareSize - steps;\r
1825 \r
1826         for( i=0; i<steps; i++ ) {\r
1827             BYTE r = r1 - (r1-r2) * i / steps;\r
1828             BYTE g = g1 - (g1-g2) * i / steps;\r
1829             BYTE b = b1 - (b1-b2) * i / steps;\r
1830 \r
1831             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1832             hbrush_old = SelectObject( hdc, hbrush );\r
1833             Polygon( hdc, triangle, 3 );\r
1834             SelectObject( hdc, hbrush_old );\r
1835             DeleteObject(hbrush);\r
1836             triangle[0].x++;\r
1837             triangle[2].y++;\r
1838         }\r
1839 \r
1840         SelectObject( hdc, hpen );\r
1841     }\r
1842 }\r
1843 \r
1844 /*\r
1845     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1846     seems to work ok. The main problem here is to find the "inside" of a chess\r
1847     piece: follow the steps as explained below.\r
1848 */\r
1849 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1850 {\r
1851     HBITMAP hbm;\r
1852     HBITMAP hbm_old;\r
1853     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1854     RECT rc;\r
1855     SIZE sz;\r
1856 \r
1857 \r
1858     POINT pt;\r
1859     int backColor = whitePieceColor; \r
1860     int foreColor = blackPieceColor;\r
1861     \r
1862     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1863         backColor = appData.fontBackColorWhite;\r
1864         foreColor = appData.fontForeColorWhite;\r
1865     }\r
1866     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1867         backColor = appData.fontBackColorBlack;\r
1868         foreColor = appData.fontForeColorBlack;\r
1869     }\r
1870 \r
1871     /* Mask */\r
1872     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1873 \r
1874     hbm_old = SelectObject( hdc, hbm );\r
1875 \r
1876     rc.left = 0;\r
1877     rc.top = 0;\r
1878     rc.right = squareSize;\r
1879     rc.bottom = squareSize;\r
1880 \r
1881     /* Step 1: background is now black */\r
1882     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1883 \r
1884     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1885 \r
1886     pt.x = (squareSize - sz.cx) / 2;\r
1887     pt.y = (squareSize - sz.cy) / 2;\r
1888 \r
1889     SetBkMode( hdc, TRANSPARENT );\r
1890     SetTextColor( hdc, chroma );\r
1891     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1892     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1893 \r
1894     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1895     /* Step 3: the area outside the piece is filled with white */\r
1896 //    FloodFill( hdc, 0, 0, chroma );\r
1897     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1898     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1899     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1900     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1901     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1902     /* \r
1903         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1904         but if the start point is not inside the piece we're lost!\r
1905         There should be a better way to do this... if we could create a region or path\r
1906         from the fill operation we would be fine for example.\r
1907     */\r
1908 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1909     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1910 \r
1911     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1912         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1913         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1914 \r
1915         SelectObject( dc2, bm2 );\r
1916         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1917         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1918         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1919         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1920         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1921 \r
1922         DeleteDC( dc2 );\r
1923         DeleteObject( bm2 );\r
1924     }\r
1925 \r
1926     SetTextColor( hdc, 0 );\r
1927     /* \r
1928         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1929         draw the piece again in black for safety.\r
1930     */\r
1931     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1932 \r
1933     SelectObject( hdc, hbm_old );\r
1934 \r
1935     if( hPieceMask[index] != NULL ) {\r
1936         DeleteObject( hPieceMask[index] );\r
1937     }\r
1938 \r
1939     hPieceMask[index] = hbm;\r
1940 \r
1941     /* Face */\r
1942     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1943 \r
1944     SelectObject( hdc, hbm );\r
1945 \r
1946     {\r
1947         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1948         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1949         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1950 \r
1951         SelectObject( dc1, hPieceMask[index] );\r
1952         SelectObject( dc2, bm2 );\r
1953         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1954         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1955         \r
1956         /* \r
1957             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1958             the piece background and deletes (makes transparent) the rest.\r
1959             Thanks to that mask, we are free to paint the background with the greates\r
1960             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1961             We use this, to make gradients and give the pieces a "roundish" look.\r
1962         */\r
1963         SetPieceBackground( hdc, backColor, 2 );\r
1964         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1965 \r
1966         DeleteDC( dc2 );\r
1967         DeleteDC( dc1 );\r
1968         DeleteObject( bm2 );\r
1969     }\r
1970 \r
1971     SetTextColor( hdc, foreColor );\r
1972     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1973 \r
1974     SelectObject( hdc, hbm_old );\r
1975 \r
1976     if( hPieceFace[index] != NULL ) {\r
1977         DeleteObject( hPieceFace[index] );\r
1978     }\r
1979 \r
1980     hPieceFace[index] = hbm;\r
1981 }\r
1982 \r
1983 static int TranslatePieceToFontPiece( int piece )\r
1984 {\r
1985     switch( piece ) {\r
1986     case BlackPawn:\r
1987         return PM_BP;\r
1988     case BlackKnight:\r
1989         return PM_BN;\r
1990     case BlackBishop:\r
1991         return PM_BB;\r
1992     case BlackRook:\r
1993         return PM_BR;\r
1994     case BlackQueen:\r
1995         return PM_BQ;\r
1996     case BlackKing:\r
1997         return PM_BK;\r
1998     case WhitePawn:\r
1999         return PM_WP;\r
2000     case WhiteKnight:\r
2001         return PM_WN;\r
2002     case WhiteBishop:\r
2003         return PM_WB;\r
2004     case WhiteRook:\r
2005         return PM_WR;\r
2006     case WhiteQueen:\r
2007         return PM_WQ;\r
2008     case WhiteKing:\r
2009         return PM_WK;\r
2010 \r
2011     case BlackAngel:\r
2012         return PM_BA;\r
2013     case BlackMarshall:\r
2014         return PM_BC;\r
2015     case BlackFerz:\r
2016         return PM_BF;\r
2017     case BlackNightrider:\r
2018         return PM_BH;\r
2019     case BlackAlfil:\r
2020         return PM_BE;\r
2021     case BlackWazir:\r
2022         return PM_BW;\r
2023     case BlackUnicorn:\r
2024         return PM_BU;\r
2025     case BlackCannon:\r
2026         return PM_BO;\r
2027     case BlackGrasshopper:\r
2028         return PM_BG;\r
2029     case BlackMan:\r
2030         return PM_BM;\r
2031     case BlackSilver:\r
2032         return PM_BSG;\r
2033     case BlackLance:\r
2034         return PM_BL;\r
2035     case BlackFalcon:\r
2036         return PM_BV;\r
2037     case BlackCobra:\r
2038         return PM_BS;\r
2039     case BlackCardinal:\r
2040         return PM_BAB;\r
2041     case BlackDragon:\r
2042         return PM_BD;\r
2043 \r
2044     case WhiteAngel:\r
2045         return PM_WA;\r
2046     case WhiteMarshall:\r
2047         return PM_WC;\r
2048     case WhiteFerz:\r
2049         return PM_WF;\r
2050     case WhiteNightrider:\r
2051         return PM_WH;\r
2052     case WhiteAlfil:\r
2053         return PM_WE;\r
2054     case WhiteWazir:\r
2055         return PM_WW;\r
2056     case WhiteUnicorn:\r
2057         return PM_WU;\r
2058     case WhiteCannon:\r
2059         return PM_WO;\r
2060     case WhiteGrasshopper:\r
2061         return PM_WG;\r
2062     case WhiteMan:\r
2063         return PM_WM;\r
2064     case WhiteSilver:\r
2065         return PM_WSG;\r
2066     case WhiteLance:\r
2067         return PM_WL;\r
2068     case WhiteFalcon:\r
2069         return PM_WV;\r
2070     case WhiteCobra:\r
2071         return PM_WS;\r
2072     case WhiteCardinal:\r
2073         return PM_WAB;\r
2074     case WhiteDragon:\r
2075         return PM_WD;\r
2076     }\r
2077 \r
2078     return 0;\r
2079 }\r
2080 \r
2081 void CreatePiecesFromFont()\r
2082 {\r
2083     LOGFONT lf;\r
2084     HDC hdc_window = NULL;\r
2085     HDC hdc = NULL;\r
2086     HFONT hfont_old;\r
2087     int fontHeight;\r
2088     int i;\r
2089 \r
2090     if( fontBitmapSquareSize < 0 ) {\r
2091         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2092         return;\r
2093     }\r
2094 \r
2095     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2096             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2097         fontBitmapSquareSize = -1;\r
2098         return;\r
2099     }\r
2100 \r
2101     if( fontBitmapSquareSize != squareSize ) {\r
2102         hdc_window = GetDC( hwndMain );\r
2103         hdc = CreateCompatibleDC( hdc_window );\r
2104 \r
2105         if( hPieceFont != NULL ) {\r
2106             DeleteObject( hPieceFont );\r
2107         }\r
2108         else {\r
2109             for( i=0; i<=(int)BlackKing; i++ ) {\r
2110                 hPieceMask[i] = NULL;\r
2111                 hPieceFace[i] = NULL;\r
2112             }\r
2113         }\r
2114 \r
2115         fontHeight = 75;\r
2116 \r
2117         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2118             fontHeight = appData.fontPieceSize;\r
2119         }\r
2120 \r
2121         fontHeight = (fontHeight * squareSize) / 100;\r
2122 \r
2123         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2124         lf.lfWidth = 0;\r
2125         lf.lfEscapement = 0;\r
2126         lf.lfOrientation = 0;\r
2127         lf.lfWeight = FW_NORMAL;\r
2128         lf.lfItalic = 0;\r
2129         lf.lfUnderline = 0;\r
2130         lf.lfStrikeOut = 0;\r
2131         lf.lfCharSet = DEFAULT_CHARSET;\r
2132         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2133         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2134         lf.lfQuality = PROOF_QUALITY;\r
2135         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2136         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2137         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2138 \r
2139         hPieceFont = CreateFontIndirect( &lf );\r
2140 \r
2141         if( hPieceFont == NULL ) {\r
2142             fontBitmapSquareSize = -2;\r
2143         }\r
2144         else {\r
2145             /* Setup font-to-piece character table */\r
2146             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2147                 /* No (or wrong) global settings, try to detect the font */\r
2148                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2149                     /* Alpha */\r
2150                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2151                 }\r
2152                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2153                     /* DiagramTT* family */\r
2154                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2155                 }\r
2156                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2157                     /* Fairy symbols */\r
2158                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2159                 }\r
2160                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2161                     /* Good Companion (Some characters get warped as literal :-( */\r
2162                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2163                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2164                     SetCharTable(pieceToFontChar, s);\r
2165                 }\r
2166                 else {\r
2167                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2168                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2169                 }\r
2170             }\r
2171 \r
2172             /* Create bitmaps */\r
2173             hfont_old = SelectObject( hdc, hPieceFont );\r
2174             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2175                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2176                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2177 \r
2178             SelectObject( hdc, hfont_old );\r
2179 \r
2180             fontBitmapSquareSize = squareSize;\r
2181         }\r
2182     }\r
2183 \r
2184     if( hdc != NULL ) {\r
2185         DeleteDC( hdc );\r
2186     }\r
2187 \r
2188     if( hdc_window != NULL ) {\r
2189         ReleaseDC( hwndMain, hdc_window );\r
2190     }\r
2191 }\r
2192 \r
2193 HBITMAP\r
2194 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2195 {\r
2196   char name[128], buf[MSG_SIZ];\r
2197 \r
2198     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2199   if(appData.pieceDirectory[0]) {\r
2200     HBITMAP res;\r
2201     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2202     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2203     if(res) return res;\r
2204   }\r
2205   if (gameInfo.event &&\r
2206       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2207       strcmp(name, "k80s") == 0) {\r
2208     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2209   }\r
2210   return LoadBitmap(hinst, name);\r
2211 }\r
2212 \r
2213 \r
2214 /* Insert a color into the program's logical palette\r
2215    structure.  This code assumes the given color is\r
2216    the result of the RGB or PALETTERGB macro, and it\r
2217    knows how those macros work (which is documented).\r
2218 */\r
2219 VOID\r
2220 InsertInPalette(COLORREF color)\r
2221 {\r
2222   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2223 \r
2224   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2225     DisplayFatalError(_("Too many colors"), 0, 1);\r
2226     pLogPal->palNumEntries--;\r
2227     return;\r
2228   }\r
2229 \r
2230   pe->peFlags = (char) 0;\r
2231   pe->peRed = (char) (0xFF & color);\r
2232   pe->peGreen = (char) (0xFF & (color >> 8));\r
2233   pe->peBlue = (char) (0xFF & (color >> 16));\r
2234   return;\r
2235 }\r
2236 \r
2237 \r
2238 VOID\r
2239 InitDrawingColors()\r
2240 {\r
2241   int i;\r
2242   if (pLogPal == NULL) {\r
2243     /* Allocate enough memory for a logical palette with\r
2244      * PALETTESIZE entries and set the size and version fields\r
2245      * of the logical palette structure.\r
2246      */\r
2247     pLogPal = (NPLOGPALETTE)\r
2248       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2249                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2250     pLogPal->palVersion    = 0x300;\r
2251   }\r
2252   pLogPal->palNumEntries = 0;\r
2253 \r
2254   InsertInPalette(lightSquareColor);\r
2255   InsertInPalette(darkSquareColor);\r
2256   InsertInPalette(whitePieceColor);\r
2257   InsertInPalette(blackPieceColor);\r
2258   InsertInPalette(highlightSquareColor);\r
2259   InsertInPalette(premoveHighlightColor);\r
2260 \r
2261   /*  create a logical color palette according the information\r
2262    *  in the LOGPALETTE structure.\r
2263    */\r
2264   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2265 \r
2266   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2267   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2268   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2269   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2270   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2271   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2272   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2273     for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers\r
2274 \r
2275    /* [AS] Force rendering of the font-based pieces */\r
2276   if( fontBitmapSquareSize > 0 ) {\r
2277     fontBitmapSquareSize = 0;\r
2278   }\r
2279 }\r
2280 \r
2281 \r
2282 int\r
2283 BoardWidth(int boardSize, int n)\r
2284 { /* [HGM] argument n added to allow different width and height */\r
2285   int lineGap = sizeInfo[boardSize].lineGap;\r
2286 \r
2287   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2288       lineGap = appData.overrideLineGap;\r
2289   }\r
2290 \r
2291   return (n + 1) * lineGap +\r
2292           n * sizeInfo[boardSize].squareSize;\r
2293 }\r
2294 \r
2295 /* Respond to board resize by dragging edge */\r
2296 VOID\r
2297 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2298 {\r
2299   BoardSize newSize = NUM_SIZES - 1;\r
2300   static int recurse = 0;\r
2301   if (IsIconic(hwndMain)) return;\r
2302   if (recurse > 0) return;\r
2303   recurse++;\r
2304   while (newSize > 0) {\r
2305         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2306         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2307            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2308     newSize--;\r
2309   } \r
2310   boardSize = newSize;\r
2311   InitDrawingSizes(boardSize, flags);\r
2312   recurse--;\r
2313 }\r
2314 \r
2315 \r
2316 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2317 \r
2318 VOID\r
2319 InitDrawingSizes(BoardSize boardSize, int flags)\r
2320 {\r
2321   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2322   ChessSquare piece;\r
2323   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2324   HDC hdc;\r
2325   SIZE clockSize, messageSize;\r
2326   HFONT oldFont;\r
2327   char buf[MSG_SIZ];\r
2328   char *str;\r
2329   HMENU hmenu = GetMenu(hwndMain);\r
2330   RECT crect, wrect, oldRect;\r
2331   int offby;\r
2332   LOGBRUSH logbrush;\r
2333   VariantClass v = gameInfo.variant;\r
2334 \r
2335   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2336   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2337 \r
2338   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2339   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2340   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2341   oldBoardSize = boardSize;\r
2342 \r
2343   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2344   { // correct board size to one where built-in pieces exist\r
2345     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2346        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2347 \r
2348       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2349       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2350       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {\r
2351       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2352       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2353                                    boardSize = SizeMiddling;\r
2354     }\r
2355   }\r
2356   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2357 \r
2358   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2359   oldRect.top = wpMain.y;\r
2360   oldRect.right = wpMain.x + wpMain.width;\r
2361   oldRect.bottom = wpMain.y + wpMain.height;\r
2362 \r
2363   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2364   smallLayout = sizeInfo[boardSize].smallLayout;\r
2365   squareSize = sizeInfo[boardSize].squareSize;\r
2366   lineGap = sizeInfo[boardSize].lineGap;\r
2367   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2368   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2369 \r
2370   // [HGM] decide on tininess based on total board width rather than square size\r
2371   tinyLayout = squareSize * (BOARD_WIDTH);\r
2372   tinyLayout = tinyLayout < 35*8 ? 2 : tinyLayout < 43*8 ? 1 : 0;\r
2373 \r
2374   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2375       lineGap = appData.overrideLineGap;\r
2376   }\r
2377 \r
2378   if (tinyLayout != oldTinyLayout) {\r
2379     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2380     if (tinyLayout == 2) {\r
2381       style &= ~WS_SYSMENU;\r
2382       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2383                  "&Minimize\tCtrl+F4");\r
2384     } else {\r
2385       style |= WS_SYSMENU;\r
2386       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2387     }\r
2388     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2389 \r
2390     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2391       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2392         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2393     }\r
2394     DrawMenuBar(hwndMain);\r
2395   }\r
2396 \r
2397   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2398   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2399 \r
2400   /* Get text area sizes */\r
2401   hdc = GetDC(hwndMain);\r
2402   if (appData.clockMode) {\r
2403     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2404   } else {\r
2405     snprintf(buf, MSG_SIZ, _("White"));\r
2406   }\r
2407   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2408   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2409   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2410   str = _("We only care about the height here");\r
2411   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2412   SelectObject(hdc, oldFont);\r
2413   ReleaseDC(hwndMain, hdc);\r
2414 \r
2415   /* Compute where everything goes */\r
2416   if((first.programLogo || second.programLogo) && tinyLayout != 2) {\r
2417         /* [HGM] logo: if either logo is on, reserve space for it */\r
2418         logoHeight =  2*clockSize.cy;\r
2419         leftLogoRect.left   = OUTER_MARGIN;\r
2420         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2421         leftLogoRect.top    = OUTER_MARGIN;\r
2422         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2423 \r
2424         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2425         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2426         rightLogoRect.top    = OUTER_MARGIN;\r
2427         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2428 \r
2429 \r
2430     whiteRect.left = leftLogoRect.right;\r
2431     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2432     whiteRect.top = OUTER_MARGIN;\r
2433     whiteRect.bottom = whiteRect.top + logoHeight;\r
2434 \r
2435     blackRect.right = rightLogoRect.left;\r
2436     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2437     blackRect.top = whiteRect.top;\r
2438     blackRect.bottom = whiteRect.bottom;\r
2439   } else {\r
2440     whiteRect.left = OUTER_MARGIN;\r
2441     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2442     whiteRect.top = OUTER_MARGIN;\r
2443     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2444 \r
2445     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2446     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2447     blackRect.top = whiteRect.top;\r
2448     blackRect.bottom = whiteRect.bottom;\r
2449 \r
2450     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2451   }\r
2452 \r
2453   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2454   if (appData.showButtonBar) {\r
2455     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2456       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2457   } else {\r
2458     messageRect.right = OUTER_MARGIN + boardWidth;\r
2459   }\r
2460   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2461   messageRect.bottom = messageRect.top + messageSize.cy;\r
2462 \r
2463   boardRect.left = OUTER_MARGIN;\r
2464   boardRect.right = boardRect.left + boardWidth;\r
2465   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2466   boardRect.bottom = boardRect.top + boardHeight;\r
2467 \r
2468   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2469   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2470   oldTinyLayout = tinyLayout;\r
2471   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2472   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2473     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2474   winW *= 1 + twoBoards;\r
2475   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2476   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2477   wpMain.height = winH; //       without disturbing window attachments\r
2478   GetWindowRect(hwndMain, &wrect);\r
2479   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2480                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2481 \r
2482   // [HGM] placement: let attached windows follow size change.\r
2483   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2484   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2485   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2486   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2487   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2488 \r
2489   /* compensate if menu bar wrapped */\r
2490   GetClientRect(hwndMain, &crect);\r
2491   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2492   wpMain.height += offby;\r
2493   switch (flags) {\r
2494   case WMSZ_TOPLEFT:\r
2495     SetWindowPos(hwndMain, NULL, \r
2496                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2497                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2498     break;\r
2499 \r
2500   case WMSZ_TOPRIGHT:\r
2501   case WMSZ_TOP:\r
2502     SetWindowPos(hwndMain, NULL, \r
2503                  wrect.left, wrect.bottom - wpMain.height, \r
2504                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2505     break;\r
2506 \r
2507   case WMSZ_BOTTOMLEFT:\r
2508   case WMSZ_LEFT:\r
2509     SetWindowPos(hwndMain, NULL, \r
2510                  wrect.right - wpMain.width, wrect.top, \r
2511                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2512     break;\r
2513 \r
2514   case WMSZ_BOTTOMRIGHT:\r
2515   case WMSZ_BOTTOM:\r
2516   case WMSZ_RIGHT:\r
2517   default:\r
2518     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2519                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2520     break;\r
2521   }\r
2522 \r
2523   hwndPause = NULL;\r
2524   for (i = 0; i < N_BUTTONS; i++) {\r
2525     if (buttonDesc[i].hwnd != NULL) {\r
2526       DestroyWindow(buttonDesc[i].hwnd);\r
2527       buttonDesc[i].hwnd = NULL;\r
2528     }\r
2529     if (appData.showButtonBar) {\r
2530       buttonDesc[i].hwnd =\r
2531         CreateWindow("BUTTON", buttonDesc[i].label,\r
2532                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2533                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2534                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2535                      (HMENU) buttonDesc[i].id,\r
2536                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2537       if (tinyLayout == 2) {\r
2538         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2539                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2540                     MAKELPARAM(FALSE, 0));\r
2541       }\r
2542       if (buttonDesc[i].id == IDM_Pause)\r
2543         hwndPause = buttonDesc[i].hwnd;\r
2544       buttonDesc[i].wndproc = (WNDPROC)\r
2545         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2546     }\r
2547   }\r
2548   if (gridPen != NULL) DeleteObject(gridPen);\r
2549   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2550   if (premovePen != NULL) DeleteObject(premovePen);\r
2551   if (lineGap != 0) {\r
2552     logbrush.lbStyle = BS_SOLID;\r
2553     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2554     gridPen =\r
2555       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2556                    lineGap, &logbrush, 0, NULL);\r
2557     logbrush.lbColor = highlightSquareColor;\r
2558     highlightPen =\r
2559       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2560                    lineGap, &logbrush, 0, NULL);\r
2561 \r
2562     logbrush.lbColor = premoveHighlightColor; \r
2563     premovePen =\r
2564       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2565                    lineGap, &logbrush, 0, NULL);\r
2566 \r
2567     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2568     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2569       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2570       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2571         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2572       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2573         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2574       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2575     }\r
2576     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2577       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2578       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2579         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2580         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2581       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2582         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2583       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2584     }\r
2585   }\r
2586 \r
2587   /* [HGM] Licensing requirement */\r
2588 #ifdef GOTHIC\r
2589   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2590 #endif\r
2591 #ifdef FALCON\r
2592   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2593 #endif\r
2594   GothicPopUp( "", VariantNormal);\r
2595 \r
2596 \r
2597 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2598 \r
2599   /* Load piece bitmaps for this board size */\r
2600   for (i=0; i<=2; i++) {\r
2601     for (piece = WhitePawn;\r
2602          (int) piece < (int) BlackPawn;\r
2603          piece = (ChessSquare) ((int) piece + 1)) {\r
2604       if (pieceBitmap[i][piece] != NULL)\r
2605         DeleteObject(pieceBitmap[i][piece]);\r
2606       pieceBitmap[i][piece] = NULL;\r
2607     }\r
2608   }\r
2609 \r
2610   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2611 \r
2612   // Orthodox Chess pieces\r
2613   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2614   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2615   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2616   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2617   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2618   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2619   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2620   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2621   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2622   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2623   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2624   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2625   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2626   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2627   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2628   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2629     // in Shogi, Hijack the unused Queen for Lance\r
2630     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2631     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2632     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2633   } else {\r
2634     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2635     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2636     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2637   }\r
2638 \r
2639   if(squareSize <= 72 && squareSize >= 33) { \r
2640     /* A & C are available in most sizes now */\r
2641     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2642       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2643       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2644       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2645       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2646       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2647       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2648       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2649       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2650       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2651       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2652       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2653       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2654     } else { // Smirf-like\r
2655       if(gameInfo.variant == VariantSChess) {\r
2656         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2657         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2658         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2659       } else {\r
2660         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2661         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2662         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2663       }\r
2664     }\r
2665     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2666       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2667       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2668       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2669     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2670       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2671       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2672       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2673     } else { // WinBoard standard\r
2674       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2675       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2676       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2677     }\r
2678   }\r
2679 \r
2680 \r
2681   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2682     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2683     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2684     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2685     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2686     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2687     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2688     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2689     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2690     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2691     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2692     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2693     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2694     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2695     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2696     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2697     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2698     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2699     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2700     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2701     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2702     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2703     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2704     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2705     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2706     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2707     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2708     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2709     pieceBitmap[0][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2710     pieceBitmap[1][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2711     pieceBitmap[2][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2712     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2713     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2714     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2715     pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2716     pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2717     pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2718     pieceBitmap[0][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2719     pieceBitmap[1][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2720     pieceBitmap[2][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2721     pieceBitmap[0][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "s");\r
2722     pieceBitmap[1][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "o");\r
2723     pieceBitmap[2][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "w");\r
2724     pieceBitmap[0][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "s");\r
2725     pieceBitmap[1][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "o");\r
2726     pieceBitmap[2][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "w");\r
2727     pieceBitmap[0][WhiteZebra] = DoLoadBitmap(hInst, "zebra", squareSize, "s");\r
2728     pieceBitmap[1][WhiteZebra] = DoLoadBitmap(hInst, "zebra", squareSize, "o");\r
2729     pieceBitmap[2][WhiteZebra] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2730 \r
2731     if(gameInfo.variant == VariantShogi && BOARD_HEIGHT != 7) { /* promoted Gold representations (but not in Tori!)*/\r
2732       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2733       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2734       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2735       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2736       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2737       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2738       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2739       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2740       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2741       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2742       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2743       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2744     } else {\r
2745       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2746       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2747       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2748       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2749       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2750       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2751       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2752       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2753       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2754       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2755       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2756       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2757     }\r
2758 \r
2759   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2760     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2761     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2762     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2763     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2764     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2765     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2766     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2767     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2768     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2769     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2770     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2771     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2772     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2773     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2774   }\r
2775 \r
2776 \r
2777   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2778   /* special Shogi support in this size */\r
2779   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2780       for (piece = WhitePawn;\r
2781            (int) piece < (int) BlackPawn;\r
2782            piece = (ChessSquare) ((int) piece + 1)) {\r
2783         if (pieceBitmap[i][piece] != NULL)\r
2784           DeleteObject(pieceBitmap[i][piece]);\r
2785       }\r
2786     }\r
2787   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2788   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2789   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2790   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2791   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2792   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2793   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2794   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2795   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2796   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2797   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2798   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2799   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2800   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2801   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2802   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2803   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2804   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2805   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2806   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2807   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2808   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2809   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2810   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2811   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2812   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2813   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2814   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2815   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2816   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2817   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2818   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2819   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2820   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2821   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2822   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2823   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2824   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2825   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2826   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2827   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2828   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2829   minorSize = 0;\r
2830   }\r
2831 \r
2832   if(appData.pieceDirectory[0]) for(i=WhitePawn; i<BlackPawn; i++) { // try for all missing pieces with new naming convention\r
2833     char buf[MSG_SIZ];\r
2834     if(pieceBitmap[0][i]) continue;\r
2835     snprintf(buf, MSG_SIZ, "piece%d_", i);\r
2836     pieceBitmap[0][i] = DoLoadBitmap(hInst, buf, squareSize, "s");\r
2837     pieceBitmap[1][i] = DoLoadBitmap(hInst, buf, squareSize, "o");\r
2838     pieceBitmap[2][i] = DoLoadBitmap(hInst, buf, squareSize, "w");\r
2839   }\r
2840 }\r
2841 \r
2842 HBITMAP\r
2843 PieceBitmap(ChessSquare p, int kind)\r
2844 {\r
2845   if ((int) p >= (int) BlackPawn)\r
2846     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2847 \r
2848   return pieceBitmap[kind][(int) p];\r
2849 }\r
2850 \r
2851 /***************************************************************/\r
2852 \r
2853 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2854 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2855 /*\r
2856 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2857 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2858 */\r
2859 \r
2860 VOID\r
2861 SquareToPos(int row, int column, int * x, int * y)\r
2862 {\r
2863   if (flipView) {\r
2864     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2865     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2866   } else {\r
2867     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2868     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2869   }\r
2870 }\r
2871 \r
2872 VOID\r
2873 DrawCoordsOnDC(HDC hdc)\r
2874 {\r
2875   static char files[] = "0123456789012345678901221098765432109876543210";\r
2876   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2877   char str[2] = { NULLCHAR, NULLCHAR };\r
2878   int oldMode, oldAlign, x, y, start, i;\r
2879   HFONT oldFont;\r
2880   HBRUSH oldBrush;\r
2881 \r
2882   if (!appData.showCoords)\r
2883     return;\r
2884 \r
2885   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2886 \r
2887   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2888   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2889   oldAlign = GetTextAlign(hdc);\r
2890   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2891 \r
2892   y = boardRect.top + lineGap;\r
2893   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2894 \r
2895   if(border) {\r
2896     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2897     x += border - lineGap - 4; y += squareSize - 6;\r
2898   } else\r
2899   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2900   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2901     str[0] = files[start + i];\r
2902     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2903     y += squareSize + lineGap;\r
2904   }\r
2905 \r
2906   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2907 \r
2908   if(border) {\r
2909     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2910     x += -border + 4; y += border - squareSize + 6;\r
2911   } else\r
2912   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2913   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2914     str[0] = ranks[start + i];\r
2915     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2916     x += squareSize + lineGap;\r
2917   }    \r
2918 \r
2919   SelectObject(hdc, oldBrush);\r
2920   SetBkMode(hdc, oldMode);\r
2921   SetTextAlign(hdc, oldAlign);\r
2922   SelectObject(hdc, oldFont);\r
2923 }\r
2924 \r
2925 VOID\r
2926 DrawGridOnDC(HDC hdc)\r
2927 {\r
2928   HPEN oldPen;\r
2929  \r
2930   if (lineGap != 0) {\r
2931     oldPen = SelectObject(hdc, gridPen);\r
2932     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2933     SelectObject(hdc, oldPen);\r
2934   }\r
2935 }\r
2936 \r
2937 #define HIGHLIGHT_PEN 0\r
2938 #define PREMOVE_PEN   1\r
2939 \r
2940 VOID\r
2941 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2942 {\r
2943   int x1, y1;\r
2944   HPEN oldPen, hPen;\r
2945   if (lineGap == 0) return;\r
2946   if (flipView) {\r
2947     x1 = boardRect.left +\r
2948       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2949     y1 = boardRect.top +\r
2950       lineGap/2 + y * (squareSize + lineGap) + border;\r
2951   } else {\r
2952     x1 = boardRect.left +\r
2953       lineGap/2 + x * (squareSize + lineGap) + border;\r
2954     y1 = boardRect.top +\r
2955       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2956   }\r
2957   hPen = pen ? premovePen : highlightPen;\r
2958   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2959   MoveToEx(hdc, x1, y1, NULL);\r
2960   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2961   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2962   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2963   LineTo(hdc, x1, y1);\r
2964   SelectObject(hdc, oldPen);\r
2965 }\r
2966 \r
2967 VOID\r
2968 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2969 {\r
2970   int i;\r
2971   for (i=0; i<2; i++) {\r
2972     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2973       DrawHighlightOnDC(hdc, TRUE,\r
2974                         h->sq[i].x, h->sq[i].y,\r
2975                         pen);\r
2976   }\r
2977 }\r
2978 \r
2979 /* Note: sqcolor is used only in monoMode */\r
2980 /* Note that this code is largely duplicated in woptions.c,\r
2981    function DrawSampleSquare, so that needs to be updated too */\r
2982 VOID\r
2983 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2984 {\r
2985   HBITMAP oldBitmap;\r
2986   HBRUSH oldBrush;\r
2987   int tmpSize;\r
2988 \r
2989   if (appData.blindfold) return;\r
2990 \r
2991   /* [AS] Use font-based pieces if needed */\r
2992   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2993     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2994     CreatePiecesFromFont();\r
2995 \r
2996     if( fontBitmapSquareSize == squareSize ) {\r
2997         int index = TranslatePieceToFontPiece(piece);\r
2998 \r
2999         SelectObject( tmphdc, hPieceMask[ index ] );\r
3000 \r
3001       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
3002         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
3003       else\r
3004         BitBlt( hdc,\r
3005             x, y,\r
3006             squareSize, squareSize,\r
3007             tmphdc,\r
3008             0, 0,\r
3009             SRCAND );\r
3010 \r
3011         SelectObject( tmphdc, hPieceFace[ index ] );\r
3012 \r
3013       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
3014         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
3015       else\r
3016         BitBlt( hdc,\r
3017             x, y,\r
3018             squareSize, squareSize,\r
3019             tmphdc,\r
3020             0, 0,\r
3021             SRCPAINT );\r
3022 \r
3023         return;\r
3024     }\r
3025   }\r
3026 \r
3027   if (appData.monoMode) {\r
3028     SelectObject(tmphdc, PieceBitmap(piece, \r
3029       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3030     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3031            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3032   } else {\r
3033     HBRUSH xBrush = whitePieceBrush;\r
3034     tmpSize = squareSize;\r
3035     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
3036     if(minorSize &&\r
3037         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3038          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3039       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3040       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3041       x += (squareSize - minorSize)>>1;\r
3042       y += squareSize - minorSize - 2;\r
3043       tmpSize = minorSize;\r
3044     }\r
3045     if (color || appData.allWhite ) {\r
3046       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3047       if( color )\r
3048               oldBrush = SelectObject(hdc, xBrush);\r
3049       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3050       if(appData.upsideDown && color==flipView)\r
3051         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3052       else\r
3053         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3054       /* Use black for outline of white pieces */\r
3055       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3056       if(appData.upsideDown && color==flipView)\r
3057         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3058       else\r
3059         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3060     } else if(appData.pieceDirectory[0]) {\r
3061       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3062       oldBrush = SelectObject(hdc, xBrush);\r
3063       if(appData.upsideDown && color==flipView)\r
3064         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3065       else\r
3066         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3067       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3068       if(appData.upsideDown && color==flipView)\r
3069         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3070       else\r
3071         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3072     } else {\r
3073       /* Use square color for details of black pieces */\r
3074       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3075       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3076       if(appData.upsideDown && !flipView)\r
3077         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3078       else\r
3079         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3080     }\r
3081     SelectObject(hdc, oldBrush);\r
3082     SelectObject(tmphdc, oldBitmap);\r
3083   }\r
3084 }\r
3085 \r
3086 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3087 int GetBackTextureMode( int algo )\r
3088 {\r
3089     int result = BACK_TEXTURE_MODE_DISABLED;\r
3090 \r
3091     switch( algo ) \r
3092     {\r
3093         case BACK_TEXTURE_MODE_PLAIN:\r
3094             result = 1; /* Always use identity map */\r
3095             break;\r
3096         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3097             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3098             break;\r
3099     }\r
3100 \r
3101     return result;\r
3102 }\r
3103 \r
3104 /* \r
3105     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3106     to handle redraws cleanly (as random numbers would always be different).\r
3107 */\r
3108 VOID RebuildTextureSquareInfo()\r
3109 {\r
3110     BITMAP bi;\r
3111     int lite_w = 0;\r
3112     int lite_h = 0;\r
3113     int dark_w = 0;\r
3114     int dark_h = 0;\r
3115     int row;\r
3116     int col;\r
3117 \r
3118     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3119 \r
3120     if( liteBackTexture != NULL ) {\r
3121         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3122             lite_w = bi.bmWidth;\r
3123             lite_h = bi.bmHeight;\r
3124         }\r
3125     }\r
3126 \r
3127     if( darkBackTexture != NULL ) {\r
3128         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3129             dark_w = bi.bmWidth;\r
3130             dark_h = bi.bmHeight;\r
3131         }\r
3132     }\r
3133 \r
3134     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3135         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3136             if( (col + row) & 1 ) {\r
3137                 /* Lite square */\r
3138                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3139                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3140                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3141                   else\r
3142                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3143                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3144                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3145                   else\r
3146                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3147                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3148                 }\r
3149             }\r
3150             else {\r
3151                 /* Dark square */\r
3152                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3153                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3154                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3155                   else\r
3156                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3157                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3158                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3159                   else\r
3160                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3161                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3162                 }\r
3163             }\r
3164         }\r
3165     }\r
3166 }\r
3167 \r
3168 /* [AS] Arrow highlighting support */\r
3169 \r
3170 static double A_WIDTH = 5; /* Width of arrow body */\r
3171 \r
3172 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3173 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3174 \r
3175 static double Sqr( double x )\r
3176 {\r
3177     return x*x;\r
3178 }\r
3179 \r
3180 static int Round( double x )\r
3181 {\r
3182     return (int) (x + 0.5);\r
3183 }\r
3184 \r
3185 /* Draw an arrow between two points using current settings */\r
3186 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3187 {\r
3188     POINT arrow[7];\r
3189     double dx, dy, j, k, x, y;\r
3190 \r
3191     if( d_x == s_x ) {\r
3192         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3193 \r
3194         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3195         arrow[0].y = s_y;\r
3196 \r
3197         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3198         arrow[1].y = d_y - h;\r
3199 \r
3200         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3201         arrow[2].y = d_y - h;\r
3202 \r
3203         arrow[3].x = d_x;\r
3204         arrow[3].y = d_y;\r
3205 \r
3206         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3207         arrow[5].y = d_y - h;\r
3208 \r
3209         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3210         arrow[4].y = d_y - h;\r
3211 \r
3212         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3213         arrow[6].y = s_y;\r
3214     }\r
3215     else if( d_y == s_y ) {\r
3216         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3217 \r
3218         arrow[0].x = s_x;\r
3219         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3220 \r
3221         arrow[1].x = d_x - w;\r
3222         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3223 \r
3224         arrow[2].x = d_x - w;\r
3225         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3226 \r
3227         arrow[3].x = d_x;\r
3228         arrow[3].y = d_y;\r
3229 \r
3230         arrow[5].x = d_x - w;\r
3231         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3232 \r
3233         arrow[4].x = d_x - w;\r
3234         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3235 \r
3236         arrow[6].x = s_x;\r
3237         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3238     }\r
3239     else {\r
3240         /* [AS] Needed a lot of paper for this! :-) */\r
3241         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3242         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3243   \r
3244         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3245 \r
3246         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3247 \r
3248         x = s_x;\r
3249         y = s_y;\r
3250 \r
3251         arrow[0].x = Round(x - j);\r
3252         arrow[0].y = Round(y + j*dx);\r
3253 \r
3254         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3255         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3256 \r
3257         if( d_x > s_x ) {\r
3258             x = (double) d_x - k;\r
3259             y = (double) d_y - k*dy;\r
3260         }\r
3261         else {\r
3262             x = (double) d_x + k;\r
3263             y = (double) d_y + k*dy;\r
3264         }\r
3265 \r
3266         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3267 \r
3268         arrow[6].x = Round(x - j);\r
3269         arrow[6].y = Round(y + j*dx);\r
3270 \r
3271         arrow[2].x = Round(arrow[6].x + 2*j);\r
3272         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3273 \r
3274         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3275         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3276 \r
3277         arrow[4].x = d_x;\r
3278         arrow[4].y = d_y;\r
3279 \r
3280         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3281         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3282     }\r
3283 \r
3284     Polygon( hdc, arrow, 7 );\r
3285 }\r
3286 \r
3287 /* [AS] Draw an arrow between two squares */\r
3288 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3289 {\r
3290     int s_x, s_y, d_x, d_y;\r
3291     HPEN hpen;\r
3292     HPEN holdpen;\r
3293     HBRUSH hbrush;\r
3294     HBRUSH holdbrush;\r
3295     LOGBRUSH stLB;\r
3296 \r
3297     if( s_col == d_col && s_row == d_row ) {\r
3298         return;\r
3299     }\r
3300 \r
3301     /* Get source and destination points */\r
3302     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3303     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3304 \r
3305     if( d_y > s_y ) {\r
3306         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3307     }\r
3308     else if( d_y < s_y ) {\r
3309         d_y += squareSize / 2 + squareSize / 4;\r
3310     }\r
3311     else {\r
3312         d_y += squareSize / 2;\r
3313     }\r
3314 \r
3315     if( d_x > s_x ) {\r
3316         d_x += squareSize / 2 - squareSize / 4;\r
3317     }\r
3318     else if( d_x < s_x ) {\r
3319         d_x += squareSize / 2 + squareSize / 4;\r
3320     }\r
3321     else {\r
3322         d_x += squareSize / 2;\r
3323     }\r
3324 \r
3325     s_x += squareSize / 2;\r
3326     s_y += squareSize / 2;\r
3327 \r
3328     /* Adjust width */\r
3329     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3330 \r
3331     /* Draw */\r
3332     stLB.lbStyle = BS_SOLID;\r
3333     stLB.lbColor = appData.highlightArrowColor;\r
3334     stLB.lbHatch = 0;\r
3335 \r
3336     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3337     holdpen = SelectObject( hdc, hpen );\r
3338     hbrush = CreateBrushIndirect( &stLB );\r
3339     holdbrush = SelectObject( hdc, hbrush );\r
3340 \r
3341     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3342 \r
3343     SelectObject( hdc, holdpen );\r
3344     SelectObject( hdc, holdbrush );\r
3345     DeleteObject( hpen );\r
3346     DeleteObject( hbrush );\r
3347 }\r
3348 \r
3349 BOOL HasHighlightInfo()\r
3350 {\r
3351     BOOL result = FALSE;\r
3352 \r
3353     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3354         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3355     {\r
3356         result = TRUE;\r
3357     }\r
3358 \r
3359     return result;\r
3360 \r
3361 \r
3362 \r
3363 }\r
3364 \r
3365 BOOL IsDrawArrowEnabled()\r
3366 {\r
3367     BOOL result = FALSE;\r
3368 \r
3369     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3370         result = TRUE;\r
3371     }\r
3372 \r
3373     return result;\r
3374 }\r
3375 \r
3376 VOID DrawArrowHighlight( HDC hdc )\r
3377 {\r
3378     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3379         DrawArrowBetweenSquares( hdc,\r
3380             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3381             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3382     }\r
3383 }\r
3384 \r
3385 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3386 {\r
3387     HRGN result = NULL;\r
3388 \r
3389     if( HasHighlightInfo() ) {\r
3390         int x1, y1, x2, y2;\r
3391         int sx, sy, dx, dy;\r
3392 \r
3393         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3394         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3395 \r
3396         sx = MIN( x1, x2 );\r
3397         sy = MIN( y1, y2 );\r
3398         dx = MAX( x1, x2 ) + squareSize;\r
3399         dy = MAX( y1, y2 ) + squareSize;\r
3400 \r
3401         result = CreateRectRgn( sx, sy, dx, dy );\r
3402     }\r
3403 \r
3404     return result;\r
3405 }\r
3406 \r
3407 /*\r
3408     Warning: this function modifies the behavior of several other functions. \r
3409     \r
3410     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3411     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3412     repaint is scattered all over the place, which is not good for features such as\r
3413     "arrow highlighting" that require a full repaint of the board.\r
3414 \r
3415     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3416     user interaction, when speed is not so important) but especially to avoid errors\r
3417     in the displayed graphics.\r
3418 \r
3419     In such patched places, I always try refer to this function so there is a single\r
3420     place to maintain knowledge.\r
3421     \r
3422     To restore the original behavior, just return FALSE unconditionally.\r
3423 */\r
3424 BOOL IsFullRepaintPreferrable()\r
3425 {\r
3426     BOOL result = FALSE;\r
3427 \r
3428     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3429         /* Arrow may appear on the board */\r
3430         result = TRUE;\r
3431     }\r
3432 \r
3433     return result;\r
3434 }\r
3435 \r
3436 /* \r
3437     This function is called by DrawPosition to know whether a full repaint must\r
3438     be forced or not.\r
3439 \r
3440     Only DrawPosition may directly call this function, which makes use of \r
3441     some state information. Other function should call DrawPosition specifying \r
3442     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3443 */\r
3444 BOOL DrawPositionNeedsFullRepaint()\r
3445 {\r
3446     BOOL result = FALSE;\r
3447 \r
3448     /* \r
3449         Probably a slightly better policy would be to trigger a full repaint\r
3450         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3451         but animation is fast enough that it's difficult to notice.\r
3452     */\r
3453     if( animInfo.piece == EmptySquare ) {\r
3454         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3455             result = TRUE;\r
3456         }\r
3457     }\r
3458 \r
3459     return result;\r
3460 }\r
3461 \r
3462 static HBITMAP borderBitmap;\r
3463 \r
3464 VOID\r
3465 DrawBackgroundOnDC(HDC hdc)\r
3466 {\r
3467   \r
3468   BITMAP bi;\r
3469   HDC tmphdc;\r
3470   HBITMAP hbm;\r
3471   static char oldBorder[MSG_SIZ];\r
3472   int w = 600, h = 600, mode;\r
3473 \r
3474   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3475     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3476     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3477   }\r
3478   if(borderBitmap == NULL) { // loading failed, use white\r
3479     FillRect( hdc, &boardRect, whitePieceBrush );\r
3480     return;\r
3481   }\r
3482   tmphdc = CreateCompatibleDC(hdc);\r
3483   hbm = SelectObject(tmphdc, borderBitmap);\r
3484   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3485             w = bi.bmWidth;\r
3486             h = bi.bmHeight;\r
3487   }\r
3488   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3489   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3490                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3491   SetStretchBltMode(hdc, mode);\r
3492   SelectObject(tmphdc, hbm);\r
3493   DeleteDC(tmphdc);\r
3494 }\r
3495 \r
3496 VOID\r
3497 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3498 {\r
3499   int row, column, x, y, square_color, piece_color;\r
3500   ChessSquare piece;\r
3501   HBRUSH oldBrush;\r
3502   HDC texture_hdc = NULL;\r
3503 \r
3504   /* [AS] Initialize background textures if needed */\r
3505   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3506       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3507       if( backTextureSquareSize != squareSize \r
3508        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3509           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3510           backTextureSquareSize = squareSize;\r
3511           RebuildTextureSquareInfo();\r
3512       }\r
3513 \r
3514       texture_hdc = CreateCompatibleDC( hdc );\r
3515   }\r
3516 \r
3517   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3518     for (column = 0; column < BOARD_WIDTH; column++) {\r
3519   \r
3520       SquareToPos(row, column, &x, &y);\r
3521 \r
3522       piece = board[row][column];\r
3523 \r
3524       square_color = ((column + row) % 2) == 1;\r
3525       if( gameInfo.variant == VariantXiangqi ) {\r
3526           square_color = !InPalace(row, column);\r
3527           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3528           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3529       }\r
3530       piece_color = (int) piece < (int) BlackPawn;\r
3531 \r
3532 \r
3533       /* [HGM] holdings file: light square or black */\r
3534       if(column == BOARD_LEFT-2) {\r
3535             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3536                 square_color = 1;\r
3537             else {\r
3538                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3539                 continue;\r
3540             }\r
3541       } else\r
3542       if(column == BOARD_RGHT + 1 ) {\r
3543             if( row < gameInfo.holdingsSize )\r
3544                 square_color = 1;\r
3545             else {\r
3546                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3547                 continue;\r
3548             }\r
3549       }\r
3550       if(column == BOARD_LEFT-1 ) /* left align */\r
3551             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3552       else if( column == BOARD_RGHT) /* right align */\r
3553             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3554       else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);\r
3555       else\r
3556       if (appData.monoMode) {\r
3557         if (piece == EmptySquare) {\r
3558           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3559                  square_color ? WHITENESS : BLACKNESS);\r
3560         } else {\r
3561           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3562         }\r
3563       } \r
3564       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3565           /* [AS] Draw the square using a texture bitmap */\r
3566           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3567           int r = row, c = column; // [HGM] do not flip board in flipView\r
3568           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3569 \r
3570           DrawTile( x, y, \r
3571               squareSize, squareSize, \r
3572               hdc, \r
3573               texture_hdc,\r
3574               backTextureSquareInfo[r][c].mode,\r
3575               backTextureSquareInfo[r][c].x,\r
3576               backTextureSquareInfo[r][c].y );\r
3577 \r
3578           SelectObject( texture_hdc, hbm );\r
3579 \r
3580           if (piece != EmptySquare) {\r
3581               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3582           }\r
3583       }\r
3584       else {\r
3585         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3586 \r
3587         oldBrush = SelectObject(hdc, brush );\r
3588         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3589         SelectObject(hdc, oldBrush);\r
3590         if (piece != EmptySquare)\r
3591           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3592       }\r
3593     }\r
3594   }\r
3595 \r
3596   if( texture_hdc != NULL ) {\r
3597     DeleteDC( texture_hdc );\r
3598   }\r
3599 }\r
3600 \r
3601 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3602 void fputDW(FILE *f, int x)\r
3603 {\r
3604         fputc(x     & 255, f);\r
3605         fputc(x>>8  & 255, f);\r
3606         fputc(x>>16 & 255, f);\r
3607         fputc(x>>24 & 255, f);\r
3608 }\r
3609 \r
3610 #define MAX_CLIPS 200   /* more than enough */\r
3611 \r
3612 VOID\r
3613 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3614 {\r
3615 //  HBITMAP bufferBitmap;\r
3616   BITMAP bi;\r
3617 //  RECT Rect;\r
3618   HDC tmphdc;\r
3619   HBITMAP hbm;\r
3620   int w = 100, h = 50;\r
3621 \r
3622   if(logo == NULL) {\r
3623     if(!logoHeight) return;\r
3624     FillRect( hdc, &logoRect, whitePieceBrush );\r
3625   }\r
3626 //  GetClientRect(hwndMain, &Rect);\r
3627 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3628 //                                      Rect.bottom-Rect.top+1);\r
3629   tmphdc = CreateCompatibleDC(hdc);\r
3630   hbm = SelectObject(tmphdc, logo);\r
3631   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3632             w = bi.bmWidth;\r
3633             h = bi.bmHeight;\r
3634   }\r
3635   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3636                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3637   SelectObject(tmphdc, hbm);\r
3638   DeleteDC(tmphdc);\r
3639 }\r
3640 \r
3641 VOID\r
3642 DisplayLogos()\r
3643 {\r
3644   if(logoHeight) {\r
3645         HDC hdc = GetDC(hwndMain);\r
3646         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3647         if(appData.autoLogo) {\r
3648           \r
3649           switch(gameMode) { // pick logos based on game mode\r
3650             case IcsObserving:\r
3651                 whiteLogo = second.programLogo; // ICS logo\r
3652                 blackLogo = second.programLogo;\r
3653             default:\r
3654                 break;\r
3655             case IcsPlayingWhite:\r
3656                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3657                 blackLogo = second.programLogo; // ICS logo\r
3658                 break;\r
3659             case IcsPlayingBlack:\r
3660                 whiteLogo = second.programLogo; // ICS logo\r
3661                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3662                 break;\r
3663             case TwoMachinesPlay:\r
3664                 if(first.twoMachinesColor[0] == 'b') {\r
3665                     whiteLogo = second.programLogo;\r
3666                     blackLogo = first.programLogo;\r
3667                 }\r
3668                 break;\r
3669             case MachinePlaysWhite:\r
3670                 blackLogo = userLogo;\r
3671                 break;\r
3672             case MachinePlaysBlack:\r
3673                 whiteLogo = userLogo;\r
3674                 blackLogo = first.programLogo;\r
3675           }\r
3676         }\r
3677         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3678         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3679         ReleaseDC(hwndMain, hdc);\r
3680   }\r
3681 }\r
3682 \r
3683 void\r
3684 UpdateLogos(int display)\r
3685 { // called after loading new engine(s), in tourney or from menu\r
3686   LoadLogo(&first, 0, FALSE);\r
3687   LoadLogo(&second, 1, appData.icsActive);\r
3688   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3689   if(display) DisplayLogos();\r
3690 }\r
3691 \r
3692 static HDC hdcSeek;\r
3693 \r
3694 // [HGM] seekgraph\r
3695 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3696 {\r
3697     POINT stPt;\r
3698     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3699     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3700     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3701     SelectObject( hdcSeek, hp );\r
3702 }\r
3703 \r
3704 // front-end wrapper for drawing functions to do rectangles\r
3705 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3706 {\r
3707     HPEN hp;\r
3708     RECT rc;\r
3709 \r
3710     if (hdcSeek == NULL) {\r
3711     hdcSeek = GetDC(hwndMain);\r
3712       if (!appData.monoMode) {\r
3713         SelectPalette(hdcSeek, hPal, FALSE);\r
3714         RealizePalette(hdcSeek);\r
3715       }\r
3716     }\r
3717     hp = SelectObject( hdcSeek, gridPen );\r
3718     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3719     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3720     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3721     SelectObject( hdcSeek, hp );\r
3722 }\r
3723 \r
3724 // front-end wrapper for putting text in graph\r
3725 void DrawSeekText(char *buf, int x, int y)\r
3726 {\r
3727         SIZE stSize;\r
3728         SetBkMode( hdcSeek, TRANSPARENT );\r
3729         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3730         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3731 }\r
3732 \r
3733 void DrawSeekDot(int x, int y, int color)\r
3734 {\r
3735         int square = color & 0x80;\r
3736         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3737                         color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);\r
3738         color &= 0x7F;\r
3739         if(square)\r
3740             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3741                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3742         else\r
3743             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3744                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3745             SelectObject(hdcSeek, oldBrush);\r
3746 }\r
3747 \r
3748 void DrawSeekOpen()\r
3749 {\r
3750 }\r
3751 \r
3752 void DrawSeekClose()\r
3753 {\r
3754 }\r
3755 \r
3756 VOID\r
3757 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3758 {\r
3759   static Board lastReq[2], lastDrawn[2];\r
3760   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3761   static int lastDrawnFlipView = 0;\r
3762   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3763   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3764   HDC tmphdc;\r
3765   HDC hdcmem;\r
3766   HBITMAP bufferBitmap;\r
3767   HBITMAP oldBitmap;\r
3768   RECT Rect;\r
3769   HRGN clips[MAX_CLIPS];\r
3770   ChessSquare dragged_piece = EmptySquare;\r
3771   int nr = twoBoards*partnerUp;\r
3772 \r
3773   /* I'm undecided on this - this function figures out whether a full\r
3774    * repaint is necessary on its own, so there's no real reason to have the\r
3775    * caller tell it that.  I think this can safely be set to FALSE - but\r
3776    * if we trust the callers not to request full repaints unnessesarily, then\r
3777    * we could skip some clipping work.  In other words, only request a full\r
3778    * redraw when the majority of pieces have changed positions (ie. flip, \r
3779    * gamestart and similar)  --Hawk\r
3780    */\r
3781   Boolean fullrepaint = repaint;\r
3782 \r
3783   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3784 \r
3785   if( DrawPositionNeedsFullRepaint() ) {\r
3786       fullrepaint = TRUE;\r
3787   }\r
3788 \r
3789   if (board == NULL) {\r
3790     if (!lastReqValid[nr]) {\r
3791       return;\r
3792     }\r
3793     board = lastReq[nr];\r
3794   } else {\r
3795     CopyBoard(lastReq[nr], board);\r
3796     lastReqValid[nr] = 1;\r
3797   }\r
3798 \r
3799   if (doingSizing) {\r
3800     return;\r
3801   }\r
3802 \r
3803   if (IsIconic(hwndMain)) {\r
3804     return;\r
3805   }\r
3806 \r
3807   if (hdc == NULL) {\r
3808     hdc = GetDC(hwndMain);\r
3809     if (!appData.monoMode) {\r
3810       SelectPalette(hdc, hPal, FALSE);\r
3811       RealizePalette(hdc);\r
3812     }\r
3813     releaseDC = TRUE;\r
3814   } else {\r
3815     releaseDC = FALSE;\r
3816   }\r
3817 \r
3818   /* Create some work-DCs */\r
3819   hdcmem = CreateCompatibleDC(hdc);\r
3820   tmphdc = CreateCompatibleDC(hdc);\r
3821 \r
3822   /* If dragging is in progress, we temporarely remove the piece */\r
3823   /* [HGM] or temporarily decrease count if stacked              */\r
3824   /*       !! Moved to before board compare !!                   */\r
3825   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3826     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3827     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3828             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3829         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3830     } else \r
3831     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3832             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3833         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3834     } else \r
3835         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3836   }\r
3837 \r
3838   /* Figure out which squares need updating by comparing the \r
3839    * newest board with the last drawn board and checking if\r
3840    * flipping has changed.\r
3841    */\r
3842   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3843     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3844       for (column = 0; column < BOARD_WIDTH; column++) {\r
3845         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3846           SquareToPos(row, column, &x, &y);\r
3847           clips[num_clips++] =\r
3848             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3849         }\r
3850       }\r
3851     }\r
3852    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3853     for (i=0; i<2; i++) {\r
3854       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3855           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3856         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3857             lastDrawnHighlight.sq[i].y >= 0) {\r
3858           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3859                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3860           clips[num_clips++] =\r
3861             CreateRectRgn(x - lineGap, y - lineGap, \r
3862                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3863         }\r
3864         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3865           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3866           clips[num_clips++] =\r
3867             CreateRectRgn(x - lineGap, y - lineGap, \r
3868                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3869         }\r
3870       }\r
3871     }\r
3872     for (i=0; i<2; i++) {\r
3873       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3874           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3875         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3876             lastDrawnPremove.sq[i].y >= 0) {\r
3877           SquareToPos(lastDrawnPremove.sq[i].y,\r
3878                       lastDrawnPremove.sq[i].x, &x, &y);\r
3879           clips[num_clips++] =\r
3880             CreateRectRgn(x - lineGap, y - lineGap, \r
3881                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3882         }\r
3883         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3884             premoveHighlightInfo.sq[i].y >= 0) {\r
3885           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3886                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3887           clips[num_clips++] =\r
3888             CreateRectRgn(x - lineGap, y - lineGap, \r
3889                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3890         }\r
3891       }\r
3892     }\r
3893    } else { // nr == 1\r
3894         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3895         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3896         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3897         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3898       for (i=0; i<2; i++) {\r
3899         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3900             partnerHighlightInfo.sq[i].y >= 0) {\r
3901           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3902                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3903           clips[num_clips++] =\r
3904             CreateRectRgn(x - lineGap, y - lineGap, \r
3905                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3906         }\r
3907         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3908             oldPartnerHighlight.sq[i].y >= 0) {\r
3909           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3910                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3911           clips[num_clips++] =\r
3912             CreateRectRgn(x - lineGap, y - lineGap, \r
3913                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3914         }\r
3915       }\r
3916    }\r
3917   } else {\r
3918     fullrepaint = TRUE;\r
3919   }\r
3920 \r
3921   /* Create a buffer bitmap - this is the actual bitmap\r
3922    * being written to.  When all the work is done, we can\r
3923    * copy it to the real DC (the screen).  This avoids\r
3924    * the problems with flickering.\r
3925    */\r
3926   GetClientRect(hwndMain, &Rect);\r
3927   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3928                                         Rect.bottom-Rect.top+1);\r
3929   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3930   if (!appData.monoMode) {\r
3931     SelectPalette(hdcmem, hPal, FALSE);\r
3932   }\r
3933 \r
3934   /* Create clips for dragging */\r
3935   if (!fullrepaint) {\r
3936     if (dragInfo.from.x >= 0) {\r
3937       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3938       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3939     }\r
3940     if (dragInfo.start.x >= 0) {\r
3941       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3942       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3943     }\r
3944     if (dragInfo.pos.x >= 0) {\r
3945       x = dragInfo.pos.x - squareSize / 2;\r
3946       y = dragInfo.pos.y - squareSize / 2;\r
3947       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3948     }\r
3949     if (dragInfo.lastpos.x >= 0) {\r
3950       x = dragInfo.lastpos.x - squareSize / 2;\r
3951       y = dragInfo.lastpos.y - squareSize / 2;\r
3952       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3953     }\r
3954   }\r
3955 \r
3956   /* Are we animating a move?  \r
3957    * If so, \r
3958    *   - remove the piece from the board (temporarely)\r
3959    *   - calculate the clipping region\r
3960    */\r
3961   if (!fullrepaint) {\r
3962     if (animInfo.piece != EmptySquare) {\r
3963       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3964       x = boardRect.left + animInfo.lastpos.x;\r
3965       y = boardRect.top + animInfo.lastpos.y;\r
3966       x2 = boardRect.left + animInfo.pos.x;\r
3967       y2 = boardRect.top + animInfo.pos.y;\r
3968       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3969       /* Slight kludge.  The real problem is that after AnimateMove is\r
3970          done, the position on the screen does not match lastDrawn.\r
3971          This currently causes trouble only on e.p. captures in\r
3972          atomic, where the piece moves to an empty square and then\r
3973          explodes.  The old and new positions both had an empty square\r
3974          at the destination, but animation has drawn a piece there and\r
3975          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3976       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3977     }\r
3978   }\r
3979 \r
3980   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3981   if (num_clips == 0)\r
3982     fullrepaint = TRUE;\r
3983 \r
3984   /* Set clipping on the memory DC */\r
3985   if (!fullrepaint) {\r
3986     SelectClipRgn(hdcmem, clips[0]);\r
3987     for (x = 1; x < num_clips; x++) {\r
3988       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3989         abort();  // this should never ever happen!\r
3990     }\r
3991   }\r
3992 \r
3993   /* Do all the drawing to the memory DC */\r
3994   if(explodeInfo.radius) { // [HGM] atomic\r
3995         HBRUSH oldBrush;\r
3996         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3997         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3998         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3999         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4000         x += squareSize/2;\r
4001         y += squareSize/2;\r
4002         if(!fullrepaint) {\r
4003           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4004           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4005         }\r
4006         DrawGridOnDC(hdcmem);\r
4007         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
4008         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
4009         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4010         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
4011         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4012         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4013         SelectObject(hdcmem, oldBrush);\r
4014   } else {\r
4015     if(border) DrawBackgroundOnDC(hdcmem);\r
4016     DrawGridOnDC(hdcmem);\r
4017     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
4018         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
4019         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
4020     } else {\r
4021         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
4022         oldPartnerHighlight = partnerHighlightInfo;\r
4023     }\r
4024     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4025   }\r
4026   if(nr == 0) // [HGM] dual: markers only on left board\r
4027   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4028     for (column = 0; column < BOARD_WIDTH; column++) {\r
4029         if (marker[row][column]) { // marker changes only occur with full repaint!\r
4030             HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);\r
4031             SquareToPos(row, column, &x, &y);\r
4032             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
4033                           x + 3*squareSize/4, y + 3*squareSize/4);\r
4034             SelectObject(hdcmem, oldBrush);\r
4035         }\r
4036     }\r
4037   }\r
4038 \r
4039   if( appData.highlightMoveWithArrow ) {\r
4040 \r
4041     DrawArrowHighlight(hdcmem);\r
4042   }\r
4043 \r
4044   DrawCoordsOnDC(hdcmem);\r
4045 \r
4046   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
4047                  /* to make sure lastDrawn contains what is actually drawn */\r
4048 \r
4049   /* Put the dragged piece back into place and draw it (out of place!) */\r
4050     if (dragged_piece != EmptySquare) {\r
4051     /* [HGM] or restack */\r
4052     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4053                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4054     else\r
4055     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4056                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4057 \r
4058     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4059     x = dragInfo.pos.x - squareSize / 2;\r
4060     y = dragInfo.pos.y - squareSize / 2;\r
4061     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
4062                   ((int) dragInfo.piece < (int) BlackPawn), \r
4063                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4064   }   \r
4065   \r
4066   /* Put the animated piece back into place and draw it */\r
4067   if (animInfo.piece != EmptySquare) {\r
4068     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4069     x = boardRect.left + animInfo.pos.x;\r
4070     y = boardRect.top + animInfo.pos.y;\r
4071     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4072                   ((int) animInfo.piece < (int) BlackPawn),\r
4073                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4074   }\r
4075 \r
4076   /* Release the bufferBitmap by selecting in the old bitmap \r
4077    * and delete the memory DC\r
4078    */\r
4079   SelectObject(hdcmem, oldBitmap);\r
4080   DeleteDC(hdcmem);\r
4081 \r
4082   /* Set clipping on the target DC */\r
4083   if (!fullrepaint) {\r
4084     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
4085         RECT rect;\r
4086         GetRgnBox(clips[x], &rect);\r
4087         DeleteObject(clips[x]);\r
4088         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
4089                           rect.right + wpMain.width/2, rect.bottom);\r
4090     }\r
4091     SelectClipRgn(hdc, clips[0]);\r
4092     for (x = 1; x < num_clips; x++) {\r
4093       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4094         abort();   // this should never ever happen!\r
4095     } \r
4096   }\r
4097 \r
4098   /* Copy the new bitmap onto the screen in one go.\r
4099    * This way we avoid any flickering\r
4100    */\r
4101   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4102   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
4103          boardRect.right - boardRect.left,\r
4104          boardRect.bottom - boardRect.top,\r
4105          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4106   if(saveDiagFlag) { \r
4107     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
4108     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4109     HBITMAP src = bufferBitmap, obmp; HDC tmp = CreateCompatibleDC(hdc);\r
4110 \r
4111     bufferBitmap = CreateCompatibleBitmap(hdc, boardRect.right-boardRect.left, Rect.bottom-Rect.top-2*OUTER_MARGIN);\r
4112     obmp = SelectObject(tmp, bufferBitmap);\r
4113     BitBlt(tmp, 0, 0, boardRect.right - boardRect.left, Rect.bottom - Rect.top - 2*OUTER_MARGIN,\r
4114            tmphdc, boardRect.left, OUTER_MARGIN, SRCCOPY);\r
4115     GetObject(bufferBitmap, sizeof(b), &b);\r
4116     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
4117         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4118         bih.biWidth = b.bmWidth;\r
4119         bih.biHeight = b.bmHeight;\r
4120         bih.biPlanes = 1;\r
4121         bih.biBitCount = b.bmBitsPixel;\r
4122         bih.biCompression = 0;\r
4123         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4124         bih.biXPelsPerMeter = 0;\r
4125         bih.biYPelsPerMeter = 0;\r
4126         bih.biClrUsed = 0;\r
4127         bih.biClrImportant = 0;\r
4128 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4129 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4130         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4131 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4132 \r
4133         wb = b.bmWidthBytes;\r
4134         // count colors\r
4135         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4136                 int k = ((int*) pData)[i];\r
4137                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4138                 if(j >= 16) break;\r
4139                 color[j] = k;\r
4140                 if(j >= nrColors) nrColors = j+1;\r
4141         }\r
4142         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4143                 INT p = 0;\r
4144                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4145                     for(w=0; w<(wb>>2); w+=2) {\r
4146                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4147                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4148                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4149                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4150                         pData[p++] = m | j<<4;\r
4151                     }\r
4152                     while(p&3) pData[p++] = 0;\r
4153                 }\r
4154                 fac = 3;\r
4155                 wb = ((wb+31)>>5)<<2;\r
4156         }\r
4157         // write BITMAPFILEHEADER\r
4158         fprintf(diagFile, "BM");\r
4159         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4160         fputDW(diagFile, 0);\r
4161         fputDW(diagFile, 0x36 + (fac?64:0));\r
4162         // write BITMAPINFOHEADER\r
4163         fputDW(diagFile, 40);\r
4164         fputDW(diagFile, b.bmWidth);\r
4165         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4166         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4167         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4168         fputDW(diagFile, 0);\r
4169         fputDW(diagFile, 0);\r
4170         fputDW(diagFile, 0);\r
4171         fputDW(diagFile, 0);\r
4172         fputDW(diagFile, 0);\r
4173         fputDW(diagFile, 0);\r
4174         // write color table\r
4175         if(fac)\r
4176         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4177         // write bitmap data\r
4178         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4179                 fputc(pData[i], diagFile);\r
4180         free(pData);\r
4181      }\r
4182      DeleteObject(bufferBitmap); bufferBitmap = src;\r
4183      SelectObject(tmp, obmp);\r
4184      DeleteDC(tmp);\r
4185   }\r
4186 \r
4187   SelectObject(tmphdc, oldBitmap);\r
4188 \r
4189   /* Massive cleanup */\r
4190   for (x = 0; x < num_clips; x++)\r
4191     DeleteObject(clips[x]);\r
4192 \r
4193   DeleteDC(tmphdc);\r
4194   DeleteObject(bufferBitmap);\r
4195 \r
4196   if (releaseDC) \r
4197     ReleaseDC(hwndMain, hdc);\r
4198   \r
4199   if (lastDrawnFlipView != flipView && nr == 0) {\r
4200     if (flipView)\r
4201       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4202     else\r
4203       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4204   }\r
4205 \r
4206 /*  CopyBoard(lastDrawn, board);*/\r
4207   lastDrawnHighlight = highlightInfo;\r
4208   lastDrawnPremove   = premoveHighlightInfo;\r
4209   lastDrawnFlipView = flipView;\r
4210   lastDrawnValid[nr] = 1;\r
4211 }\r
4212 \r
4213 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4214 int\r
4215 SaveDiagram(f)\r
4216      FILE *f;\r
4217 {\r
4218     saveDiagFlag = 1; diagFile = f;\r
4219     HDCDrawPosition(NULL, TRUE, NULL);\r
4220     saveDiagFlag = 0;\r
4221 \r
4222     fclose(f);\r
4223     return TRUE;\r
4224 }\r
4225 \r
4226 \r
4227 /*---------------------------------------------------------------------------*\\r
4228 | CLIENT PAINT PROCEDURE\r
4229 |   This is the main event-handler for the WM_PAINT message.\r
4230 |\r
4231 \*---------------------------------------------------------------------------*/\r
4232 VOID\r
4233 PaintProc(HWND hwnd)\r
4234 {\r
4235   HDC         hdc;\r
4236   PAINTSTRUCT ps;\r
4237   HFONT       oldFont;\r
4238 \r
4239   if((hdc = BeginPaint(hwnd, &ps))) {\r
4240     if (IsIconic(hwnd)) {\r
4241       DrawIcon(hdc, 2, 2, iconCurrent);\r
4242     } else {\r
4243       if (!appData.monoMode) {\r
4244         SelectPalette(hdc, hPal, FALSE);\r
4245         RealizePalette(hdc);\r
4246       }\r
4247       HDCDrawPosition(hdc, 1, NULL);\r
4248       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4249         flipView = !flipView; partnerUp = !partnerUp;\r
4250         HDCDrawPosition(hdc, 1, NULL);\r
4251         flipView = !flipView; partnerUp = !partnerUp;\r
4252       }\r
4253       oldFont =\r
4254         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4255       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4256                  ETO_CLIPPED|ETO_OPAQUE,\r
4257                  &messageRect, messageText, strlen(messageText), NULL);\r
4258       SelectObject(hdc, oldFont);\r
4259       DisplayBothClocks();\r
4260       DisplayLogos();\r
4261     }\r
4262     EndPaint(hwnd,&ps);\r
4263   }\r
4264 \r
4265   return;\r
4266 }\r
4267 \r
4268 \r
4269 /*\r
4270  * If the user selects on a border boundary, return -1; if off the board,\r
4271  *   return -2.  Otherwise map the event coordinate to the square.\r
4272  * The offset boardRect.left or boardRect.top must already have been\r
4273  *   subtracted from x.\r
4274  */\r
4275 int EventToSquare(x, limit)\r
4276      int x, limit;\r
4277 {\r
4278   if (x <= border)\r
4279     return -2;\r
4280   if (x < lineGap + border)\r
4281     return -1;\r
4282   x -= lineGap + border;\r
4283   if ((x % (squareSize + lineGap)) >= squareSize)\r
4284     return -1;\r
4285   x /= (squareSize + lineGap);\r
4286     if (x >= limit)\r
4287     return -2;\r
4288   return x;\r
4289 }\r
4290 \r
4291 typedef struct {\r
4292   char piece;\r
4293   int command;\r
4294   char* name;\r
4295 } DropEnable;\r
4296 \r
4297 DropEnable dropEnables[] = {\r
4298   { 'P', DP_Pawn, N_("Pawn") },\r
4299   { 'N', DP_Knight, N_("Knight") },\r
4300   { 'B', DP_Bishop, N_("Bishop") },\r
4301   { 'R', DP_Rook, N_("Rook") },\r
4302   { 'Q', DP_Queen, N_("Queen") },\r
4303 };\r
4304 \r
4305 VOID\r
4306 SetupDropMenu(HMENU hmenu)\r
4307 {\r
4308   int i, count, enable;\r
4309   char *p;\r
4310   extern char white_holding[], black_holding[];\r
4311   char item[MSG_SIZ];\r
4312 \r
4313   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4314     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4315                dropEnables[i].piece);\r
4316     count = 0;\r
4317     while (p && *p++ == dropEnables[i].piece) count++;\r
4318       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4319     enable = count > 0 || !appData.testLegality\r
4320       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4321                       && !appData.icsActive);\r
4322     ModifyMenu(hmenu, dropEnables[i].command,\r
4323                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4324                dropEnables[i].command, item);\r
4325   }\r
4326 }\r
4327 \r
4328 void DragPieceBegin(int x, int y, Boolean instantly)\r
4329 {\r
4330       dragInfo.lastpos.x = boardRect.left + x;\r
4331       dragInfo.lastpos.y = boardRect.top + y;\r
4332       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4333       dragInfo.from.x = fromX;\r
4334       dragInfo.from.y = fromY;\r
4335       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4336       dragInfo.start = dragInfo.from;\r
4337       SetCapture(hwndMain);\r
4338 }\r
4339 \r
4340 void DragPieceEnd(int x, int y)\r
4341 {\r
4342     ReleaseCapture();\r
4343     dragInfo.start.x = dragInfo.start.y = -1;\r
4344     dragInfo.from = dragInfo.start;\r
4345     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4346 }\r
4347 \r
4348 void ChangeDragPiece(ChessSquare piece)\r
4349 {\r
4350     dragInfo.piece = piece;\r
4351 }\r
4352 \r
4353 /* Event handler for mouse messages */\r
4354 VOID\r
4355 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4356 {\r
4357   int x, y, menuNr;\r
4358   POINT pt;\r
4359   static int recursive = 0;\r
4360   HMENU hmenu;\r
4361   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4362 \r
4363   if (recursive) {\r
4364     if (message == WM_MBUTTONUP) {\r
4365       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4366          to the middle button: we simulate pressing the left button too!\r
4367          */\r
4368       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4369       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4370     }\r
4371     return;\r
4372   }\r
4373   recursive++;\r
4374   \r
4375   pt.x = LOWORD(lParam);\r
4376   pt.y = HIWORD(lParam);\r
4377   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4378   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4379   if (!flipView && y >= 0) {\r
4380     y = BOARD_HEIGHT - 1 - y;\r
4381   }\r
4382   if (flipView && x >= 0) {\r
4383     x = BOARD_WIDTH - 1 - x;\r
4384   }\r
4385 \r
4386   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4387   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4388 \r
4389   switch (message) {\r
4390   case WM_LBUTTONDOWN:\r
4391       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4392         ClockClick(flipClock); break;\r
4393       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4394         ClockClick(!flipClock); break;\r
4395       }\r
4396     if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging\r
4397       dragInfo.start.x = dragInfo.start.y = -1;\r
4398       dragInfo.from = dragInfo.start;\r
4399     }\r
4400     if(fromX == -1 && frozen) { // not sure where this is for\r
4401                 fromX = fromY = -1; \r
4402       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4403       break;\r
4404     }\r
4405       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4406       DrawPosition(TRUE, NULL);\r
4407     break;\r
4408 \r
4409   case WM_LBUTTONUP:\r
4410       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4411       DrawPosition(TRUE, NULL);\r
4412     break;\r
4413 \r
4414   case WM_MOUSEMOVE:\r
4415     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4416     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4417     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4418     if ((appData.animateDragging || appData.highlightDragging)\r
4419         && (wParam & MK_LBUTTON || dragging == 2)\r
4420         && dragInfo.from.x >= 0) \r
4421     {\r
4422       BOOL full_repaint = FALSE;\r
4423 \r
4424       if (appData.animateDragging) {\r
4425         dragInfo.pos = pt;\r
4426       }\r
4427       if (appData.highlightDragging) {\r
4428         HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);\r
4429         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4430             full_repaint = TRUE;\r
4431         }\r
4432       }\r
4433       \r
4434       DrawPosition( full_repaint, NULL);\r
4435       \r
4436       dragInfo.lastpos = dragInfo.pos;\r
4437     }\r
4438     break;\r
4439 \r
4440   case WM_MOUSEWHEEL: // [DM]\r
4441     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4442        /* Mouse Wheel is being rolled forward\r
4443         * Play moves forward\r
4444         */\r
4445        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4446                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4447        /* Mouse Wheel is being rolled backward\r
4448         * Play moves backward\r
4449         */\r
4450        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4451                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4452     }\r
4453     break;\r
4454 \r
4455   case WM_MBUTTONUP:\r
4456   case WM_RBUTTONUP:\r
4457     ReleaseCapture();\r
4458     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4459     break;\r
4460  \r
4461   case WM_MBUTTONDOWN:\r
4462   case WM_RBUTTONDOWN:\r
4463     ErrorPopDown();\r
4464     ReleaseCapture();\r
4465     fromX = fromY = -1;\r
4466     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4467     dragInfo.start.x = dragInfo.start.y = -1;\r
4468     dragInfo.from = dragInfo.start;\r
4469     dragInfo.lastpos = dragInfo.pos;\r
4470     if (appData.highlightDragging) {\r
4471       ClearHighlights();\r
4472     }\r
4473     if(y == -2) {\r
4474       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4475       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4476           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4477       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4478           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4479       }\r
4480       break;\r
4481     }\r
4482     DrawPosition(TRUE, NULL);\r
4483 \r
4484     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4485     switch (menuNr) {\r
4486     case 0:\r
4487       if (message == WM_MBUTTONDOWN) {\r
4488         buttonCount = 3;  /* even if system didn't think so */\r
4489         if (wParam & MK_SHIFT) \r
4490           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4491         else\r
4492           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4493       } else { /* message == WM_RBUTTONDOWN */\r
4494         /* Just have one menu, on the right button.  Windows users don't\r
4495            think to try the middle one, and sometimes other software steals\r
4496            it, or it doesn't really exist. */\r
4497         if(gameInfo.variant != VariantShogi)\r
4498             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4499         else\r
4500             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4501       }\r
4502       break;\r
4503     case 2:\r
4504       SetCapture(hwndMain);\r
4505       break;\r
4506     case 1:\r
4507       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4508       SetupDropMenu(hmenu);\r
4509       MenuPopup(hwnd, pt, hmenu, -1);\r
4510     default:\r
4511       break;\r
4512     }\r
4513     break;\r
4514   }\r
4515 \r
4516   recursive--;\r
4517 }\r
4518 \r
4519 /* Preprocess messages for buttons in main window */\r
4520 LRESULT CALLBACK\r
4521 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4522 {\r
4523   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4524   int i, dir;\r
4525 \r
4526   for (i=0; i<N_BUTTONS; i++) {\r
4527     if (buttonDesc[i].id == id) break;\r
4528   }\r
4529   if (i == N_BUTTONS) return 0;\r
4530   switch (message) {\r
4531   case WM_KEYDOWN:\r
4532     switch (wParam) {\r
4533     case VK_LEFT:\r
4534     case VK_RIGHT:\r
4535       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4536       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4537       return TRUE;\r
4538     }\r
4539     break;\r
4540   case WM_CHAR:\r
4541     switch (wParam) {\r
4542     case '\r':\r
4543       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4544       return TRUE;\r
4545     default:\r
4546       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4547         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4548         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4549         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4550         SetFocus(h);\r
4551         SendMessage(h, WM_CHAR, wParam, lParam);\r
4552         return TRUE;\r
4553       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4554         TypeInEvent((char)wParam);\r
4555       }\r
4556       break;\r
4557     }\r
4558     break;\r
4559   }\r
4560   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4561 }\r
4562 \r
4563 static int promoStyle;\r
4564 \r
4565 /* Process messages for Promotion dialog box */\r
4566 LRESULT CALLBACK\r
4567 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4568 {\r
4569 \r
4570   char promoChar;\r
4571 \r
4572   switch (message) {\r
4573 \r
4574   case WM_INITDIALOG: /* message: initialize dialog box */\r
4575     /* Center the dialog over the application window */\r
4576     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4577     Translate(hDlg, DLG_PromotionKing);\r
4578     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4579       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4580        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4581        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4582                SW_SHOW : SW_HIDE);\r
4583     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4584     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4585        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4586          PieceToChar(WhiteAngel) != '~') ||\r
4587         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4588          PieceToChar(BlackAngel) != '~')   ) ?\r
4589                SW_SHOW : SW_HIDE);\r
4590     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4591        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4592          PieceToChar(WhiteMarshall) != '~') ||\r
4593         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4594          PieceToChar(BlackMarshall) != '~')   ) ?\r
4595                SW_SHOW : SW_HIDE);\r
4596     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4597     ShowWindow(GetDlgItem(hDlg, PB_Rook),   !promoStyle ? SW_SHOW : SW_HIDE);\r
4598     ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);\r
4599     if(promoStyle) {\r
4600         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4601         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4602         SetWindowText(hDlg, "Promote?");\r
4603     }\r
4604     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4605        gameInfo.variant == VariantSuper ?\r
4606                SW_SHOW : SW_HIDE);\r
4607     return TRUE;\r
4608 \r
4609   case WM_COMMAND: /* message: received a command */\r
4610     switch (LOWORD(wParam)) {\r
4611     case IDCANCEL:\r
4612       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4613       ClearHighlights();\r
4614       DrawPosition(FALSE, NULL);\r
4615       return TRUE;\r
4616     case PB_King:\r
4617       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4618       break;\r
4619     case PB_Queen:\r
4620       promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4621       break;\r
4622     case PB_Rook:\r
4623       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4624       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4625       break;\r
4626     case PB_Bishop:\r
4627       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4628       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4629       break;\r
4630     case PB_Chancellor:\r
4631       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4632       break;\r
4633     case PB_Archbishop:\r
4634       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4635       break;\r
4636     case PB_Knight:\r
4637       promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR : \r
4638                   ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));\r
4639       break;\r
4640     default:\r
4641       return FALSE;\r
4642     }\r
4643     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4644     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4645     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4646     fromX = fromY = -1;\r
4647     if (!appData.highlightLastMove) {\r
4648       ClearHighlights();\r
4649       DrawPosition(FALSE, NULL);\r
4650     }\r
4651     return TRUE;\r
4652   }\r
4653   return FALSE;\r
4654 }\r
4655 \r
4656 /* Pop up promotion dialog */\r
4657 VOID\r
4658 PromotionPopup(HWND hwnd)\r
4659 {\r
4660   FARPROC lpProc;\r
4661 \r
4662   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4663   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4664     hwnd, (DLGPROC)lpProc);\r
4665   FreeProcInstance(lpProc);\r
4666 }\r
4667 \r
4668 void\r
4669 PromotionPopUp(char choice)\r
4670 {\r
4671   promoStyle = (choice == '+' || IS_SHOGI(gameInfo.variant));\r
4672   DrawPosition(TRUE, NULL);\r
4673   PromotionPopup(hwndMain);\r
4674 }\r
4675 \r
4676 VOID\r
4677 LoadGameDialog(HWND hwnd, char* title)\r
4678 {\r
4679   UINT number = 0;\r
4680   FILE *f;\r
4681   char fileTitle[MSG_SIZ];\r
4682   f = OpenFileDialog(hwnd, "rb", "",\r
4683                      appData.oldSaveStyle ? "gam" : "pgn",\r
4684                      GAME_FILT,\r
4685                      title, &number, fileTitle, NULL);\r
4686   if (f != NULL) {\r
4687     cmailMsgLoaded = FALSE;\r
4688     if (number == 0) {\r
4689       int error = GameListBuild(f);\r
4690       if (error) {\r
4691         DisplayError(_("Cannot build game list"), error);\r
4692       } else if (!ListEmpty(&gameList) &&\r
4693                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4694         GameListPopUp(f, fileTitle);\r
4695         return;\r
4696       }\r
4697       GameListDestroy();\r
4698       number = 1;\r
4699     }\r
4700     LoadGame(f, number, fileTitle, FALSE);\r
4701   }\r
4702 }\r
4703 \r
4704 int get_term_width()\r
4705 {\r
4706     HDC hdc;\r
4707     TEXTMETRIC tm;\r
4708     RECT rc;\r
4709     HFONT hfont, hold_font;\r
4710     LOGFONT lf;\r
4711     HWND hText;\r
4712 \r
4713     if (hwndConsole)\r
4714         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4715     else\r
4716         return 79;\r
4717 \r
4718     // get the text metrics\r
4719     hdc = GetDC(hText);\r
4720     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4721     if (consoleCF.dwEffects & CFE_BOLD)\r
4722         lf.lfWeight = FW_BOLD;\r
4723     if (consoleCF.dwEffects & CFE_ITALIC)\r
4724         lf.lfItalic = TRUE;\r
4725     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4726         lf.lfStrikeOut = TRUE;\r
4727     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4728         lf.lfUnderline = TRUE;\r
4729     hfont = CreateFontIndirect(&lf);\r
4730     hold_font = SelectObject(hdc, hfont);\r
4731     GetTextMetrics(hdc, &tm);\r
4732     SelectObject(hdc, hold_font);\r
4733     DeleteObject(hfont);\r
4734     ReleaseDC(hText, hdc);\r
4735 \r
4736     // get the rectangle\r
4737     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4738 \r
4739     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4740 }\r
4741 \r
4742 void UpdateICSWidth(HWND hText)\r
4743 {\r
4744     LONG old_width, new_width;\r
4745 \r
4746     new_width = get_term_width(hText, FALSE);\r
4747     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4748     if (new_width != old_width)\r
4749     {\r
4750         ics_update_width(new_width);\r
4751         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4752     }\r
4753 }\r
4754 \r
4755 VOID\r
4756 ChangedConsoleFont()\r
4757 {\r
4758   CHARFORMAT cfmt;\r
4759   CHARRANGE tmpsel, sel;\r
4760   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4761   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4762   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4763   PARAFORMAT paraf;\r
4764 \r
4765   cfmt.cbSize = sizeof(CHARFORMAT);\r
4766   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4767     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4768                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4769   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4770    * size.  This was undocumented in the version of MSVC++ that I had\r
4771    * when I wrote the code, but is apparently documented now.\r
4772    */\r
4773   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4774   cfmt.bCharSet = f->lf.lfCharSet;\r
4775   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4776   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4777   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4778   /* Why are the following seemingly needed too? */\r
4779   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4780   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4781   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4782   tmpsel.cpMin = 0;\r
4783   tmpsel.cpMax = -1; /*999999?*/\r
4784   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4785   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4786   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4787    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4788    */\r
4789   paraf.cbSize = sizeof(paraf);\r
4790   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4791   paraf.dxStartIndent = 0;\r
4792   paraf.dxOffset = WRAP_INDENT;\r
4793   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4794   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4795   UpdateICSWidth(hText);\r
4796 }\r
4797 \r
4798 /*---------------------------------------------------------------------------*\\r
4799  *\r
4800  * Window Proc for main window\r
4801  *\r
4802 \*---------------------------------------------------------------------------*/\r
4803 \r
4804 /* Process messages for main window, etc. */\r
4805 LRESULT CALLBACK\r
4806 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4807 {\r
4808   FARPROC lpProc;\r
4809   int wmId;\r
4810   char *defName;\r
4811   FILE *f;\r
4812   UINT number;\r
4813   char fileTitle[MSG_SIZ];\r
4814   static SnapData sd;\r
4815   static int peek=0;\r
4816 \r
4817   switch (message) {\r
4818 \r
4819   case WM_PAINT: /* message: repaint portion of window */\r
4820     PaintProc(hwnd);\r
4821     break;\r
4822 \r
4823   case WM_ERASEBKGND:\r
4824     if (IsIconic(hwnd)) {\r
4825       /* Cheat; change the message */\r
4826       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4827     } else {\r
4828       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4829     }\r
4830     break;\r
4831 \r
4832   case WM_LBUTTONDOWN:\r
4833   case WM_MBUTTONDOWN:\r
4834   case WM_RBUTTONDOWN:\r
4835   case WM_LBUTTONUP:\r
4836   case WM_MBUTTONUP:\r
4837   case WM_RBUTTONUP:\r
4838   case WM_MOUSEMOVE:\r
4839   case WM_MOUSEWHEEL:\r
4840     MouseEvent(hwnd, message, wParam, lParam);\r
4841     break;\r
4842 \r
4843   case WM_KEYUP:\r
4844     if((char)wParam == '\b') {\r
4845       ForwardEvent(); peek = 0;\r
4846     }\r
4847 \r
4848     JAWS_KBUP_NAVIGATION\r
4849 \r
4850     break;\r
4851 \r
4852   case WM_KEYDOWN:\r
4853     if((char)wParam == '\b') {\r
4854       if(!peek) BackwardEvent(), peek = 1;\r
4855     }\r
4856 \r
4857     JAWS_KBDOWN_NAVIGATION\r
4858 \r
4859     break;\r
4860 \r
4861   case WM_CHAR:\r
4862     \r
4863     JAWS_ALT_INTERCEPT\r
4864 \r
4865     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4866         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4867         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4868         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4869         SetFocus(h);\r
4870         SendMessage(h, message, wParam, lParam);\r
4871     } else if(lParam != KF_REPEAT) {\r
4872         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4873                 TypeInEvent((char)wParam);\r
4874         } else if((char)wParam == 003) CopyGameToClipboard();\r
4875          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4876     }\r
4877 \r
4878     break;\r
4879 \r
4880   case WM_PALETTECHANGED:\r
4881     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4882       int nnew;\r
4883       HDC hdc = GetDC(hwndMain);\r
4884       SelectPalette(hdc, hPal, TRUE);\r
4885       nnew = RealizePalette(hdc);\r
4886       if (nnew > 0) {\r
4887         paletteChanged = TRUE;\r
4888 \r
4889         InvalidateRect(hwnd, &boardRect, FALSE);\r
4890       }\r
4891       ReleaseDC(hwnd, hdc);\r
4892     }\r
4893     break;\r
4894 \r
4895   case WM_QUERYNEWPALETTE:\r
4896     if (!appData.monoMode /*&& paletteChanged*/) {\r
4897       int nnew;\r
4898       HDC hdc = GetDC(hwndMain);\r
4899       paletteChanged = FALSE;\r
4900       SelectPalette(hdc, hPal, FALSE);\r
4901       nnew = RealizePalette(hdc);\r
4902       if (nnew > 0) {\r
4903         InvalidateRect(hwnd, &boardRect, FALSE);\r
4904       }\r
4905       ReleaseDC(hwnd, hdc);\r
4906       return TRUE;\r
4907     }\r
4908     return FALSE;\r
4909 \r
4910   case WM_COMMAND: /* message: command from application menu */\r
4911     wmId    = LOWORD(wParam);\r
4912 \r
4913     switch (wmId) {\r
4914     case IDM_NewGame:\r
4915       ResetGameEvent();\r
4916       SAY("new game enter a move to play against the computer with white");\r
4917       break;\r
4918 \r
4919     case IDM_NewGameFRC:\r
4920       if( NewGameFRC() == 0 ) {\r
4921         ResetGameEvent();\r
4922       }\r
4923       break;\r
4924 \r
4925     case IDM_NewVariant:\r
4926       NewVariantPopup(hwnd);\r
4927       break;\r
4928 \r
4929     case IDM_LoadGame:\r
4930       LoadGameDialog(hwnd, _("Load Game from File"));\r
4931       break;\r
4932 \r
4933     case IDM_LoadNextGame:\r
4934       ReloadGame(1);\r
4935       break;\r
4936 \r
4937     case IDM_LoadPrevGame:\r
4938       ReloadGame(-1);\r
4939       break;\r
4940 \r
4941     case IDM_ReloadGame:\r
4942       ReloadGame(0);\r
4943       break;\r
4944 \r
4945     case IDM_LoadPosition:\r
4946       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4947         Reset(FALSE, TRUE);\r
4948       }\r
4949       number = 1;\r
4950       f = OpenFileDialog(hwnd, "rb", "",\r
4951                          appData.oldSaveStyle ? "pos" : "fen",\r
4952                          POSITION_FILT,\r
4953                          _("Load Position from File"), &number, fileTitle, NULL);\r
4954       if (f != NULL) {\r
4955         LoadPosition(f, number, fileTitle);\r
4956       }\r
4957       break;\r
4958 \r
4959     case IDM_LoadNextPosition:\r
4960       ReloadPosition(1);\r
4961       break;\r
4962 \r
4963     case IDM_LoadPrevPosition:\r
4964       ReloadPosition(-1);\r
4965       break;\r
4966 \r
4967     case IDM_ReloadPosition:\r
4968       ReloadPosition(0);\r
4969       break;\r
4970 \r
4971     case IDM_SaveGame:\r
4972       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4973       f = OpenFileDialog(hwnd, "a", defName,\r
4974                          appData.oldSaveStyle ? "gam" : "pgn",\r
4975                          GAME_FILT,\r
4976                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4977       if (f != NULL) {\r
4978         SaveGame(f, 0, "");\r
4979       }\r
4980       break;\r
4981 \r
4982     case IDM_SavePosition:\r
4983       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4984       f = OpenFileDialog(hwnd, "a", defName,\r
4985                          appData.oldSaveStyle ? "pos" : "fen",\r
4986                          POSITION_FILT,\r
4987                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4988       if (f != NULL) {\r
4989         SavePosition(f, 0, "");\r
4990       }\r
4991       break;\r
4992 \r
4993     case IDM_SaveDiagram:\r
4994       defName = "diagram";\r
4995       f = OpenFileDialog(hwnd, "wb", defName,\r
4996                          "bmp",\r
4997                          DIAGRAM_FILT,\r
4998                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4999       if (f != NULL) {\r
5000         SaveDiagram(f);\r
5001       }\r
5002       break;\r
5003 \r
5004     case IDM_SaveSelected:\r
5005       f = OpenFileDialog(hwnd, "a", "",\r
5006                          "pgn",\r
5007                          GAME_FILT,\r
5008                          _("Save Game to File"), NULL, fileTitle, NULL);\r
5009       if (f != NULL) {\r
5010         SaveSelected(f, 0, "");\r
5011       }\r
5012       break;\r
5013 \r
5014     case IDM_CreateBook:\r
5015       CreateBookEvent();\r
5016       break;\r
5017 \r
5018     case IDM_CopyGame:\r
5019       CopyGameToClipboard();\r
5020       break;\r
5021 \r
5022     case IDM_PasteGame:\r
5023       PasteGameFromClipboard();\r
5024       break;\r
5025 \r
5026     case IDM_CopyGameListToClipboard:\r
5027       CopyGameListToClipboard();\r
5028       break;\r
5029 \r
5030     /* [AS] Autodetect FEN or PGN data */\r
5031     case IDM_PasteAny:\r
5032       PasteGameOrFENFromClipboard();\r
5033       break;\r
5034 \r
5035     /* [AS] Move history */\r
5036     case IDM_ShowMoveHistory:\r
5037         if( MoveHistoryIsUp() ) {\r
5038             MoveHistoryPopDown();\r
5039         }\r
5040         else {\r
5041             MoveHistoryPopUp();\r
5042         }\r
5043         break;\r
5044 \r
5045     /* [AS] Eval graph */\r
5046     case IDM_ShowEvalGraph:\r
5047         if( EvalGraphIsUp() ) {\r
5048             EvalGraphPopDown();\r
5049         }\r
5050         else {\r
5051             EvalGraphPopUp();\r
5052             SetFocus(hwndMain);\r
5053         }\r
5054         break;\r
5055 \r
5056     /* [AS] Engine output */\r
5057     case IDM_ShowEngineOutput:\r
5058         if( EngineOutputIsUp() ) {\r
5059             EngineOutputPopDown();\r
5060         }\r
5061         else {\r
5062             EngineOutputPopUp();\r
5063         }\r
5064         break;\r
5065 \r
5066     /* [AS] User adjudication */\r
5067     case IDM_UserAdjudication_White:\r
5068         UserAdjudicationEvent( +1 );\r
5069         break;\r
5070 \r
5071     case IDM_UserAdjudication_Black:\r
5072         UserAdjudicationEvent( -1 );\r
5073         break;\r
5074 \r
5075     case IDM_UserAdjudication_Draw:\r
5076         UserAdjudicationEvent( 0 );\r
5077         break;\r
5078 \r
5079     /* [AS] Game list options dialog */\r
5080     case IDM_GameListOptions:\r
5081       GameListOptions();\r
5082       break;\r
5083 \r
5084     case IDM_NewChat:\r
5085       ChatPopUp(NULL);\r
5086       break;\r
5087 \r
5088     case IDM_CopyPosition:\r
5089       CopyFENToClipboard();\r
5090       break;\r
5091 \r
5092     case IDM_PastePosition:\r
5093       PasteFENFromClipboard();\r
5094       break;\r
5095 \r
5096     case IDM_MailMove:\r
5097       MailMoveEvent();\r
5098       break;\r
5099 \r
5100     case IDM_ReloadCMailMsg:\r
5101       Reset(TRUE, TRUE);\r
5102       ReloadCmailMsgEvent(FALSE);\r
5103       break;\r
5104 \r
5105     case IDM_Minimize:\r
5106       ShowWindow(hwnd, SW_MINIMIZE);\r
5107       break;\r
5108 \r
5109     case IDM_Exit:\r
5110       ExitEvent(0);\r
5111       break;\r
5112 \r
5113     case IDM_MachineWhite:\r
5114       MachineWhiteEvent();\r
5115       /*\r
5116        * refresh the tags dialog only if it's visible\r
5117        */\r
5118       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5119           char *tags;\r
5120           tags = PGNTags(&gameInfo);\r
5121           TagsPopUp(tags, CmailMsg());\r
5122           free(tags);\r
5123       }\r
5124       SAY("computer starts playing white");\r
5125       break;\r
5126 \r
5127     case IDM_MachineBlack:\r
5128       MachineBlackEvent();\r
5129       /*\r
5130        * refresh the tags dialog only if it's visible\r
5131        */\r
5132       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5133           char *tags;\r
5134           tags = PGNTags(&gameInfo);\r
5135           TagsPopUp(tags, CmailMsg());\r
5136           free(tags);\r
5137       }\r
5138       SAY("computer starts playing black");\r
5139       break;\r
5140 \r
5141     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5142       if(matchMode) EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_GRAYED);\r
5143       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5144       break;\r
5145 \r
5146     case IDM_TwoMachines:\r
5147       TwoMachinesEvent();\r
5148       /*\r
5149 \r
5150        * refresh the tags dialog only if it's visible\r
5151        */\r
5152       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5153           char *tags;\r
5154           tags = PGNTags(&gameInfo);\r
5155           TagsPopUp(tags, CmailMsg());\r
5156           free(tags);\r
5157       }\r
5158       SAY("computer starts playing both sides");\r
5159       break;\r
5160 \r
5161     case IDM_AnalysisMode:\r
5162       if(AnalyzeModeEvent()) {\r
5163         SAY("analyzing current position");\r
5164       }\r
5165       break;\r
5166 \r
5167     case IDM_AnalyzeFile:\r
5168       AnalyzeFileEvent();\r
5169       break;\r
5170 \r
5171     case IDM_IcsClient:\r
5172       IcsClientEvent();\r
5173       break;\r
5174 \r
5175     case IDM_EditGame:\r
5176     case IDM_EditGame2:\r
5177       EditGameEvent();\r
5178       SAY("edit game");\r
5179       break;\r
5180 \r
5181     case IDM_EditPosition:\r
5182     case IDM_EditPosition2:\r
5183       EditPositionEvent();\r
5184       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5185       break;\r
5186 \r
5187     case IDM_Training:\r
5188       TrainingEvent();\r
5189       break;\r
5190 \r
5191     case IDM_ShowGameList:\r
5192       ShowGameListProc();\r
5193       break;\r
5194 \r
5195     case IDM_EditProgs1:\r
5196       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5197       break;\r
5198 \r
5199     case IDM_LoadProg1:\r
5200      LoadEnginePopUp(hwndMain, 0);\r
5201       break;\r
5202 \r
5203     case IDM_LoadProg2:\r
5204      LoadEnginePopUp(hwndMain, 1);\r
5205       break;\r
5206 \r
5207     case IDM_EditServers:\r
5208       EditTagsPopUp(icsNames, &icsNames);\r
5209       break;\r
5210 \r
5211     case IDM_EditTags:\r
5212     case IDM_Tags:\r
5213       EditTagsProc();\r
5214       break;\r
5215 \r
5216     case IDM_EditBook:\r
5217       EditBookEvent();\r
5218       break;\r
5219 \r
5220     case IDM_EditComment:\r
5221     case IDM_Comment:\r
5222       if (commentUp && editComment) {\r
5223         CommentPopDown();\r
5224       } else {\r
5225         EditCommentEvent();\r
5226       }\r
5227       break;\r
5228 \r
5229     case IDM_Pause:\r
5230       PauseEvent();\r
5231       break;\r
5232 \r
5233     case IDM_Accept:\r
5234       AcceptEvent();\r
5235       break;\r
5236 \r
5237     case IDM_Decline:\r
5238       DeclineEvent();\r
5239       break;\r
5240 \r
5241     case IDM_Rematch:\r
5242 \r
5243       RematchEvent();\r
5244       break;\r
5245 \r
5246     case IDM_CallFlag:\r
5247       CallFlagEvent();\r
5248       break;\r
5249 \r
5250     case IDM_Draw:\r
5251       DrawEvent();\r
5252       break;\r
5253 \r
5254     case IDM_Adjourn:\r
5255       AdjournEvent();\r
5256       break;\r
5257 \r
5258     case IDM_Abort:\r
5259       AbortEvent();\r
5260       break;\r
5261 \r
5262     case IDM_Resign:\r
5263       ResignEvent();\r
5264       break;\r
5265 \r
5266     case IDM_StopObserving:\r
5267       StopObservingEvent();\r
5268       break;\r
5269 \r
5270     case IDM_StopExamining:\r
5271       StopExaminingEvent();\r
5272       break;\r
5273 \r
5274     case IDM_Upload:\r
5275       UploadGameEvent();\r
5276       break;\r
5277 \r
5278     case IDM_TypeInMove:\r
5279       TypeInEvent('\000');\r
5280       break;\r
5281 \r
5282     case IDM_TypeInName:\r
5283       PopUpNameDialog('\000');\r
5284       break;\r
5285 \r
5286     case IDM_Backward:\r
5287       BackwardEvent();\r
5288       SetFocus(hwndMain);\r
5289       break;\r
5290 \r
5291     JAWS_MENU_ITEMS\r
5292 \r
5293     case IDM_Forward:\r
5294       ForwardEvent();\r
5295       SetFocus(hwndMain);\r
5296       break;\r
5297 \r
5298     case IDM_ToStart:\r
5299       ToStartEvent();\r
5300       SetFocus(hwndMain);\r
5301       break;\r
5302 \r
5303     case IDM_ToEnd:\r
5304       ToEndEvent();\r
5305       SetFocus(hwndMain);\r
5306       break;\r
5307 \r
5308     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5309     case OPT_GameListPrev:\r
5310       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5311       break;\r
5312 \r
5313     case IDM_Revert:\r
5314       RevertEvent(FALSE);\r
5315       break;\r
5316 \r
5317     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5318       RevertEvent(TRUE);\r
5319       break;\r
5320 \r
5321     case IDM_TruncateGame:\r
5322       TruncateGameEvent();\r
5323       break;\r
5324 \r
5325     case IDM_MoveNow:\r
5326       MoveNowEvent();\r
5327       break;\r
5328 \r
5329     case IDM_RetractMove:\r
5330       RetractMoveEvent();\r
5331       break;\r
5332 \r
5333     case IDM_FlipView:\r
5334       flipView = !flipView;\r
5335       DrawPosition(FALSE, NULL);\r
5336       break;\r
5337 \r
5338     case IDM_FlipClock:\r
5339       flipClock = !flipClock;\r
5340       DisplayBothClocks();\r
5341       DisplayLogos();\r
5342       break;\r
5343 \r
5344     case IDM_MuteSounds:\r
5345       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5346       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5347                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5348       break;\r
5349 \r
5350     case IDM_GeneralOptions:\r
5351       GeneralOptionsPopup(hwnd);\r
5352       DrawPosition(TRUE, NULL);\r
5353       break;\r
5354 \r
5355     case IDM_BoardOptions:\r
5356       BoardOptionsPopup(hwnd);\r
5357       break;\r
5358 \r
5359     case IDM_ThemeOptions:\r
5360       ThemeOptionsPopup(hwnd);\r
5361       break;\r
5362 \r
5363     case IDM_EnginePlayOptions:\r
5364       EnginePlayOptionsPopup(hwnd);\r
5365       break;\r
5366 \r
5367     case IDM_Engine1Options:\r
5368       EngineOptionsPopup(hwnd, &first);\r
5369       break;\r
5370 \r
5371     case IDM_Engine2Options:\r
5372       savedHwnd = hwnd;\r
5373       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5374       EngineOptionsPopup(hwnd, &second);\r
5375       break;\r
5376 \r
5377     case IDM_OptionsUCI:\r
5378       UciOptionsPopup(hwnd);\r
5379       break;\r
5380 \r
5381     case IDM_Tourney:\r
5382       TourneyPopup(hwnd);\r
5383       break;\r
5384 \r
5385     case IDM_IcsOptions:\r
5386       IcsOptionsPopup(hwnd);\r
5387       break;\r
5388 \r
5389     case IDM_Fonts:\r
5390       FontsOptionsPopup(hwnd);\r
5391       break;\r
5392 \r
5393     case IDM_Sounds:\r
5394       SoundOptionsPopup(hwnd);\r
5395       break;\r
5396 \r
5397     case IDM_CommPort:\r
5398       CommPortOptionsPopup(hwnd);\r
5399       break;\r
5400 \r
5401     case IDM_LoadOptions:\r
5402       LoadOptionsPopup(hwnd);\r
5403       break;\r
5404 \r
5405     case IDM_SaveOptions:\r
5406       SaveOptionsPopup(hwnd);\r
5407       break;\r
5408 \r
5409     case IDM_TimeControl:\r
5410       TimeControlOptionsPopup(hwnd);\r
5411       break;\r
5412 \r
5413     case IDM_SaveSettings:\r
5414       SaveSettings(settingsFileName);\r
5415       break;\r
5416 \r
5417     case IDM_SaveSettingsOnExit:\r
5418       saveSettingsOnExit = !saveSettingsOnExit;\r
5419       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5420                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5421                                          MF_CHECKED : MF_UNCHECKED));\r
5422       break;\r
5423 \r
5424     case IDM_Hint:\r
5425       HintEvent();\r
5426       break;\r
5427 \r
5428     case IDM_Book:\r
5429       BookEvent();\r
5430       break;\r
5431 \r
5432     case IDM_AboutGame:\r
5433       AboutGameEvent();\r
5434       break;\r
5435 \r
5436     case IDM_Debug:\r
5437       appData.debugMode = !appData.debugMode;\r
5438       if (appData.debugMode) {\r
5439         char dir[MSG_SIZ];\r
5440         GetCurrentDirectory(MSG_SIZ, dir);\r
5441         SetCurrentDirectory(installDir);\r
5442         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5443         SetCurrentDirectory(dir);\r
5444         setbuf(debugFP, NULL);\r
5445       } else {\r
5446         fclose(debugFP);\r
5447         debugFP = NULL;\r
5448       }\r
5449       break;\r
5450 \r
5451     case IDM_HELPCONTENTS:\r
5452       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5453           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5454           MessageBox (GetFocus(),\r
5455                     _("Unable to activate help"),\r
5456                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5457       }\r
5458       break;\r
5459 \r
5460     case IDM_HELPSEARCH:\r
5461         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5462             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5463         MessageBox (GetFocus(),\r
5464                     _("Unable to activate help"),\r
5465                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5466       }\r
5467       break;\r
5468 \r
5469     case IDM_HELPHELP:\r
5470       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5471         MessageBox (GetFocus(),\r
5472                     _("Unable to activate help"),\r
5473                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5474       }\r
5475       break;\r
5476 \r
5477     case IDM_ABOUT:\r
5478       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5479       DialogBox(hInst, \r
5480         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5481         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5482       FreeProcInstance(lpProc);\r
5483       break;\r
5484 \r
5485     case IDM_DirectCommand1:\r
5486       AskQuestionEvent(_("Direct Command"),\r
5487                        _("Send to chess program:"), "", "1");\r
5488       break;\r
5489     case IDM_DirectCommand2:\r
5490       AskQuestionEvent(_("Direct Command"),\r
5491                        _("Send to second chess program:"), "", "2");\r
5492       break;\r
5493 \r
5494     case EP_WhitePawn:\r
5495       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5496       fromX = fromY = -1;\r
5497       break;\r
5498 \r
5499     case EP_WhiteKnight:\r
5500       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5501       fromX = fromY = -1;\r
5502       break;\r
5503 \r
5504     case EP_WhiteBishop:\r
5505       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5506       fromX = fromY = -1;\r
5507       break;\r
5508 \r
5509     case EP_WhiteRook:\r
5510       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5511       fromX = fromY = -1;\r
5512       break;\r
5513 \r
5514     case EP_WhiteQueen:\r
5515       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5516       fromX = fromY = -1;\r
5517       break;\r
5518 \r
5519     case EP_WhiteFerz:\r
5520       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5521       fromX = fromY = -1;\r
5522       break;\r
5523 \r
5524     case EP_WhiteWazir:\r
5525       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5526       fromX = fromY = -1;\r
5527       break;\r
5528 \r
5529     case EP_WhiteAlfil:\r
5530       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5531       fromX = fromY = -1;\r
5532       break;\r
5533 \r
5534     case EP_WhiteCannon:\r
5535       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5536       fromX = fromY = -1;\r
5537       break;\r
5538 \r
5539     case EP_WhiteCardinal:\r
5540       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5541       fromX = fromY = -1;\r
5542       break;\r
5543 \r
5544     case EP_WhiteMarshall:\r
5545       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5546       fromX = fromY = -1;\r
5547       break;\r
5548 \r
5549     case EP_WhiteKing:\r
5550       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5551       fromX = fromY = -1;\r
5552       break;\r
5553 \r
5554     case EP_BlackPawn:\r
5555       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5556       fromX = fromY = -1;\r
5557       break;\r
5558 \r
5559     case EP_BlackKnight:\r
5560       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5561       fromX = fromY = -1;\r
5562       break;\r
5563 \r
5564     case EP_BlackBishop:\r
5565       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5566       fromX = fromY = -1;\r
5567       break;\r
5568 \r
5569     case EP_BlackRook:\r
5570       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5571       fromX = fromY = -1;\r
5572       break;\r
5573 \r
5574     case EP_BlackQueen:\r
5575       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5576       fromX = fromY = -1;\r
5577       break;\r
5578 \r
5579     case EP_BlackFerz:\r
5580       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5581       fromX = fromY = -1;\r
5582       break;\r
5583 \r
5584     case EP_BlackWazir:\r
5585       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5586       fromX = fromY = -1;\r
5587       break;\r
5588 \r
5589     case EP_BlackAlfil:\r
5590       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5591       fromX = fromY = -1;\r
5592       break;\r
5593 \r
5594     case EP_BlackCannon:\r
5595       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5596       fromX = fromY = -1;\r
5597       break;\r
5598 \r
5599     case EP_BlackCardinal:\r
5600       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5601       fromX = fromY = -1;\r
5602       break;\r
5603 \r
5604     case EP_BlackMarshall:\r
5605       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5606       fromX = fromY = -1;\r
5607       break;\r
5608 \r
5609     case EP_BlackKing:\r
5610       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5611       fromX = fromY = -1;\r
5612       break;\r
5613 \r
5614     case EP_EmptySquare:\r
5615       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5616       fromX = fromY = -1;\r
5617       break;\r
5618 \r
5619     case EP_ClearBoard:\r
5620       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5621       fromX = fromY = -1;\r
5622       break;\r
5623 \r
5624     case EP_White:\r
5625       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5626       fromX = fromY = -1;\r
5627       break;\r
5628 \r
5629     case EP_Black:\r
5630       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5631       fromX = fromY = -1;\r
5632       break;\r
5633 \r
5634     case EP_Promote:\r
5635       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5636       fromX = fromY = -1;\r
5637       break;\r
5638 \r
5639     case EP_Demote:\r
5640       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5641       fromX = fromY = -1;\r
5642       break;\r
5643 \r
5644     case DP_Pawn:\r
5645       DropMenuEvent(WhitePawn, fromX, fromY);\r
5646       fromX = fromY = -1;\r
5647       break;\r
5648 \r
5649     case DP_Knight:\r
5650       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5651       fromX = fromY = -1;\r
5652       break;\r
5653 \r
5654     case DP_Bishop:\r
5655       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5656       fromX = fromY = -1;\r
5657       break;\r
5658 \r
5659     case DP_Rook:\r
5660       DropMenuEvent(WhiteRook, fromX, fromY);\r
5661       fromX = fromY = -1;\r
5662       break;\r
5663 \r
5664     case DP_Queen:\r
5665       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5666       fromX = fromY = -1;\r
5667       break;\r
5668 \r
5669     case IDM_English:\r
5670       barbaric = 0; appData.language = "";\r
5671       TranslateMenus(0);\r
5672       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5673       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5674       lastChecked = wmId;\r
5675       break;\r
5676 \r
5677     default:\r
5678       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5679           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5680       else\r
5681       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5682           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5683           TranslateMenus(0);\r
5684           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5685           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5686           lastChecked = wmId;\r
5687           break;\r
5688       }\r
5689       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5690     }\r
5691     break;\r
5692 \r
5693   case WM_TIMER:\r
5694     switch (wParam) {\r
5695     case CLOCK_TIMER_ID:\r
5696       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5697       clockTimerEvent = 0;\r
5698       DecrementClocks(); /* call into back end */\r
5699       break;\r
5700     case LOAD_GAME_TIMER_ID:\r
5701       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5702       loadGameTimerEvent = 0;\r
5703       AutoPlayGameLoop(); /* call into back end */\r
5704       break;\r
5705     case ANALYSIS_TIMER_ID:\r
5706       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5707                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5708         AnalysisPeriodicEvent(0);\r
5709       } else {\r
5710         KillTimer(hwnd, analysisTimerEvent);\r
5711         analysisTimerEvent = 0;\r
5712       }\r
5713       break;\r
5714     case DELAYED_TIMER_ID:\r
5715       KillTimer(hwnd, delayedTimerEvent);\r
5716       delayedTimerEvent = 0;\r
5717       delayedTimerCallback();\r
5718       break;\r
5719     }\r
5720     break;\r
5721 \r
5722   case WM_USER_Input:\r
5723     InputEvent(hwnd, message, wParam, lParam);\r
5724     break;\r
5725 \r
5726   /* [AS] Also move "attached" child windows */\r
5727   case WM_WINDOWPOSCHANGING:\r
5728 \r
5729     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5730         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5731 \r
5732         if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?\r
5733             /* Window is moving */\r
5734             RECT rcMain;\r
5735 \r
5736 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5737             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5738             rcMain.right  = wpMain.x + wpMain.width;\r
5739             rcMain.top    = wpMain.y;\r
5740             rcMain.bottom = wpMain.y + wpMain.height;\r
5741             \r
5742             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5743             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5744             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5745             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5746             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5747             wpMain.x = lpwp->x;\r
5748             wpMain.y = lpwp->y;\r
5749 \r
5750         }\r
5751     }\r
5752     break;\r
5753 \r
5754   /* [AS] Snapping */\r
5755   case WM_ENTERSIZEMOVE:\r
5756     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5757     if (hwnd == hwndMain) {\r
5758       doingSizing = TRUE;\r
5759       lastSizing = 0;\r
5760     }\r
5761     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5762     break;\r
5763 \r
5764   case WM_SIZING:\r
5765     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5766     if (hwnd == hwndMain) {\r
5767       lastSizing = wParam;\r
5768     }\r
5769     break;\r
5770 \r
5771   case WM_MOVING:\r
5772     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5773       return OnMoving( &sd, hwnd, wParam, lParam );\r
5774 \r
5775   case WM_EXITSIZEMOVE:\r
5776     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5777     if (hwnd == hwndMain) {\r
5778       RECT client;\r
5779       doingSizing = FALSE;\r
5780       InvalidateRect(hwnd, &boardRect, FALSE);\r
5781       GetClientRect(hwnd, &client);\r
5782       ResizeBoard(client.right, client.bottom, lastSizing);\r
5783       lastSizing = 0;\r
5784       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5785     }\r
5786     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5787     break;\r
5788 \r
5789   case WM_DESTROY: /* message: window being destroyed */\r
5790     PostQuitMessage(0);\r
5791     break;\r
5792 \r
5793   case WM_CLOSE:\r
5794     if (hwnd == hwndMain) {\r
5795       ExitEvent(0);\r
5796     }\r
5797     break;\r
5798 \r
5799   default:      /* Passes it on if unprocessed */\r
5800     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5801   }\r
5802 \r
5803 \r
5804   return 0;\r
5805 }\r
5806 \r
5807 /*---------------------------------------------------------------------------*\\r
5808  *\r
5809  * Misc utility routines\r
5810  *\r
5811 \*---------------------------------------------------------------------------*/\r
5812 \r
5813 /*\r
5814  * Decent random number generator, at least not as bad as Windows\r
5815  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5816  */\r
5817 unsigned int randstate;\r
5818 \r
5819 int\r
5820 myrandom(void)\r
5821 {\r
5822   randstate = randstate * 1664525 + 1013904223;\r
5823   return (int) randstate & 0x7fffffff;\r
5824 }\r
5825 \r
5826 void\r
5827 mysrandom(unsigned int seed)\r
5828 {\r
5829   randstate = seed;\r
5830 }\r
5831 \r
5832 \r
5833 /* \r
5834  * returns TRUE if user selects a different color, FALSE otherwise \r
5835  */\r
5836 \r
5837 BOOL\r
5838 ChangeColor(HWND hwnd, COLORREF *which)\r
5839 {\r
5840   static BOOL firstTime = TRUE;\r
5841   static DWORD customColors[16];\r
5842   CHOOSECOLOR cc;\r
5843   COLORREF newcolor;\r
5844   int i;\r
5845   ColorClass ccl;\r
5846 \r
5847   if (firstTime) {\r
5848     /* Make initial colors in use available as custom colors */\r
5849     /* Should we put the compiled-in defaults here instead? */\r
5850     i = 0;\r
5851     customColors[i++] = lightSquareColor & 0xffffff;\r
5852     customColors[i++] = darkSquareColor & 0xffffff;\r
5853     customColors[i++] = whitePieceColor & 0xffffff;\r
5854     customColors[i++] = blackPieceColor & 0xffffff;\r
5855     customColors[i++] = highlightSquareColor & 0xffffff;\r
5856     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5857 \r
5858     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5859       customColors[i++] = textAttribs[ccl].color;\r
5860     }\r
5861     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5862     firstTime = FALSE;\r
5863   }\r
5864 \r
5865   cc.lStructSize = sizeof(cc);\r
5866   cc.hwndOwner = hwnd;\r
5867   cc.hInstance = NULL;\r
5868   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5869   cc.lpCustColors = (LPDWORD) customColors;\r
5870   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5871 \r
5872   if (!ChooseColor(&cc)) return FALSE;\r
5873 \r
5874   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5875   if (newcolor == *which) return FALSE;\r
5876   *which = newcolor;\r
5877   return TRUE;\r
5878 \r
5879   /*\r
5880   InitDrawingColors();\r
5881   InvalidateRect(hwnd, &boardRect, FALSE);\r
5882   */\r
5883 }\r
5884 \r
5885 BOOLEAN\r
5886 MyLoadSound(MySound *ms)\r
5887 {\r
5888   BOOL ok = FALSE;\r
5889   struct stat st;\r
5890   FILE *f;\r
5891 \r
5892   if (ms->data && ms->flag) free(ms->data);\r
5893   ms->data = NULL;\r
5894 \r
5895   switch (ms->name[0]) {\r
5896   case NULLCHAR:\r
5897     /* Silence */\r
5898     ok = TRUE;\r
5899     break;\r
5900   case '$':\r
5901     /* System sound from Control Panel.  Don't preload here. */\r
5902     ok = TRUE;\r
5903     break;\r
5904   case '!':\r
5905     if (ms->name[1] == NULLCHAR) {\r
5906       /* "!" alone = silence */\r
5907       ok = TRUE;\r
5908     } else {\r
5909       /* Builtin wave resource.  Error if not found. */\r
5910       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5911       if (h == NULL) break;\r
5912       ms->data = (void *)LoadResource(hInst, h);\r
5913       ms->flag = 0; // not maloced, so cannot be freed!\r
5914       if (h == NULL) break;\r
5915       ok = TRUE;\r
5916     }\r
5917     break;\r
5918   default:\r
5919     /* .wav file.  Error if not found. */\r
5920     f = fopen(ms->name, "rb");\r
5921     if (f == NULL) break;\r
5922     if (fstat(fileno(f), &st) < 0) break;\r
5923     ms->data = malloc(st.st_size);\r
5924     ms->flag = 1;\r
5925     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5926     fclose(f);\r
5927     ok = TRUE;\r
5928     break;\r
5929   }\r
5930   if (!ok) {\r
5931     char buf[MSG_SIZ];\r
5932       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5933     DisplayError(buf, GetLastError());\r
5934   }\r
5935   return ok;\r
5936 }\r
5937 \r
5938 BOOLEAN\r
5939 MyPlaySound(MySound *ms)\r
5940 {\r
5941   BOOLEAN ok = FALSE;\r
5942 \r
5943   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5944   switch (ms->name[0]) {\r
5945   case NULLCHAR:\r
5946         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5947     /* Silence */\r
5948     ok = TRUE;\r
5949     break;\r
5950   case '$':\r
5951     /* System sound from Control Panel (deprecated feature).\r
5952        "$" alone or an unset sound name gets default beep (still in use). */\r
5953     if (ms->name[1]) {\r
5954       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5955     }\r
5956     if (!ok) ok = MessageBeep(MB_OK);\r
5957     break; \r
5958   case '!':\r
5959     /* Builtin wave resource, or "!" alone for silence */\r
5960     if (ms->name[1]) {\r
5961       if (ms->data == NULL) return FALSE;\r
5962       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5963     } else {\r
5964       ok = TRUE;\r
5965     }\r
5966     break;\r
5967   default:\r
5968     /* .wav file.  Error if not found. */\r
5969     if (ms->data == NULL) return FALSE;\r
5970     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5971     break;\r
5972   }\r
5973   /* Don't print an error: this can happen innocently if the sound driver\r
5974      is busy; for instance, if another instance of WinBoard is playing\r
5975      a sound at about the same time. */\r
5976   return ok;\r
5977 }\r
5978 \r
5979 \r
5980 LRESULT CALLBACK\r
5981 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5982 {\r
5983   BOOL ok;\r
5984   OPENFILENAME *ofn;\r
5985   static UINT *number; /* gross that this is static */\r
5986 \r
5987   switch (message) {\r
5988   case WM_INITDIALOG: /* message: initialize dialog box */\r
5989     /* Center the dialog over the application window */\r
5990     ofn = (OPENFILENAME *) lParam;\r
5991     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5992       number = (UINT *) ofn->lCustData;\r
5993       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5994     } else {\r
5995       number = NULL;\r
5996     }\r
5997     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5998     Translate(hDlg, 1536);\r
5999     return FALSE;  /* Allow for further processing */\r
6000 \r
6001   case WM_COMMAND:\r
6002     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6003       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6004     }\r
6005     return FALSE;  /* Allow for further processing */\r
6006   }\r
6007   return FALSE;\r
6008 }\r
6009 \r
6010 UINT APIENTRY\r
6011 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6012 {\r
6013   static UINT *number;\r
6014   OPENFILENAME *ofname;\r
6015   OFNOTIFY *ofnot;\r
6016   switch (uiMsg) {\r
6017   case WM_INITDIALOG:\r
6018     Translate(hdlg, DLG_IndexNumber);\r
6019     ofname = (OPENFILENAME *)lParam;\r
6020     number = (UINT *)(ofname->lCustData);\r
6021     break;\r
6022   case WM_NOTIFY:\r
6023     ofnot = (OFNOTIFY *)lParam;\r
6024     if (ofnot->hdr.code == CDN_FILEOK) {\r
6025       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6026     }\r
6027     break;\r
6028   }\r
6029   return 0;\r
6030 }\r
6031 \r
6032 \r
6033 FILE *\r
6034 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6035                char *nameFilt, char *dlgTitle, UINT *number,\r
6036                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6037 {\r
6038   OPENFILENAME openFileName;\r
6039   char buf1[MSG_SIZ];\r
6040   FILE *f;\r
6041 \r
6042   if (fileName == NULL) fileName = buf1;\r
6043   if (defName == NULL) {\r
6044     safeStrCpy(fileName, "*.", 3 );\r
6045     strcat(fileName, defExt);\r
6046   } else {\r
6047     safeStrCpy(fileName, defName, MSG_SIZ );\r
6048   }\r
6049     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
6050   if (number) *number = 0;\r
6051 \r
6052   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6053   openFileName.hwndOwner         = hwnd;\r
6054   openFileName.hInstance         = (HANDLE) hInst;\r
6055   openFileName.lpstrFilter       = nameFilt;\r
6056   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6057   openFileName.nMaxCustFilter    = 0L;\r
6058   openFileName.nFilterIndex      = 1L;\r
6059   openFileName.lpstrFile         = fileName;\r
6060   openFileName.nMaxFile          = MSG_SIZ;\r
6061   openFileName.lpstrFileTitle    = fileTitle;\r
6062   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6063   openFileName.lpstrInitialDir   = NULL;\r
6064   openFileName.lpstrTitle        = dlgTitle;\r
6065   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6066     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6067     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6068     | (oldDialog ? 0 : OFN_EXPLORER);\r
6069   openFileName.nFileOffset       = 0;\r
6070   openFileName.nFileExtension    = 0;\r
6071   openFileName.lpstrDefExt       = defExt;\r
6072   openFileName.lCustData         = (LONG) number;\r
6073   openFileName.lpfnHook          = oldDialog ?\r
6074     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6075   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6076 \r
6077   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6078                         GetOpenFileName(&openFileName)) {\r
6079     /* open the file */\r
6080     f = fopen(openFileName.lpstrFile, write);\r
6081     if (f == NULL) {\r
6082       MessageBox(hwnd, _("File open failed"), NULL,\r
6083                  MB_OK|MB_ICONEXCLAMATION);\r
6084       return NULL;\r
6085     }\r
6086   } else {\r
6087     int err = CommDlgExtendedError();\r
6088     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
6089     return FALSE;\r
6090   }\r
6091   return f;\r
6092 }\r
6093 \r
6094 \r
6095 \r
6096 VOID APIENTRY\r
6097 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6098 {\r
6099   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6100 \r
6101   /*\r
6102    * Get the first pop-up menu in the menu template. This is the\r
6103    * menu that TrackPopupMenu displays.\r
6104    */\r
6105   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6106   TranslateOneMenu(10, hmenuTrackPopup);\r
6107 \r
6108   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6109 \r
6110   /*\r
6111    * TrackPopup uses screen coordinates, so convert the\r
6112    * coordinates of the mouse click to screen coordinates.\r
6113    */\r
6114   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6115 \r
6116   /* Draw and track the floating pop-up menu. */\r
6117   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6118                  pt.x, pt.y, 0, hwnd, NULL);\r
6119 \r
6120   /* Destroy the menu.*/\r
6121   DestroyMenu(hmenu);\r
6122 }\r
6123    \r
6124 typedef struct {\r
6125   HWND hDlg, hText;\r
6126   int sizeX, sizeY, newSizeX, newSizeY;\r
6127   HDWP hdwp;\r
6128 } ResizeEditPlusButtonsClosure;\r
6129 \r
6130 BOOL CALLBACK\r
6131 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6132 {\r
6133   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6134   RECT rect;\r
6135   POINT pt;\r
6136 \r
6137   if (hChild == cl->hText) return TRUE;\r
6138   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6139   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6140   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6141   ScreenToClient(cl->hDlg, &pt);\r
6142   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6143     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6144   return TRUE;\r
6145 }\r
6146 \r
6147 /* Resize a dialog that has a (rich) edit field filling most of\r
6148    the top, with a row of buttons below */\r
6149 VOID\r
6150 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6151 {\r
6152   RECT rectText;\r
6153   int newTextHeight, newTextWidth;\r
6154   ResizeEditPlusButtonsClosure cl;\r
6155   \r
6156   /*if (IsIconic(hDlg)) return;*/\r
6157   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6158   \r
6159   cl.hdwp = BeginDeferWindowPos(8);\r
6160 \r
6161   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6162   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6163   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6164   if (newTextHeight < 0) {\r
6165     newSizeY += -newTextHeight;\r
6166     newTextHeight = 0;\r
6167   }\r
6168   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6169     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6170 \r
6171   cl.hDlg = hDlg;\r
6172   cl.hText = hText;\r
6173   cl.sizeX = sizeX;\r
6174   cl.sizeY = sizeY;\r
6175   cl.newSizeX = newSizeX;\r
6176   cl.newSizeY = newSizeY;\r
6177   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6178 \r
6179   EndDeferWindowPos(cl.hdwp);\r
6180 }\r
6181 \r
6182 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6183 {\r
6184     RECT    rChild, rParent;\r
6185     int     wChild, hChild, wParent, hParent;\r
6186     int     wScreen, hScreen, xNew, yNew;\r
6187     HDC     hdc;\r
6188 \r
6189     /* Get the Height and Width of the child window */\r
6190     GetWindowRect (hwndChild, &rChild);\r
6191     wChild = rChild.right - rChild.left;\r
6192     hChild = rChild.bottom - rChild.top;\r
6193 \r
6194     /* Get the Height and Width of the parent window */\r
6195     GetWindowRect (hwndParent, &rParent);\r
6196     wParent = rParent.right - rParent.left;\r
6197     hParent = rParent.bottom - rParent.top;\r
6198 \r
6199     /* Get the display limits */\r
6200     hdc = GetDC (hwndChild);\r
6201     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6202     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6203     ReleaseDC(hwndChild, hdc);\r
6204 \r
6205     /* Calculate new X position, then adjust for screen */\r
6206     xNew = rParent.left + ((wParent - wChild) /2);\r
6207     if (xNew < 0) {\r
6208         xNew = 0;\r
6209     } else if ((xNew+wChild) > wScreen) {\r
6210         xNew = wScreen - wChild;\r
6211     }\r
6212 \r
6213     /* Calculate new Y position, then adjust for screen */\r
6214     if( mode == 0 ) {\r
6215         yNew = rParent.top  + ((hParent - hChild) /2);\r
6216     }\r
6217     else {\r
6218         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6219     }\r
6220 \r
6221     if (yNew < 0) {\r
6222         yNew = 0;\r
6223     } else if ((yNew+hChild) > hScreen) {\r
6224         yNew = hScreen - hChild;\r
6225     }\r
6226 \r
6227     /* Set it, and return */\r
6228     return SetWindowPos (hwndChild, NULL,\r
6229                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6230 }\r
6231 \r
6232 /* Center one window over another */\r
6233 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6234 {\r
6235     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6236 }\r
6237 \r
6238 /*---------------------------------------------------------------------------*\\r
6239  *\r
6240  * Startup Dialog functions\r
6241  *\r
6242 \*---------------------------------------------------------------------------*/\r
6243 void\r
6244 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6245 {\r
6246   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6247 \r
6248   while (*cd != NULL) {\r
6249     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6250     cd++;\r
6251   }\r
6252 }\r
6253 \r
6254 void\r
6255 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6256 {\r
6257   char buf1[MAX_ARG_LEN];\r
6258   int len;\r
6259 \r
6260   if (str[0] == '@') {\r
6261     FILE* f = fopen(str + 1, "r");\r
6262     if (f == NULL) {\r
6263       DisplayFatalError(str + 1, errno, 2);\r
6264       return;\r
6265     }\r
6266     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6267     fclose(f);\r
6268     buf1[len] = NULLCHAR;\r
6269     str = buf1;\r
6270   }\r
6271 \r
6272   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6273 \r
6274   for (;;) {\r
6275     char buf[MSG_SIZ];\r
6276     char *end = strchr(str, '\n');\r
6277     if (end == NULL) return;\r
6278     memcpy(buf, str, end - str);\r
6279     buf[end - str] = NULLCHAR;\r
6280     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6281     str = end + 1;\r
6282   }\r
6283 }\r
6284 \r
6285 void\r
6286 SetStartupDialogEnables(HWND hDlg)\r
6287 {\r
6288   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6289     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6290     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6291   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6292     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6293   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6294     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6295   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6296     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6297   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6298     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6299     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6300     IsDlgButtonChecked(hDlg, OPT_View));\r
6301 }\r
6302 \r
6303 char *\r
6304 QuoteForFilename(char *filename)\r
6305 {\r
6306   int dquote, space;\r
6307   dquote = strchr(filename, '"') != NULL;\r
6308   space = strchr(filename, ' ') != NULL;\r
6309   if (dquote || space) {\r
6310     if (dquote) {\r
6311       return "'";\r
6312     } else {\r
6313       return "\"";\r
6314     }\r
6315   } else {\r
6316     return "";\r
6317   }\r
6318 }\r
6319 \r
6320 VOID\r
6321 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6322 {\r
6323   char buf[MSG_SIZ];\r
6324   char *q;\r
6325 \r
6326   InitComboStringsFromOption(hwndCombo, nthnames);\r
6327   q = QuoteForFilename(nthcp);\r
6328     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6329   if (*nthdir != NULLCHAR) {\r
6330     q = QuoteForFilename(nthdir);\r
6331       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6332   }\r
6333   if (*nthcp == NULLCHAR) {\r
6334     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6335   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6336     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6337     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6338   }\r
6339 }\r
6340 \r
6341 LRESULT CALLBACK\r
6342 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6343 {\r
6344   char buf[MSG_SIZ];\r
6345   HANDLE hwndCombo;\r
6346   char *p;\r
6347 \r
6348   switch (message) {\r
6349   case WM_INITDIALOG:\r
6350     /* Center the dialog */\r
6351     CenterWindow (hDlg, GetDesktopWindow());\r
6352     Translate(hDlg, DLG_Startup);\r
6353     /* Initialize the dialog items */\r
6354     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6355                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6356                   firstChessProgramNames);\r
6357     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6358                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6359                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6360     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6361     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6362       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6363     if (*appData.icsHelper != NULLCHAR) {\r
6364       char *q = QuoteForFilename(appData.icsHelper);\r
6365       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6366     }\r
6367     if (*appData.icsHost == NULLCHAR) {\r
6368       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6369       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6370     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6371       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6372       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6373     }\r
6374 \r
6375     if (appData.icsActive) {\r
6376       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6377     }\r
6378     else if (appData.noChessProgram) {\r
6379       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6380     }\r
6381     else {\r
6382       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6383     }\r
6384 \r
6385     SetStartupDialogEnables(hDlg);\r
6386     return TRUE;\r
6387 \r
6388   case WM_COMMAND:\r
6389     switch (LOWORD(wParam)) {\r
6390     case IDOK:\r
6391       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6392         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6393         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6394         p = buf;\r
6395         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6396         ParseArgs(StringGet, &p);\r
6397         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6398         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6399         p = buf;\r
6400         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6401         ParseArgs(StringGet, &p);\r
6402         SwapEngines(singleList); // ... and then make it 'second'\r
6403 \r
6404         appData.noChessProgram = FALSE;\r
6405         appData.icsActive = FALSE;\r
6406       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6407         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6408         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6409         p = buf;\r
6410         ParseArgs(StringGet, &p);\r
6411         if (appData.zippyPlay) {\r
6412           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6413           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6414           p = buf;\r
6415           ParseArgs(StringGet, &p);\r
6416         }\r
6417       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6418         appData.noChessProgram = TRUE;\r
6419         appData.icsActive = FALSE;\r
6420       } else {\r
6421         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6422                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6423         return TRUE;\r
6424       }\r
6425       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6426         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6427         p = buf;\r
6428         ParseArgs(StringGet, &p);\r
6429       }\r
6430       EndDialog(hDlg, TRUE);\r
6431       return TRUE;\r
6432 \r
6433     case IDCANCEL:\r
6434       ExitEvent(0);\r
6435       return TRUE;\r
6436 \r
6437     case IDM_HELPCONTENTS:\r
6438       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6439         MessageBox (GetFocus(),\r
6440                     _("Unable to activate help"),\r
6441                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6442       }\r
6443       break;\r
6444 \r
6445     default:\r
6446       SetStartupDialogEnables(hDlg);\r
6447       break;\r
6448     }\r
6449     break;\r
6450   }\r
6451   return FALSE;\r
6452 }\r
6453 \r
6454 /*---------------------------------------------------------------------------*\\r
6455  *\r
6456  * About box dialog functions\r
6457  *\r
6458 \*---------------------------------------------------------------------------*/\r
6459 \r
6460 /* Process messages for "About" dialog box */\r
6461 LRESULT CALLBACK\r
6462 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6463 {\r
6464   switch (message) {\r
6465   case WM_INITDIALOG: /* message: initialize dialog box */\r
6466     /* Center the dialog over the application window */\r
6467     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6468     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6469     Translate(hDlg, ABOUTBOX);\r
6470     JAWS_COPYRIGHT\r
6471     return (TRUE);\r
6472 \r
6473   case WM_COMMAND: /* message: received a command */\r
6474     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6475         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6476       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6477       return (TRUE);\r
6478     }\r
6479     break;\r
6480   }\r
6481   return (FALSE);\r
6482 }\r
6483 \r
6484 /*---------------------------------------------------------------------------*\\r
6485  *\r
6486  * Comment Dialog functions\r
6487  *\r
6488 \*---------------------------------------------------------------------------*/\r
6489 \r
6490 LRESULT CALLBACK\r
6491 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6492 {\r
6493   static HANDLE hwndText = NULL;\r
6494   int len, newSizeX, newSizeY;\r
6495   static int sizeX, sizeY;\r
6496   char *str;\r
6497   RECT rect;\r
6498   MINMAXINFO *mmi;\r
6499 \r
6500   switch (message) {\r
6501   case WM_INITDIALOG: /* message: initialize dialog box */\r
6502     /* Initialize the dialog items */\r
6503     Translate(hDlg, DLG_EditComment);\r
6504     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6505     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6506     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6507     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6508     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6509     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6510     SetWindowText(hDlg, commentTitle);\r
6511     if (editComment) {\r
6512       SetFocus(hwndText);\r
6513     } else {\r
6514       SetFocus(GetDlgItem(hDlg, IDOK));\r
6515     }\r
6516     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6517                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6518                 MAKELPARAM(FALSE, 0));\r
6519     /* Size and position the dialog */\r
6520     if (!commentDialog) {\r
6521       commentDialog = hDlg;\r
6522       GetClientRect(hDlg, &rect);\r
6523       sizeX = rect.right;\r
6524       sizeY = rect.bottom;\r
6525       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6526           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6527         WINDOWPLACEMENT wp;\r
6528         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6529         wp.length = sizeof(WINDOWPLACEMENT);\r
6530         wp.flags = 0;\r
6531         wp.showCmd = SW_SHOW;\r
6532         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6533         wp.rcNormalPosition.left = wpComment.x;\r
6534         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6535         wp.rcNormalPosition.top = wpComment.y;\r
6536         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6537         SetWindowPlacement(hDlg, &wp);\r
6538 \r
6539         GetClientRect(hDlg, &rect);\r
6540         newSizeX = rect.right;\r
6541         newSizeY = rect.bottom;\r
6542         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6543                               newSizeX, newSizeY);\r
6544         sizeX = newSizeX;\r
6545         sizeY = newSizeY;\r
6546       }\r
6547     }\r
6548     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6549     return FALSE;\r
6550 \r
6551   case WM_COMMAND: /* message: received a command */\r
6552     switch (LOWORD(wParam)) {\r
6553     case IDOK:\r
6554       if (editComment) {\r
6555         char *p, *q;\r
6556         /* Read changed options from the dialog box */\r
6557         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6558         len = GetWindowTextLength(hwndText);\r
6559         str = (char *) malloc(len + 1);\r
6560         GetWindowText(hwndText, str, len + 1);\r
6561         p = q = str;\r
6562         while (*q) {\r
6563           if (*q == '\r')\r
6564             q++;\r
6565           else\r
6566             *p++ = *q++;\r
6567         }\r
6568         *p = NULLCHAR;\r
6569         ReplaceComment(commentIndex, str);\r
6570         free(str);\r
6571       }\r
6572       CommentPopDown();\r
6573       return TRUE;\r
6574 \r
6575     case IDCANCEL:\r
6576     case OPT_CancelComment:\r
6577       CommentPopDown();\r
6578       return TRUE;\r
6579 \r
6580     case OPT_ClearComment:\r
6581       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6582       break;\r
6583 \r
6584     case OPT_EditComment:\r
6585       EditCommentEvent();\r
6586       return TRUE;\r
6587 \r
6588     default:\r
6589       break;\r
6590     }\r
6591     break;\r
6592 \r
6593   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6594         if( wParam == OPT_CommentText ) {\r
6595             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6596 \r
6597             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6598                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6599                 POINTL pt;\r
6600                 LRESULT index;\r
6601 \r
6602                 pt.x = LOWORD( lpMF->lParam );\r
6603                 pt.y = HIWORD( lpMF->lParam );\r
6604 \r
6605                 if(lpMF->msg == WM_CHAR) {\r
6606                         CHARRANGE sel;\r
6607                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6608                         index = sel.cpMin;\r
6609                 } else\r
6610                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6611 \r
6612                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6613                 len = GetWindowTextLength(hwndText);\r
6614                 str = (char *) malloc(len + 1);\r
6615                 GetWindowText(hwndText, str, len + 1);\r
6616                 ReplaceComment(commentIndex, str);\r
6617                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6618                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6619                 free(str);\r
6620 \r
6621                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6622                 lpMF->msg = WM_USER;\r
6623 \r
6624                 return TRUE;\r
6625             }\r
6626         }\r
6627         break;\r
6628 \r
6629   case WM_SIZE:\r
6630     newSizeX = LOWORD(lParam);\r
6631     newSizeY = HIWORD(lParam);\r
6632     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6633     sizeX = newSizeX;\r
6634     sizeY = newSizeY;\r
6635     break;\r
6636 \r
6637   case WM_GETMINMAXINFO:\r
6638     /* Prevent resizing window too small */\r
6639     mmi = (MINMAXINFO *) lParam;\r
6640     mmi->ptMinTrackSize.x = 100;\r
6641     mmi->ptMinTrackSize.y = 100;\r
6642     break;\r
6643   }\r
6644   return FALSE;\r
6645 }\r
6646 \r
6647 VOID\r
6648 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6649 {\r
6650   FARPROC lpProc;\r
6651   char *p, *q;\r
6652 \r
6653   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6654 \r
6655   if (str == NULL) str = "";\r
6656   p = (char *) malloc(2 * strlen(str) + 2);\r
6657   q = p;\r
6658   while (*str) {\r
6659     if (*str == '\n') *q++ = '\r';\r
6660     *q++ = *str++;\r
6661   }\r
6662   *q = NULLCHAR;\r
6663   if (commentText != NULL) free(commentText);\r
6664 \r
6665   commentIndex = index;\r
6666   commentTitle = title;\r
6667   commentText = p;\r
6668   editComment = edit;\r
6669 \r
6670   if (commentDialog) {\r
6671     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6672     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6673   } else {\r
6674     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6675     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6676                  hwndMain, (DLGPROC)lpProc);\r
6677     FreeProcInstance(lpProc);\r
6678   }\r
6679   commentUp = TRUE;\r
6680 }\r
6681 \r
6682 \r
6683 /*---------------------------------------------------------------------------*\\r
6684  *\r
6685  * Type-in move dialog functions\r
6686  * \r
6687 \*---------------------------------------------------------------------------*/\r
6688 \r
6689 LRESULT CALLBACK\r
6690 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6691 {\r
6692   char move[MSG_SIZ];\r
6693   HWND hInput;\r
6694 \r
6695   switch (message) {\r
6696   case WM_INITDIALOG:\r
6697     move[0] = (char) lParam;\r
6698     move[1] = NULLCHAR;\r
6699     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6700     Translate(hDlg, DLG_TypeInMove);\r
6701     hInput = GetDlgItem(hDlg, OPT_Move);\r
6702     SetWindowText(hInput, move);\r
6703     SetFocus(hInput);\r
6704     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6705     return FALSE;\r
6706 \r
6707   case WM_COMMAND:\r
6708     switch (LOWORD(wParam)) {\r
6709     case IDOK:\r
6710 \r
6711       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6712       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6713       TypeInDoneEvent(move);\r
6714       EndDialog(hDlg, TRUE);\r
6715       return TRUE;\r
6716     case IDCANCEL:\r
6717       EndDialog(hDlg, FALSE);\r
6718       return TRUE;\r
6719     default:\r
6720       break;\r
6721     }\r
6722     break;\r
6723   }\r
6724   return FALSE;\r
6725 }\r
6726 \r
6727 VOID\r
6728 PopUpMoveDialog(char firstchar)\r
6729 {\r
6730     FARPROC lpProc;\r
6731 \r
6732       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6733       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6734         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6735       FreeProcInstance(lpProc);\r
6736 }\r
6737 \r
6738 /*---------------------------------------------------------------------------*\\r
6739  *\r
6740  * Type-in name dialog functions\r
6741  * \r
6742 \*---------------------------------------------------------------------------*/\r
6743 \r
6744 LRESULT CALLBACK\r
6745 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6746 {\r
6747   char move[MSG_SIZ];\r
6748   HWND hInput;\r
6749 \r
6750   switch (message) {\r
6751   case WM_INITDIALOG:\r
6752     move[0] = (char) lParam;\r
6753     move[1] = NULLCHAR;\r
6754     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6755     Translate(hDlg, DLG_TypeInName);\r
6756     hInput = GetDlgItem(hDlg, OPT_Name);\r
6757     SetWindowText(hInput, move);\r
6758     SetFocus(hInput);\r
6759     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6760     return FALSE;\r
6761 \r
6762   case WM_COMMAND:\r
6763     switch (LOWORD(wParam)) {\r
6764     case IDOK:\r
6765       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6766       appData.userName = strdup(move);\r
6767       SetUserLogo(); DisplayLogos();\r
6768       SetGameInfo();\r
6769       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6770         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6771         DisplayTitle(move);\r
6772       }\r
6773 \r
6774 \r
6775       EndDialog(hDlg, TRUE);\r
6776       return TRUE;\r
6777     case IDCANCEL:\r
6778       EndDialog(hDlg, FALSE);\r
6779       return TRUE;\r
6780     default:\r
6781       break;\r
6782     }\r
6783     break;\r
6784   }\r
6785   return FALSE;\r
6786 }\r
6787 \r
6788 VOID\r
6789 PopUpNameDialog(char firstchar)\r
6790 {\r
6791     FARPROC lpProc;\r
6792     \r
6793       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6794       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6795         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6796       FreeProcInstance(lpProc);\r
6797 }\r
6798 \r
6799 /*---------------------------------------------------------------------------*\\r
6800  *\r
6801  *  Error dialogs\r
6802  * \r
6803 \*---------------------------------------------------------------------------*/\r
6804 \r
6805 /* Nonmodal error box */\r
6806 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6807                              WPARAM wParam, LPARAM lParam);\r
6808 \r
6809 VOID\r
6810 ErrorPopUp(char *title, char *content)\r
6811 {\r
6812   FARPROC lpProc;\r
6813   char *p, *q;\r
6814   BOOLEAN modal = hwndMain == NULL;\r
6815 \r
6816   p = content;\r
6817   q = errorMessage;\r
6818   while (*p) {\r
6819     if (*p == '\n') {\r
6820       if (modal) {\r
6821         *q++ = ' ';\r
6822         p++;\r
6823       } else {\r
6824         *q++ = '\r';\r
6825         *q++ = *p++;\r
6826       }\r
6827     } else {\r
6828       *q++ = *p++;\r
6829     }\r
6830   }\r
6831   *q = NULLCHAR;\r
6832   strncpy(errorTitle, title, sizeof(errorTitle));\r
6833   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6834   \r
6835   if (modal) {\r
6836     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6837   } else {\r
6838     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6839     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6840                  hwndMain, (DLGPROC)lpProc);\r
6841     FreeProcInstance(lpProc);\r
6842   }\r
6843 }\r
6844 \r
6845 VOID\r
6846 ErrorPopDown()\r
6847 {\r
6848   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6849   if (errorDialog == NULL) return;\r
6850   DestroyWindow(errorDialog);\r
6851   errorDialog = NULL;\r
6852   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6853 }\r
6854 \r
6855 LRESULT CALLBACK\r
6856 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6857 {\r
6858   RECT rChild;\r
6859 \r
6860   switch (message) {\r
6861   case WM_INITDIALOG:\r
6862     GetWindowRect(hDlg, &rChild);\r
6863 \r
6864     /*\r
6865     SetWindowPos(hDlg, NULL, rChild.left,\r
6866       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6867       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6868     */\r
6869 \r
6870     /* \r
6871         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6872         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6873         and it doesn't work when you resize the dialog.\r
6874         For now, just give it a default position.\r
6875     */\r
6876     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6877     Translate(hDlg, DLG_Error);\r
6878 \r
6879     errorDialog = hDlg;\r
6880     SetWindowText(hDlg, errorTitle);\r
6881     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6882     return FALSE;\r
6883 \r
6884   case WM_COMMAND:\r
6885     switch (LOWORD(wParam)) {\r
6886     case IDOK:\r
6887     case IDCANCEL:\r
6888       if (errorDialog == hDlg) errorDialog = NULL;\r
6889       DestroyWindow(hDlg);\r
6890       return TRUE;\r
6891 \r
6892     default:\r
6893       break;\r
6894     }\r
6895     break;\r
6896   }\r
6897   return FALSE;\r
6898 }\r
6899 \r
6900 #ifdef GOTHIC\r
6901 HWND gothicDialog = NULL;\r
6902 \r
6903 LRESULT CALLBACK\r
6904 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6905 {\r
6906   RECT rChild;\r
6907   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6908 \r
6909   switch (message) {\r
6910   case WM_INITDIALOG:\r
6911     GetWindowRect(hDlg, &rChild);\r
6912 \r
6913     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6914                                                              SWP_NOZORDER);\r
6915 \r
6916     /* \r
6917         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6918         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6919         and it doesn't work when you resize the dialog.\r
6920         For now, just give it a default position.\r
6921     */\r
6922     gothicDialog = hDlg;\r
6923     SetWindowText(hDlg, errorTitle);\r
6924     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6925     return FALSE;\r
6926 \r
6927   case WM_COMMAND:\r
6928     switch (LOWORD(wParam)) {\r
6929     case IDOK:\r
6930     case IDCANCEL:\r
6931       if (errorDialog == hDlg) errorDialog = NULL;\r
6932       DestroyWindow(hDlg);\r
6933       return TRUE;\r
6934 \r
6935     default:\r
6936       break;\r
6937     }\r
6938     break;\r
6939   }\r
6940   return FALSE;\r
6941 }\r
6942 \r
6943 VOID\r
6944 GothicPopUp(char *title, VariantClass variant)\r
6945 {\r
6946   FARPROC lpProc;\r
6947   static char *lastTitle;\r
6948 \r
6949   strncpy(errorTitle, title, sizeof(errorTitle));\r
6950   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6951 \r
6952   if(lastTitle != title && gothicDialog != NULL) {\r
6953     DestroyWindow(gothicDialog);\r
6954     gothicDialog = NULL;\r
6955   }\r
6956   if(variant != VariantNormal && gothicDialog == NULL) {\r
6957     title = lastTitle;\r
6958     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6959     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6960                  hwndMain, (DLGPROC)lpProc);\r
6961     FreeProcInstance(lpProc);\r
6962   }\r
6963 }\r
6964 #endif\r
6965 \r
6966 /*---------------------------------------------------------------------------*\\r
6967  *\r
6968  *  Ics Interaction console functions\r
6969  *\r
6970 \*---------------------------------------------------------------------------*/\r
6971 \r
6972 #define HISTORY_SIZE 64\r
6973 static char *history[HISTORY_SIZE];\r
6974 int histIn = 0, histP = 0;\r
6975 \r
6976 \r
6977 VOID\r
6978 SaveInHistory(char *cmd)\r
6979 {\r
6980   if (history[histIn] != NULL) {\r
6981     free(history[histIn]);\r
6982     history[histIn] = NULL;\r
6983   }\r
6984   if (*cmd == NULLCHAR) return;\r
6985   history[histIn] = StrSave(cmd);\r
6986   histIn = (histIn + 1) % HISTORY_SIZE;\r
6987   if (history[histIn] != NULL) {\r
6988     free(history[histIn]);\r
6989 \r
6990     history[histIn] = NULL;\r
6991   }\r
6992   histP = histIn;\r
6993 }\r
6994 \r
6995 char *\r
6996 PrevInHistory(char *cmd)\r
6997 {\r
6998   int newhp;\r
6999   if (histP == histIn) {\r
7000     if (history[histIn] != NULL) free(history[histIn]);\r
7001     history[histIn] = StrSave(cmd);\r
7002   }\r
7003   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7004   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7005   histP = newhp;\r
7006   return history[histP];\r
7007 }\r
7008 \r
7009 char *\r
7010 NextInHistory()\r
7011 {\r
7012   if (histP == histIn) return NULL;\r
7013   histP = (histP + 1) % HISTORY_SIZE;\r
7014   return history[histP];   \r
7015 }\r
7016 \r
7017 HMENU\r
7018 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7019 {\r
7020   HMENU hmenu, h;\r
7021   int i = 0;\r
7022   hmenu = LoadMenu(hInst, "TextMenu");\r
7023   h = GetSubMenu(hmenu, 0);\r
7024   while (e->item) {\r
7025     if (strcmp(e->item, "-") == 0) {\r
7026       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7027     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
7028       int flags = MF_STRING, j = 0;\r
7029       if (e->item[0] == '|') {\r
7030         flags |= MF_MENUBARBREAK;\r
7031         j++;\r
7032       }\r
7033       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
7034       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
7035     }\r
7036     e++;\r
7037     i++;\r
7038   } \r
7039   return hmenu;\r
7040 }\r
7041 \r
7042 WNDPROC consoleTextWindowProc;\r
7043 \r
7044 void\r
7045 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7046 {\r
7047   char buf[MSG_SIZ], name[MSG_SIZ];\r
7048   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7049   CHARRANGE sel;\r
7050 \r
7051   if (!getname) {\r
7052     SetWindowText(hInput, command);\r
7053     if (immediate) {\r
7054       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7055     } else {\r
7056       sel.cpMin = 999999;\r
7057       sel.cpMax = 999999;\r
7058       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7059       SetFocus(hInput);\r
7060     }\r
7061     return;\r
7062   }    \r
7063   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7064   if (sel.cpMin == sel.cpMax) {\r
7065     /* Expand to surrounding word */\r
7066     TEXTRANGE tr;\r
7067     do {\r
7068       tr.chrg.cpMax = sel.cpMin;\r
7069       tr.chrg.cpMin = --sel.cpMin;\r
7070       if (sel.cpMin < 0) break;\r
7071       tr.lpstrText = name;\r
7072       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7073     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7074     sel.cpMin++;\r
7075 \r
7076     do {\r
7077       tr.chrg.cpMin = sel.cpMax;\r
7078       tr.chrg.cpMax = ++sel.cpMax;\r
7079       tr.lpstrText = name;\r
7080       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7081     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7082     sel.cpMax--;\r
7083 \r
7084     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7085       MessageBeep(MB_ICONEXCLAMATION);\r
7086       return;\r
7087     }\r
7088     tr.chrg = sel;\r
7089     tr.lpstrText = name;\r
7090     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7091   } else {\r
7092     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7093       MessageBeep(MB_ICONEXCLAMATION);\r
7094       return;\r
7095     }\r
7096     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7097   }\r
7098   if (immediate) {\r
7099     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
7100     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
7101     SetWindowText(hInput, buf);\r
7102     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7103   } else {\r
7104     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
7105       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
7106     SetWindowText(hInput, buf);\r
7107     sel.cpMin = 999999;\r
7108     sel.cpMax = 999999;\r
7109     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7110     SetFocus(hInput);\r
7111   }\r
7112 }\r
7113 \r
7114 LRESULT CALLBACK \r
7115 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7116 {\r
7117   HWND hInput;\r
7118   CHARRANGE sel;\r
7119 \r
7120   switch (message) {\r
7121   case WM_KEYDOWN:\r
7122     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7123     if(wParam=='R') return 0;\r
7124     switch (wParam) {\r
7125     case VK_PRIOR:\r
7126       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7127       return 0;\r
7128     case VK_NEXT:\r
7129       sel.cpMin = 999999;\r
7130       sel.cpMax = 999999;\r
7131       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7132       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7133       return 0;\r
7134     }\r
7135     break;\r
7136   case WM_CHAR:\r
7137    if(wParam != '\022') {\r
7138     if (wParam == '\t') {\r
7139       if (GetKeyState(VK_SHIFT) < 0) {\r
7140         /* shifted */\r
7141         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7142         if (buttonDesc[0].hwnd) {\r
7143           SetFocus(buttonDesc[0].hwnd);\r
7144         } else {\r
7145           SetFocus(hwndMain);\r
7146         }\r
7147       } else {\r
7148         /* unshifted */\r
7149         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7150       }\r
7151     } else {\r
7152       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7153       JAWS_DELETE( SetFocus(hInput); )\r
7154       SendMessage(hInput, message, wParam, lParam);\r
7155     }\r
7156     return 0;\r
7157    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7158    lParam = -1;\r
7159   case WM_RBUTTONDOWN:\r
7160     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7161       /* Move selection here if it was empty */\r
7162       POINT pt;\r
7163       pt.x = LOWORD(lParam);\r
7164       pt.y = HIWORD(lParam);\r
7165       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7166       if (sel.cpMin == sel.cpMax) {\r
7167         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7168         sel.cpMax = sel.cpMin;\r
7169         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7170       }\r
7171       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7172 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7173       POINT pt;\r
7174       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7175       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7176       if (sel.cpMin == sel.cpMax) {\r
7177         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7178         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7179       }\r
7180       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7181         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7182       }\r
7183       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7184       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7185       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7186       MenuPopup(hwnd, pt, hmenu, -1);\r
7187 }\r
7188     }\r
7189     return 0;\r
7190   case WM_RBUTTONUP:\r
7191     if (GetKeyState(VK_SHIFT) & ~1) {\r
7192       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7193         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7194     }\r
7195     return 0;\r
7196   case WM_PASTE:\r
7197     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7198     SetFocus(hInput);\r
7199     return SendMessage(hInput, message, wParam, lParam);\r
7200   case WM_MBUTTONDOWN:\r
7201     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7202   case WM_COMMAND:\r
7203     switch (LOWORD(wParam)) {\r
7204     case IDM_QuickPaste:\r
7205       {\r
7206         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7207         if (sel.cpMin == sel.cpMax) {\r
7208           MessageBeep(MB_ICONEXCLAMATION);\r
7209           return 0;\r
7210         }\r
7211         SendMessage(hwnd, WM_COPY, 0, 0);\r
7212         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7213         SendMessage(hInput, WM_PASTE, 0, 0);\r
7214         SetFocus(hInput);\r
7215         return 0;\r
7216       }\r
7217     case IDM_Cut:\r
7218       SendMessage(hwnd, WM_CUT, 0, 0);\r
7219       return 0;\r
7220     case IDM_Paste:\r
7221       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7222       return 0;\r
7223     case IDM_Copy:\r
7224       SendMessage(hwnd, WM_COPY, 0, 0);\r
7225       return 0;\r
7226     default:\r
7227       {\r
7228         int i = LOWORD(wParam) - IDM_CommandX;\r
7229         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7230             icsTextMenuEntry[i].command != NULL) {\r
7231           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7232                    icsTextMenuEntry[i].getname,\r
7233                    icsTextMenuEntry[i].immediate);\r
7234           return 0;\r
7235         }\r
7236       }\r
7237       break;\r
7238     }\r
7239     break;\r
7240   }\r
7241   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7242 }\r
7243 \r
7244 WNDPROC consoleInputWindowProc;\r
7245 \r
7246 LRESULT CALLBACK\r
7247 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7248 {\r
7249   char buf[MSG_SIZ];\r
7250   char *p;\r
7251   static BOOL sendNextChar = FALSE;\r
7252   static BOOL quoteNextChar = FALSE;\r
7253   InputSource *is = consoleInputSource;\r
7254   CHARFORMAT cf;\r
7255   CHARRANGE sel;\r
7256 \r
7257   switch (message) {\r
7258   case WM_CHAR:\r
7259     if (!appData.localLineEditing || sendNextChar) {\r
7260       is->buf[0] = (CHAR) wParam;\r
7261       is->count = 1;\r
7262       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7263       sendNextChar = FALSE;\r
7264       return 0;\r
7265     }\r
7266     if (quoteNextChar) {\r
7267       buf[0] = (char) wParam;\r
7268       buf[1] = NULLCHAR;\r
7269       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7270       quoteNextChar = FALSE;\r
7271       return 0;\r
7272     }\r
7273     switch (wParam) {\r
7274     case '\r':   /* Enter key */\r
7275       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7276       if (consoleEcho) SaveInHistory(is->buf);\r
7277       is->buf[is->count++] = '\n';\r
7278       is->buf[is->count] = NULLCHAR;\r
7279       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7280       if (consoleEcho) {\r
7281         ConsoleOutput(is->buf, is->count, TRUE);\r
7282       } else if (appData.localLineEditing) {\r
7283         ConsoleOutput("\n", 1, TRUE);\r
7284       }\r
7285       /* fall thru */\r
7286     case '\033': /* Escape key */\r
7287       SetWindowText(hwnd, "");\r
7288       cf.cbSize = sizeof(CHARFORMAT);\r
7289       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7290       if (consoleEcho) {\r
7291         cf.crTextColor = textAttribs[ColorNormal].color;\r
7292       } else {\r
7293         cf.crTextColor = COLOR_ECHOOFF;\r
7294       }\r
7295       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7296       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7297       return 0;\r
7298     case '\t':   /* Tab key */\r
7299       if (GetKeyState(VK_SHIFT) < 0) {\r
7300         /* shifted */\r
7301         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7302       } else {\r
7303         /* unshifted */\r
7304         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7305         if (buttonDesc[0].hwnd) {\r
7306           SetFocus(buttonDesc[0].hwnd);\r
7307         } else {\r
7308           SetFocus(hwndMain);\r
7309         }\r
7310       }\r
7311       return 0;\r
7312     case '\023': /* Ctrl+S */\r
7313       sendNextChar = TRUE;\r
7314       return 0;\r
7315     case '\021': /* Ctrl+Q */\r
7316       quoteNextChar = TRUE;\r
7317       return 0;\r
7318     JAWS_REPLAY\r
7319     default:\r
7320       break;\r
7321     }\r
7322     break;\r
7323   case WM_KEYDOWN:\r
7324     switch (wParam) {\r
7325     case VK_UP:\r
7326       GetWindowText(hwnd, buf, MSG_SIZ);\r
7327       p = PrevInHistory(buf);\r
7328       if (p != NULL) {\r
7329         SetWindowText(hwnd, p);\r
7330         sel.cpMin = 999999;\r
7331         sel.cpMax = 999999;\r
7332         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7333         return 0;\r
7334       }\r
7335       break;\r
7336     case VK_DOWN:\r
7337       p = NextInHistory();\r
7338       if (p != NULL) {\r
7339         SetWindowText(hwnd, p);\r
7340         sel.cpMin = 999999;\r
7341         sel.cpMax = 999999;\r
7342         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7343         return 0;\r
7344       }\r
7345       break;\r
7346     case VK_HOME:\r
7347     case VK_END:\r
7348       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7349       /* fall thru */\r
7350     case VK_PRIOR:\r
7351     case VK_NEXT:\r
7352       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7353       return 0;\r
7354     }\r
7355     break;\r
7356   case WM_MBUTTONDOWN:\r
7357     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7358       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7359     break;\r
7360   case WM_RBUTTONUP:\r
7361     if (GetKeyState(VK_SHIFT) & ~1) {\r
7362       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7363         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7364     } else {\r
7365       POINT pt;\r
7366       HMENU hmenu;\r
7367       hmenu = LoadMenu(hInst, "InputMenu");\r
7368       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7369       if (sel.cpMin == sel.cpMax) {\r
7370         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7371         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7372       }\r
7373       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7374         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7375       }\r
7376       pt.x = LOWORD(lParam);\r
7377       pt.y = HIWORD(lParam);\r
7378       MenuPopup(hwnd, pt, hmenu, -1);\r
7379     }\r
7380     return 0;\r
7381   case WM_COMMAND:\r
7382     switch (LOWORD(wParam)) { \r
7383     case IDM_Undo:\r
7384       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7385       return 0;\r
7386     case IDM_SelectAll:\r
7387       sel.cpMin = 0;\r
7388       sel.cpMax = -1; /*999999?*/\r
7389       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7390       return 0;\r
7391     case IDM_Cut:\r
7392       SendMessage(hwnd, WM_CUT, 0, 0);\r
7393       return 0;\r
7394     case IDM_Paste:\r
7395       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7396       return 0;\r
7397     case IDM_Copy:\r
7398       SendMessage(hwnd, WM_COPY, 0, 0);\r
7399       return 0;\r
7400     }\r
7401     break;\r
7402   }\r
7403   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7404 }\r
7405 \r
7406 #define CO_MAX  100000\r
7407 #define CO_TRIM   1000\r
7408 \r
7409 LRESULT CALLBACK\r
7410 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7411 {\r
7412   static SnapData sd;\r
7413   HWND hText, hInput;\r
7414   RECT rect;\r
7415   static int sizeX, sizeY;\r
7416   int newSizeX, newSizeY;\r
7417   MINMAXINFO *mmi;\r
7418   WORD wMask;\r
7419 \r
7420   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7421   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7422 \r
7423   switch (message) {\r
7424   case WM_NOTIFY:\r
7425     if (((NMHDR*)lParam)->code == EN_LINK)\r
7426     {\r
7427       ENLINK *pLink = (ENLINK*)lParam;\r
7428       if (pLink->msg == WM_LBUTTONUP)\r
7429       {\r
7430         TEXTRANGE tr;\r
7431 \r
7432         tr.chrg = pLink->chrg;\r
7433         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7434         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7435         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7436         free(tr.lpstrText);\r
7437       }\r
7438     }\r
7439     break;\r
7440   case WM_INITDIALOG: /* message: initialize dialog box */\r
7441     hwndConsole = hDlg;\r
7442     SetFocus(hInput);\r
7443     consoleTextWindowProc = (WNDPROC)\r
7444       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7445     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7446     consoleInputWindowProc = (WNDPROC)\r
7447       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7448     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7449     Colorize(ColorNormal, TRUE);\r
7450     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7451     ChangedConsoleFont();\r
7452     GetClientRect(hDlg, &rect);\r
7453     sizeX = rect.right;\r
7454     sizeY = rect.bottom;\r
7455     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7456         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7457       WINDOWPLACEMENT wp;\r
7458       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7459       wp.length = sizeof(WINDOWPLACEMENT);\r
7460       wp.flags = 0;\r
7461       wp.showCmd = SW_SHOW;\r
7462       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7463       wp.rcNormalPosition.left = wpConsole.x;\r
7464       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7465       wp.rcNormalPosition.top = wpConsole.y;\r
7466       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7467       SetWindowPlacement(hDlg, &wp);\r
7468     }\r
7469 \r
7470    // [HGM] Chessknight's change 2004-07-13\r
7471    else { /* Determine Defaults */\r
7472        WINDOWPLACEMENT wp;\r
7473        wpConsole.x = wpMain.width + 1;\r
7474        wpConsole.y = wpMain.y;\r
7475        wpConsole.width = screenWidth -  wpMain.width;\r
7476        wpConsole.height = wpMain.height;\r
7477        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7478        wp.length = sizeof(WINDOWPLACEMENT);\r
7479        wp.flags = 0;\r
7480        wp.showCmd = SW_SHOW;\r
7481        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7482        wp.rcNormalPosition.left = wpConsole.x;\r
7483        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7484        wp.rcNormalPosition.top = wpConsole.y;\r
7485        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7486        SetWindowPlacement(hDlg, &wp);\r
7487     }\r
7488 \r
7489    // Allow hText to highlight URLs and send notifications on them\r
7490    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7491    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7492    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7493    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7494 \r
7495     return FALSE;\r
7496 \r
7497   case WM_SETFOCUS:\r
7498     SetFocus(hInput);\r
7499     return 0;\r
7500 \r
7501   case WM_CLOSE:\r
7502     ExitEvent(0);\r
7503     /* not reached */\r
7504     break;\r
7505 \r
7506   case WM_SIZE:\r
7507     if (IsIconic(hDlg)) break;\r
7508     newSizeX = LOWORD(lParam);\r
7509     newSizeY = HIWORD(lParam);\r
7510     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7511       RECT rectText, rectInput;\r
7512       POINT pt;\r
7513       int newTextHeight, newTextWidth;\r
7514       GetWindowRect(hText, &rectText);\r
7515       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7516       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7517       if (newTextHeight < 0) {\r
7518         newSizeY += -newTextHeight;\r
7519         newTextHeight = 0;\r
7520       }\r
7521       SetWindowPos(hText, NULL, 0, 0,\r
7522         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7523       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7524       pt.x = rectInput.left;\r
7525       pt.y = rectInput.top + newSizeY - sizeY;\r
7526       ScreenToClient(hDlg, &pt);\r
7527       SetWindowPos(hInput, NULL, \r
7528         pt.x, pt.y, /* needs client coords */   \r
7529         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7530         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7531     }\r
7532     sizeX = newSizeX;\r
7533     sizeY = newSizeY;\r
7534     break;\r
7535 \r
7536   case WM_GETMINMAXINFO:\r
7537     /* Prevent resizing window too small */\r
7538     mmi = (MINMAXINFO *) lParam;\r
7539     mmi->ptMinTrackSize.x = 100;\r
7540     mmi->ptMinTrackSize.y = 100;\r
7541     break;\r
7542 \r
7543   /* [AS] Snapping */\r
7544   case WM_ENTERSIZEMOVE:\r
7545     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7546 \r
7547   case WM_SIZING:\r
7548     return OnSizing( &sd, hDlg, wParam, lParam );\r
7549 \r
7550   case WM_MOVING:\r
7551     return OnMoving( &sd, hDlg, wParam, lParam );\r
7552 \r
7553   case WM_EXITSIZEMOVE:\r
7554         UpdateICSWidth(hText);\r
7555     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7556   }\r
7557 \r
7558   return DefWindowProc(hDlg, message, wParam, lParam);\r
7559 }\r
7560 \r
7561 \r
7562 VOID\r
7563 ConsoleCreate()\r
7564 {\r
7565   HWND hCons;\r
7566   if (hwndConsole) return;\r
7567   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7568   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7569 }\r
7570 \r
7571 \r
7572 VOID\r
7573 ConsoleOutput(char* data, int length, int forceVisible)\r
7574 {\r
7575   HWND hText;\r
7576   int trim, exlen;\r
7577   char *p, *q;\r
7578   char buf[CO_MAX+1];\r
7579   POINT pEnd;\r
7580   RECT rect;\r
7581   static int delayLF = 0;\r
7582   CHARRANGE savesel, sel;\r
7583 \r
7584   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7585   p = data;\r
7586   q = buf;\r
7587   if (delayLF) {\r
7588     *q++ = '\r';\r
7589     *q++ = '\n';\r
7590     delayLF = 0;\r
7591   }\r
7592   while (length--) {\r
7593     if (*p == '\n') {\r
7594       if (*++p) {\r
7595         *q++ = '\r';\r
7596         *q++ = '\n';\r
7597       } else {\r
7598         delayLF = 1;\r
7599       }\r
7600     } else if (*p == '\007') {\r
7601        MyPlaySound(&sounds[(int)SoundBell]);\r
7602        p++;\r
7603     } else {\r
7604       *q++ = *p++;\r
7605     }\r
7606   }\r
7607   *q = NULLCHAR;\r
7608   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7609   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7610   /* Save current selection */\r
7611   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7612   exlen = GetWindowTextLength(hText);\r
7613   /* Find out whether current end of text is visible */\r
7614   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7615   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7616   /* Trim existing text if it's too long */\r
7617   if (exlen + (q - buf) > CO_MAX) {\r
7618     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7619     sel.cpMin = 0;\r
7620     sel.cpMax = trim;\r
7621     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7622     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7623     exlen -= trim;\r
7624     savesel.cpMin -= trim;\r
7625     savesel.cpMax -= trim;\r
7626     if (exlen < 0) exlen = 0;\r
7627     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7628     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7629   }\r
7630   /* Append the new text */\r
7631   sel.cpMin = exlen;\r
7632   sel.cpMax = exlen;\r
7633   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7634   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7635   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7636   if (forceVisible || exlen == 0 ||\r
7637       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7638        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7639     /* Scroll to make new end of text visible if old end of text\r
7640        was visible or new text is an echo of user typein */\r
7641     sel.cpMin = 9999999;\r
7642     sel.cpMax = 9999999;\r
7643     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7644     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7645     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7646     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7647   }\r
7648   if (savesel.cpMax == exlen || forceVisible) {\r
7649     /* Move insert point to new end of text if it was at the old\r
7650        end of text or if the new text is an echo of user typein */\r
7651     sel.cpMin = 9999999;\r
7652     sel.cpMax = 9999999;\r
7653     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7654   } else {\r
7655     /* Restore previous selection */\r
7656     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7657   }\r
7658   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7659 }\r
7660 \r
7661 /*---------*/\r
7662 \r
7663 \r
7664 void\r
7665 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7666 {\r
7667   char buf[100];\r
7668   char *str;\r
7669   COLORREF oldFg, oldBg;\r
7670   HFONT oldFont;\r
7671   RECT rect;\r
7672 \r
7673   if(copyNumber > 1)\r
7674     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7675 \r
7676   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7677   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7678   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7679 \r
7680   rect.left = x;\r
7681   rect.right = x + squareSize;\r
7682   rect.top  = y;\r
7683   rect.bottom = y + squareSize;\r
7684   str = buf;\r
7685 \r
7686   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7687                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7688              y, ETO_CLIPPED|ETO_OPAQUE,\r
7689              &rect, str, strlen(str), NULL);\r
7690 \r
7691   (void) SetTextColor(hdc, oldFg);\r
7692   (void) SetBkColor(hdc, oldBg);\r
7693   (void) SelectObject(hdc, oldFont);\r
7694 }\r
7695 \r
7696 void\r
7697 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7698               RECT *rect, char *color, char *flagFell)\r
7699 {\r
7700   char buf[100];\r
7701   char *str;\r
7702   COLORREF oldFg, oldBg;\r
7703   HFONT oldFont;\r
7704 \r
7705   if (twoBoards && partnerUp) return;\r
7706   if (appData.clockMode) {\r
7707     if (tinyLayout == 2)\r
7708       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7709     else\r
7710       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7711     str = buf;\r
7712   } else {\r
7713     str = color;\r
7714   }\r
7715 \r
7716   if (highlight) {\r
7717     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7718     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7719   } else {\r
7720     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7721     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7722   }\r
7723 \r
7724   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7725 \r
7726   JAWS_SILENCE\r
7727 \r
7728   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7729              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7730              rect, str, strlen(str), NULL);\r
7731   if(logoHeight > 0 && appData.clockMode) {\r
7732       RECT r;\r
7733       str += strlen(color)+2;\r
7734       r.top = rect->top + logoHeight/2;\r
7735       r.left = rect->left;\r
7736       r.right = rect->right;\r
7737       r.bottom = rect->bottom;\r
7738       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7739                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7740                  &r, str, strlen(str), NULL);\r
7741   }\r
7742   (void) SetTextColor(hdc, oldFg);\r
7743   (void) SetBkColor(hdc, oldBg);\r
7744   (void) SelectObject(hdc, oldFont);\r
7745 }\r
7746 \r
7747 \r
7748 int\r
7749 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7750            OVERLAPPED *ovl)\r
7751 {\r
7752   int ok, err;\r
7753 \r
7754   /* [AS]  */\r
7755   if( count <= 0 ) {\r
7756     if (appData.debugMode) {\r
7757       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7758     }\r
7759 \r
7760     return ERROR_INVALID_USER_BUFFER;\r
7761   }\r
7762 \r
7763   ResetEvent(ovl->hEvent);\r
7764   ovl->Offset = ovl->OffsetHigh = 0;\r
7765   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7766   if (ok) {\r
7767     err = NO_ERROR;\r
7768   } else {\r
7769     err = GetLastError();\r
7770     if (err == ERROR_IO_PENDING) {\r
7771       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7772       if (ok)\r
7773         err = NO_ERROR;\r
7774       else\r
7775         err = GetLastError();\r
7776     }\r
7777   }\r
7778   return err;\r
7779 }\r
7780 \r
7781 int\r
7782 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7783             OVERLAPPED *ovl)\r
7784 {\r
7785   int ok, err;\r
7786 \r
7787   ResetEvent(ovl->hEvent);\r
7788   ovl->Offset = ovl->OffsetHigh = 0;\r
7789   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7790   if (ok) {\r
7791     err = NO_ERROR;\r
7792   } else {\r
7793     err = GetLastError();\r
7794     if (err == ERROR_IO_PENDING) {\r
7795       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7796       if (ok)\r
7797         err = NO_ERROR;\r
7798       else\r
7799         err = GetLastError();\r
7800     }\r
7801 \r
7802   }\r
7803   return err;\r
7804 }\r
7805 \r
7806 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7807 void CheckForInputBufferFull( InputSource * is )\r
7808 {\r
7809     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7810         /* Look for end of line */\r
7811         char * p = is->buf;\r
7812         \r
7813         while( p < is->next && *p != '\n' ) {\r
7814             p++;\r
7815         }\r
7816 \r
7817         if( p >= is->next ) {\r
7818             if (appData.debugMode) {\r
7819                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7820             }\r
7821 \r
7822             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7823             is->count = (DWORD) -1;\r
7824             is->next = is->buf;\r
7825         }\r
7826     }\r
7827 }\r
7828 \r
7829 DWORD\r
7830 InputThread(LPVOID arg)\r
7831 {\r
7832   InputSource *is;\r
7833   OVERLAPPED ovl;\r
7834 \r
7835   is = (InputSource *) arg;\r
7836   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7837   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7838   while (is->hThread != NULL) {\r
7839     is->error = DoReadFile(is->hFile, is->next,\r
7840                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7841                            &is->count, &ovl);\r
7842     if (is->error == NO_ERROR) {\r
7843       is->next += is->count;\r
7844     } else {\r
7845       if (is->error == ERROR_BROKEN_PIPE) {\r
7846         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7847         is->count = 0;\r
7848       } else {\r
7849         is->count = (DWORD) -1;\r
7850         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7851         break; \r
7852       }\r
7853     }\r
7854 \r
7855     CheckForInputBufferFull( is );\r
7856 \r
7857     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7858 \r
7859     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7860 \r
7861     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7862   }\r
7863 \r
7864   CloseHandle(ovl.hEvent);\r
7865   CloseHandle(is->hFile);\r
7866 \r
7867   if (appData.debugMode) {\r
7868     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7869   }\r
7870 \r
7871   return 0;\r
7872 }\r
7873 \r
7874 \r
7875 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7876 DWORD\r
7877 NonOvlInputThread(LPVOID arg)\r
7878 {\r
7879   InputSource *is;\r
7880   char *p, *q;\r
7881   int i;\r
7882   char prev;\r
7883 \r
7884   is = (InputSource *) arg;\r
7885   while (is->hThread != NULL) {\r
7886     is->error = ReadFile(is->hFile, is->next,\r
7887                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7888                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7889     if (is->error == NO_ERROR) {\r
7890       /* Change CRLF to LF */\r
7891       if (is->next > is->buf) {\r
7892         p = is->next - 1;\r
7893         i = is->count + 1;\r
7894       } else {\r
7895         p = is->next;\r
7896         i = is->count;\r
7897       }\r
7898       q = p;\r
7899       prev = NULLCHAR;\r
7900       while (i > 0) {\r
7901         if (prev == '\r' && *p == '\n') {\r
7902           *(q-1) = '\n';\r
7903           is->count--;\r
7904         } else { \r
7905           *q++ = *p;\r
7906         }\r
7907         prev = *p++;\r
7908         i--;\r
7909       }\r
7910       *q = NULLCHAR;\r
7911       is->next = q;\r
7912     } else {\r
7913       if (is->error == ERROR_BROKEN_PIPE) {\r
7914         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7915         is->count = 0; \r
7916       } else {\r
7917         is->count = (DWORD) -1;\r
7918       }\r
7919     }\r
7920 \r
7921     CheckForInputBufferFull( is );\r
7922 \r
7923     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7924 \r
7925     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7926 \r
7927     if (is->count < 0) break;  /* Quit on error */\r
7928   }\r
7929   CloseHandle(is->hFile);\r
7930   return 0;\r
7931 }\r
7932 \r
7933 DWORD\r
7934 SocketInputThread(LPVOID arg)\r
7935 {\r
7936   InputSource *is;\r
7937 \r
7938   is = (InputSource *) arg;\r
7939   while (is->hThread != NULL) {\r
7940     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7941     if ((int)is->count == SOCKET_ERROR) {\r
7942       is->count = (DWORD) -1;\r
7943       is->error = WSAGetLastError();\r
7944     } else {\r
7945       is->error = NO_ERROR;\r
7946       is->next += is->count;\r
7947       if (is->count == 0 && is->second == is) {\r
7948         /* End of file on stderr; quit with no message */\r
7949         break;\r
7950       }\r
7951     }\r
7952     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7953 \r
7954     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7955 \r
7956     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7957   }\r
7958   return 0;\r
7959 }\r
7960 \r
7961 VOID\r
7962 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7963 {\r
7964   InputSource *is;\r
7965 \r
7966   is = (InputSource *) lParam;\r
7967   if (is->lineByLine) {\r
7968     /* Feed in lines one by one */\r
7969     char *p = is->buf;\r
7970     char *q = p;\r
7971     while (q < is->next) {\r
7972       if (*q++ == '\n') {\r
7973         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7974         p = q;\r
7975       }\r
7976     }\r
7977     \r
7978     /* Move any partial line to the start of the buffer */\r
7979     q = is->buf;\r
7980     while (p < is->next) {\r
7981       *q++ = *p++;\r
7982     }\r
7983     is->next = q;\r
7984 \r
7985     if (is->error != NO_ERROR || is->count == 0) {\r
7986       /* Notify backend of the error.  Note: If there was a partial\r
7987          line at the end, it is not flushed through. */\r
7988       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7989     }\r
7990   } else {\r
7991     /* Feed in the whole chunk of input at once */\r
7992     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7993     is->next = is->buf;\r
7994   }\r
7995 }\r
7996 \r
7997 /*---------------------------------------------------------------------------*\\r
7998  *\r
7999  *  Menu enables. Used when setting various modes.\r
8000  *\r
8001 \*---------------------------------------------------------------------------*/\r
8002 \r
8003 typedef struct {\r
8004   int item;\r
8005   int flags;\r
8006 } Enables;\r
8007 \r
8008 VOID\r
8009 GreyRevert(Boolean grey)\r
8010 { // [HGM] vari: for retracting variations in local mode\r
8011   HMENU hmenu = GetMenu(hwndMain);\r
8012   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
8013   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
8014 }\r
8015 \r
8016 VOID\r
8017 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8018 {\r
8019   while (enab->item > 0) {\r
8020     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8021     enab++;\r
8022   }\r
8023 }\r
8024 \r
8025 Enables gnuEnables[] = {\r
8026   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8027   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8028   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8029   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8030   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8031   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8032   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8033   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8034   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8035   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
8036   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8037   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8038   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8039 \r
8040 \r
8041   // Needed to switch from ncp to GNU mode on Engine Load\r
8042   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8043   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8044   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8045   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8046   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8047   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8048   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
8049   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8050   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
8051   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
8052   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8053   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8054   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8055   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8056   { -1, -1 }\r
8057 };\r
8058 \r
8059 Enables icsEnables[] = {\r
8060   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8061   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8062   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8063   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8064   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8065   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8066   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8067   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8068   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8069   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8070   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8071   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8072   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8073   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
8074   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
8075   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8076   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8077   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8078   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8079   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
8080   { -1, -1 }\r
8081 };\r
8082 \r
8083 #if ZIPPY\r
8084 Enables zippyEnables[] = {\r
8085   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8086   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8087   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8088   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8089   { -1, -1 }\r
8090 };\r
8091 #endif\r
8092 \r
8093 Enables ncpEnables[] = {\r
8094   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8095   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8096   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8097   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8098   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8099   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8100   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8101   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8102   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8103   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8104   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8105   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8106   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8107   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8108   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8109   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8110   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8111   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8112   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8113   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8114   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8115   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
8116   { -1, -1 }\r
8117 };\r
8118 \r
8119 Enables trainingOnEnables[] = {\r
8120   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8121   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
8122   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8123   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8124   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8125   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8126   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8127   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8128   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8129   { -1, -1 }\r
8130 };\r
8131 \r
8132 Enables trainingOffEnables[] = {\r
8133   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8134   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8135   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8136   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8137   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8138   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8139   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8140   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8141   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8142   { -1, -1 }\r
8143 };\r
8144 \r
8145 /* These modify either ncpEnables or gnuEnables */\r
8146 Enables cmailEnables[] = {\r
8147   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8148   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8149   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8150   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8151   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8152   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8153   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8154   { -1, -1 }\r
8155 };\r
8156 \r
8157 Enables machineThinkingEnables[] = {\r
8158   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8159   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8160   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8161   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8162   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8163   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8164   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8165   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8166   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8167   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8168   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8169   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8170   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8171 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8172   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8173   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8174   { -1, -1 }\r
8175 };\r
8176 \r
8177 Enables userThinkingEnables[] = {\r
8178   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8179   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8180   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8181   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8182   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8183   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8184   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8185   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8186   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8187   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8188   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8189   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8190   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8191 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8192   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8193   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8194   { -1, -1 }\r
8195 };\r
8196 \r
8197 /*---------------------------------------------------------------------------*\\r
8198  *\r
8199  *  Front-end interface functions exported by XBoard.\r
8200  *  Functions appear in same order as prototypes in frontend.h.\r
8201  * \r
8202 \*---------------------------------------------------------------------------*/\r
8203 VOID\r
8204 CheckMark(UINT item, int state)\r
8205 {\r
8206     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8207 }\r
8208 \r
8209 VOID\r
8210 ModeHighlight()\r
8211 {\r
8212   static UINT prevChecked = 0;\r
8213   static int prevPausing = 0;\r
8214   UINT nowChecked;\r
8215 \r
8216   if (pausing != prevPausing) {\r
8217     prevPausing = pausing;\r
8218     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8219                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8220     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8221   }\r
8222 \r
8223   switch (gameMode) {\r
8224   case BeginningOfGame:\r
8225     if (appData.icsActive)\r
8226       nowChecked = IDM_IcsClient;\r
8227     else if (appData.noChessProgram)\r
8228       nowChecked = IDM_EditGame;\r
8229     else\r
8230       nowChecked = IDM_MachineBlack;\r
8231     break;\r
8232   case MachinePlaysBlack:\r
8233     nowChecked = IDM_MachineBlack;\r
8234     break;\r
8235   case MachinePlaysWhite:\r
8236     nowChecked = IDM_MachineWhite;\r
8237     break;\r
8238   case TwoMachinesPlay:\r
8239     nowChecked = IDM_TwoMachines;\r
8240     break;\r
8241   case AnalyzeMode:\r
8242     nowChecked = IDM_AnalysisMode;\r
8243     break;\r
8244   case AnalyzeFile:\r
8245     nowChecked = IDM_AnalyzeFile;\r
8246     break;\r
8247   case EditGame:\r
8248     nowChecked = IDM_EditGame;\r
8249     break;\r
8250   case PlayFromGameFile:\r
8251     nowChecked = IDM_LoadGame;\r
8252     break;\r
8253   case EditPosition:\r
8254     nowChecked = IDM_EditPosition;\r
8255     break;\r
8256   case Training:\r
8257     nowChecked = IDM_Training;\r
8258     break;\r
8259   case IcsPlayingWhite:\r
8260   case IcsPlayingBlack:\r
8261   case IcsObserving:\r
8262   case IcsIdle:\r
8263     nowChecked = IDM_IcsClient;\r
8264     break;\r
8265   default:\r
8266   case EndOfGame:\r
8267     nowChecked = 0;\r
8268     break;\r
8269   }\r
8270   if(prevChecked == IDM_TwoMachines) // [HGM] 'Machine Match' might have gotten disabled when stopping match\r
8271     EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_ENABLED);\r
8272   CheckMark(prevChecked, MF_UNCHECKED);\r
8273   CheckMark(nowChecked, MF_CHECKED);\r
8274   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8275 \r
8276   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8277     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8278                           MF_BYCOMMAND|MF_ENABLED);\r
8279   } else {\r
8280     (void) EnableMenuItem(GetMenu(hwndMain), \r
8281                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8282   }\r
8283 \r
8284   prevChecked = nowChecked;\r
8285 \r
8286   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8287   if (appData.icsActive) {\r
8288        if (appData.icsEngineAnalyze) {\r
8289                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8290        } else {\r
8291                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8292        }\r
8293   }\r
8294   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8295 }\r
8296 \r
8297 VOID\r
8298 SetICSMode()\r
8299 {\r
8300   HMENU hmenu = GetMenu(hwndMain);\r
8301   SetMenuEnables(hmenu, icsEnables);\r
8302   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8303     MF_BYCOMMAND|MF_ENABLED);\r
8304 #if ZIPPY\r
8305   if (appData.zippyPlay) {\r
8306     SetMenuEnables(hmenu, zippyEnables);\r
8307     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8308          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8309           MF_BYCOMMAND|MF_ENABLED);\r
8310   }\r
8311 #endif\r
8312 }\r
8313 \r
8314 VOID\r
8315 SetGNUMode()\r
8316 {\r
8317   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8318 }\r
8319 \r
8320 VOID\r
8321 SetNCPMode()\r
8322 {\r
8323   HMENU hmenu = GetMenu(hwndMain);\r
8324   SetMenuEnables(hmenu, ncpEnables);\r
8325     DrawMenuBar(hwndMain);\r
8326 }\r
8327 \r
8328 VOID\r
8329 SetCmailMode()\r
8330 {\r
8331   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8332 }\r
8333 \r
8334 VOID \r
8335 SetTrainingModeOn()\r
8336 {\r
8337   int i;\r
8338   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8339   for (i = 0; i < N_BUTTONS; i++) {\r
8340     if (buttonDesc[i].hwnd != NULL)\r
8341       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8342   }\r
8343   CommentPopDown();\r
8344 }\r
8345 \r
8346 VOID SetTrainingModeOff()\r
8347 {\r
8348   int i;\r
8349   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8350   for (i = 0; i < N_BUTTONS; i++) {\r
8351     if (buttonDesc[i].hwnd != NULL)\r
8352       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8353   }\r
8354 }\r
8355 \r
8356 \r
8357 VOID\r
8358 SetUserThinkingEnables()\r
8359 {\r
8360   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8361 }\r
8362 \r
8363 VOID\r
8364 SetMachineThinkingEnables()\r
8365 {\r
8366   HMENU hMenu = GetMenu(hwndMain);\r
8367   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8368 \r
8369   SetMenuEnables(hMenu, machineThinkingEnables);\r
8370 \r
8371   if (gameMode == MachinePlaysBlack) {\r
8372     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8373   } else if (gameMode == MachinePlaysWhite) {\r
8374     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8375   } else if (gameMode == TwoMachinesPlay) {\r
8376     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8377   }\r
8378 }\r
8379 \r
8380 \r
8381 VOID\r
8382 DisplayTitle(char *str)\r
8383 {\r
8384   char title[MSG_SIZ], *host;\r
8385   if (str[0] != NULLCHAR) {\r
8386     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8387   } else if (appData.icsActive) {\r
8388     if (appData.icsCommPort[0] != NULLCHAR)\r
8389       host = "ICS";\r
8390     else \r
8391       host = appData.icsHost;\r
8392       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8393   } else if (appData.noChessProgram) {\r
8394     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8395   } else {\r
8396     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8397     strcat(title, ": ");\r
8398     strcat(title, first.tidy);\r
8399   }\r
8400   SetWindowText(hwndMain, title);\r
8401 }\r
8402 \r
8403 \r
8404 VOID\r
8405 DisplayMessage(char *str1, char *str2)\r
8406 {\r
8407   HDC hdc;\r
8408   HFONT oldFont;\r
8409   int remain = MESSAGE_TEXT_MAX - 1;\r
8410   int len;\r
8411 \r
8412   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8413   messageText[0] = NULLCHAR;\r
8414   if (*str1) {\r
8415     len = strlen(str1);\r
8416     if (len > remain) len = remain;\r
8417     strncpy(messageText, str1, len);\r
8418     messageText[len] = NULLCHAR;\r
8419     remain -= len;\r
8420   }\r
8421   if (*str2 && remain >= 2) {\r
8422     if (*str1) {\r
8423       strcat(messageText, "  ");\r
8424       remain -= 2;\r
8425     }\r
8426     len = strlen(str2);\r
8427     if (len > remain) len = remain;\r
8428     strncat(messageText, str2, len);\r
8429   }\r
8430   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8431   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8432 \r
8433   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8434 \r
8435   SAYMACHINEMOVE();\r
8436 \r
8437   hdc = GetDC(hwndMain);\r
8438   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8439   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8440              &messageRect, messageText, strlen(messageText), NULL);\r
8441   (void) SelectObject(hdc, oldFont);\r
8442   (void) ReleaseDC(hwndMain, hdc);\r
8443 }\r
8444 \r
8445 VOID\r
8446 DisplayError(char *str, int error)\r
8447 {\r
8448   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8449   int len;\r
8450 \r
8451   if (error == 0) {\r
8452     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8453   } else {\r
8454     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8455                         NULL, error, LANG_NEUTRAL,\r
8456                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8457     if (len > 0) {\r
8458       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8459     } else {\r
8460       ErrorMap *em = errmap;\r
8461       while (em->err != 0 && em->err != error) em++;\r
8462       if (em->err != 0) {\r
8463         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8464       } else {\r
8465         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8466       }\r
8467     }\r
8468   }\r
8469   \r
8470   ErrorPopUp(_("Error"), buf);\r
8471 }\r
8472 \r
8473 \r
8474 VOID\r
8475 DisplayMoveError(char *str)\r
8476 {\r
8477   fromX = fromY = -1;\r
8478   ClearHighlights();\r
8479   DrawPosition(FALSE, NULL);\r
8480   if (appData.popupMoveErrors) {\r
8481     ErrorPopUp(_("Error"), str);\r
8482   } else {\r
8483     DisplayMessage(str, "");\r
8484     moveErrorMessageUp = TRUE;\r
8485   }\r
8486 }\r
8487 \r
8488 VOID\r
8489 DisplayFatalError(char *str, int error, int exitStatus)\r
8490 {\r
8491   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8492   int len;\r
8493   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8494 \r
8495   if (error != 0) {\r
8496     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8497                         NULL, error, LANG_NEUTRAL,\r
8498                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8499     if (len > 0) {\r
8500       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8501     } else {\r
8502       ErrorMap *em = errmap;\r
8503       while (em->err != 0 && em->err != error) em++;\r
8504       if (em->err != 0) {\r
8505         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8506       } else {\r
8507         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8508       }\r
8509     }\r
8510     str = buf;\r
8511   }\r
8512   if (appData.debugMode) {\r
8513     fprintf(debugFP, "%s: %s\n", label, str);\r
8514   }\r
8515   if (appData.popupExitMessage) {\r
8516     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8517                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8518   }\r
8519   ExitEvent(exitStatus);\r
8520 }\r
8521 \r
8522 \r
8523 VOID\r
8524 DisplayInformation(char *str)\r
8525 {\r
8526   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8527 }\r
8528 \r
8529 \r
8530 VOID\r
8531 DisplayNote(char *str)\r
8532 {\r
8533   ErrorPopUp(_("Note"), str);\r
8534 }\r
8535 \r
8536 \r
8537 typedef struct {\r
8538   char *title, *question, *replyPrefix;\r
8539   ProcRef pr;\r
8540 } QuestionParams;\r
8541 \r
8542 LRESULT CALLBACK\r
8543 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8544 {\r
8545   static QuestionParams *qp;\r
8546   char reply[MSG_SIZ];\r
8547   int len, err;\r
8548 \r
8549   switch (message) {\r
8550   case WM_INITDIALOG:\r
8551     qp = (QuestionParams *) lParam;\r
8552     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8553     Translate(hDlg, DLG_Question);\r
8554     SetWindowText(hDlg, qp->title);\r
8555     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8556     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8557     return FALSE;\r
8558 \r
8559   case WM_COMMAND:\r
8560     switch (LOWORD(wParam)) {\r
8561     case IDOK:\r
8562       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8563       if (*reply) strcat(reply, " ");\r
8564       len = strlen(reply);\r
8565       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8566       strcat(reply, "\n");\r
8567       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8568       EndDialog(hDlg, TRUE);\r
8569       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8570       return TRUE;\r
8571     case IDCANCEL:\r
8572       EndDialog(hDlg, FALSE);\r
8573       return TRUE;\r
8574     default:\r
8575       break;\r
8576     }\r
8577     break;\r
8578   }\r
8579   return FALSE;\r
8580 }\r
8581 \r
8582 VOID\r
8583 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8584 {\r
8585     QuestionParams qp;\r
8586     FARPROC lpProc;\r
8587     \r
8588     qp.title = title;\r
8589     qp.question = question;\r
8590     qp.replyPrefix = replyPrefix;\r
8591     qp.pr = pr;\r
8592     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8593     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8594       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8595     FreeProcInstance(lpProc);\r
8596 }\r
8597 \r
8598 /* [AS] Pick FRC position */\r
8599 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8600 {\r
8601     static int * lpIndexFRC;\r
8602     BOOL index_is_ok;\r
8603     char buf[16];\r
8604 \r
8605     switch( message )\r
8606     {\r
8607     case WM_INITDIALOG:\r
8608         lpIndexFRC = (int *) lParam;\r
8609 \r
8610         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8611         Translate(hDlg, DLG_NewGameFRC);\r
8612 \r
8613         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8614         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8615         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8616         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8617 \r
8618         break;\r
8619 \r
8620     case WM_COMMAND:\r
8621         switch( LOWORD(wParam) ) {\r
8622         case IDOK:\r
8623             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8624             EndDialog( hDlg, 0 );\r
8625             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8626             return TRUE;\r
8627         case IDCANCEL:\r
8628             EndDialog( hDlg, 1 );   \r
8629             return TRUE;\r
8630         case IDC_NFG_Edit:\r
8631             if( HIWORD(wParam) == EN_CHANGE ) {\r
8632                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8633 \r
8634                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8635             }\r
8636             return TRUE;\r
8637         case IDC_NFG_Random:\r
8638           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8639             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8640             return TRUE;\r
8641         }\r
8642 \r
8643         break;\r
8644     }\r
8645 \r
8646     return FALSE;\r
8647 }\r
8648 \r
8649 int NewGameFRC()\r
8650 {\r
8651     int result;\r
8652     int index = appData.defaultFrcPosition;\r
8653     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8654 \r
8655     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8656 \r
8657     if( result == 0 ) {\r
8658         appData.defaultFrcPosition = index;\r
8659     }\r
8660 \r
8661     return result;\r
8662 }\r
8663 \r
8664 /* [AS] Game list options. Refactored by HGM */\r
8665 \r
8666 HWND gameListOptionsDialog;\r
8667 \r
8668 // low-level front-end: clear text edit / list widget\r
8669 void\r
8670 \r
8671 GLT_ClearList()\r
8672 {\r
8673     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8674 }\r
8675 \r
8676 // low-level front-end: clear text edit / list widget\r
8677 void\r
8678 GLT_DeSelectList()\r
8679 {\r
8680     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8681 }\r
8682 \r
8683 // low-level front-end: append line to text edit / list widget\r
8684 void\r
8685 GLT_AddToList( char *name )\r
8686 {\r
8687     if( name != 0 ) {\r
8688             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8689     }\r
8690 }\r
8691 \r
8692 // low-level front-end: get line from text edit / list widget\r
8693 Boolean\r
8694 GLT_GetFromList( int index, char *name )\r
8695 {\r
8696     if( name != 0 ) {\r
8697             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8698                 return TRUE;\r
8699     }\r
8700     return FALSE;\r
8701 }\r
8702 \r
8703 void GLT_MoveSelection( HWND hDlg, int delta )\r
8704 {\r
8705     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8706     int idx2 = idx1 + delta;\r
8707     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8708 \r
8709     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8710         char buf[128];\r
8711 \r
8712         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8713         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8714         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8715         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8716     }\r
8717 }\r
8718 \r
8719 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8720 {\r
8721     switch( message )\r
8722     {\r
8723     case WM_INITDIALOG:\r
8724         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8725         \r
8726         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8727         Translate(hDlg, DLG_GameListOptions);\r
8728 \r
8729         /* Initialize list */\r
8730         GLT_TagsToList( lpUserGLT );\r
8731 \r
8732         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8733 \r
8734         break;\r
8735 \r
8736     case WM_COMMAND:\r
8737         switch( LOWORD(wParam) ) {\r
8738         case IDOK:\r
8739             GLT_ParseList();\r
8740             EndDialog( hDlg, 0 );\r
8741             return TRUE;\r
8742         case IDCANCEL:\r
8743             EndDialog( hDlg, 1 );\r
8744             return TRUE;\r
8745 \r
8746         case IDC_GLT_Default:\r
8747             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8748             return TRUE;\r
8749 \r
8750         case IDC_GLT_Restore:\r
8751             GLT_TagsToList( appData.gameListTags );\r
8752             return TRUE;\r
8753 \r
8754         case IDC_GLT_Up:\r
8755             GLT_MoveSelection( hDlg, -1 );\r
8756             return TRUE;\r
8757 \r
8758         case IDC_GLT_Down:\r
8759             GLT_MoveSelection( hDlg, +1 );\r
8760             return TRUE;\r
8761         }\r
8762 \r
8763         break;\r
8764     }\r
8765 \r
8766     return FALSE;\r
8767 }\r
8768 \r
8769 int GameListOptions()\r
8770 {\r
8771     int result;\r
8772     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8773 \r
8774       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8775 \r
8776     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8777 \r
8778     if( result == 0 ) {\r
8779         char *oldTags = appData.gameListTags;\r
8780         /* [AS] Memory leak here! */\r
8781         appData.gameListTags = strdup( lpUserGLT ); \r
8782         if(strcmp(oldTags, appData.gameListTags)) // [HGM] redo Game List when we changed something\r
8783             GameListToListBox(NULL, TRUE, ".", NULL, FALSE, FALSE); // "." as filter is kludge to select all\r
8784     }\r
8785 \r
8786     return result;\r
8787 }\r
8788 \r
8789 VOID\r
8790 DisplayIcsInteractionTitle(char *str)\r
8791 {\r
8792   char consoleTitle[MSG_SIZ];\r
8793 \r
8794     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8795     SetWindowText(hwndConsole, consoleTitle);\r
8796 \r
8797     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8798       char buf[MSG_SIZ], *p = buf, *q;\r
8799         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8800       do {\r
8801         q = strchr(p, ';');\r
8802         if(q) *q++ = 0;\r
8803         if(*p) ChatPopUp(p);\r
8804       } while(p=q);\r
8805     }\r
8806 \r
8807     SetActiveWindow(hwndMain);\r
8808 }\r
8809 \r
8810 void\r
8811 DrawPosition(int fullRedraw, Board board)\r
8812 {\r
8813   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8814 }\r
8815 \r
8816 void NotifyFrontendLogin()\r
8817 {\r
8818         if (hwndConsole)\r
8819                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8820 }\r
8821 \r
8822 VOID\r
8823 ResetFrontEnd()\r
8824 {\r
8825   fromX = fromY = -1;\r
8826   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8827     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8828     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8829     dragInfo.lastpos = dragInfo.pos;\r
8830     dragInfo.start.x = dragInfo.start.y = -1;\r
8831     dragInfo.from = dragInfo.start;\r
8832     ReleaseCapture();\r
8833     DrawPosition(TRUE, NULL);\r
8834   }\r
8835   TagsPopDown();\r
8836 }\r
8837 \r
8838 \r
8839 VOID\r
8840 CommentPopUp(char *title, char *str)\r
8841 {\r
8842   HWND hwnd = GetActiveWindow();\r
8843   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8844   SAY(str);\r
8845   SetActiveWindow(hwnd);\r
8846 }\r
8847 \r
8848 VOID\r
8849 CommentPopDown(void)\r
8850 {\r
8851   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8852   if (commentDialog) {\r
8853     ShowWindow(commentDialog, SW_HIDE);\r
8854   }\r
8855   commentUp = FALSE;\r
8856 }\r
8857 \r
8858 VOID\r
8859 EditCommentPopUp(int index, char *title, char *str)\r
8860 {\r
8861   EitherCommentPopUp(index, title, str, TRUE);\r
8862 }\r
8863 \r
8864 \r
8865 int\r
8866 Roar()\r
8867 {\r
8868   MyPlaySound(&sounds[(int)SoundRoar]);\r
8869   return 1;\r
8870 }\r
8871 \r
8872 VOID\r
8873 RingBell()\r
8874 {\r
8875   MyPlaySound(&sounds[(int)SoundMove]);\r
8876 }\r
8877 \r
8878 VOID PlayIcsWinSound()\r
8879 {\r
8880   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8881 }\r
8882 \r
8883 VOID PlayIcsLossSound()\r
8884 {\r
8885   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8886 }\r
8887 \r
8888 VOID PlayIcsDrawSound()\r
8889 {\r
8890   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8891 }\r
8892 \r
8893 VOID PlayIcsUnfinishedSound()\r
8894 {\r
8895   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8896 }\r
8897 \r
8898 VOID\r
8899 PlayAlarmSound()\r
8900 {\r
8901   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8902 }\r
8903 \r
8904 VOID\r
8905 PlayTellSound()\r
8906 {\r
8907   MyPlaySound(&textAttribs[ColorTell].sound);\r
8908 }\r
8909 \r
8910 \r
8911 VOID\r
8912 EchoOn()\r
8913 {\r
8914   HWND hInput;\r
8915   consoleEcho = TRUE;\r
8916   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8917   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8918   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8919 }\r
8920 \r
8921 \r
8922 VOID\r
8923 EchoOff()\r
8924 {\r
8925   CHARFORMAT cf;\r
8926   HWND hInput;\r
8927   consoleEcho = FALSE;\r
8928   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8929   /* This works OK: set text and background both to the same color */\r
8930   cf = consoleCF;\r
8931   cf.crTextColor = COLOR_ECHOOFF;\r
8932   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8933   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8934 }\r
8935 \r
8936 /* No Raw()...? */\r
8937 \r
8938 void Colorize(ColorClass cc, int continuation)\r
8939 {\r
8940   currentColorClass = cc;\r
8941   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8942   consoleCF.crTextColor = textAttribs[cc].color;\r
8943   consoleCF.dwEffects = textAttribs[cc].effects;\r
8944   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8945 }\r
8946 \r
8947 char *\r
8948 UserName()\r
8949 {\r
8950   static char buf[MSG_SIZ];\r
8951   DWORD bufsiz = MSG_SIZ;\r
8952 \r
8953   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8954         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8955   }\r
8956   if (!GetUserName(buf, &bufsiz)) {\r
8957     /*DisplayError("Error getting user name", GetLastError());*/\r
8958     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8959   }\r
8960   return buf;\r
8961 }\r
8962 \r
8963 char *\r
8964 HostName()\r
8965 {\r
8966   static char buf[MSG_SIZ];\r
8967   DWORD bufsiz = MSG_SIZ;\r
8968 \r
8969   if (!GetComputerName(buf, &bufsiz)) {\r
8970     /*DisplayError("Error getting host name", GetLastError());*/\r
8971     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8972   }\r
8973   return buf;\r
8974 }\r
8975 \r
8976 \r
8977 int\r
8978 ClockTimerRunning()\r
8979 {\r
8980   return clockTimerEvent != 0;\r
8981 }\r
8982 \r
8983 int\r
8984 StopClockTimer()\r
8985 {\r
8986   if (clockTimerEvent == 0) return FALSE;\r
8987   KillTimer(hwndMain, clockTimerEvent);\r
8988   clockTimerEvent = 0;\r
8989   return TRUE;\r
8990 }\r
8991 \r
8992 void\r
8993 StartClockTimer(long millisec)\r
8994 {\r
8995   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8996                              (UINT) millisec, NULL);\r
8997 }\r
8998 \r
8999 void\r
9000 DisplayWhiteClock(long timeRemaining, int highlight)\r
9001 {\r
9002   HDC hdc;\r
9003   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9004 \r
9005   if(appData.noGUI) return;\r
9006   hdc = GetDC(hwndMain);\r
9007   if (!IsIconic(hwndMain)) {\r
9008     DisplayAClock(hdc, timeRemaining, highlight, \r
9009                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
9010   }\r
9011   if (highlight && iconCurrent == iconBlack) {\r
9012     iconCurrent = iconWhite;\r
9013     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9014     if (IsIconic(hwndMain)) {\r
9015       DrawIcon(hdc, 2, 2, iconCurrent);\r
9016     }\r
9017   }\r
9018   (void) ReleaseDC(hwndMain, hdc);\r
9019   if (hwndConsole)\r
9020     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9021 }\r
9022 \r
9023 void\r
9024 DisplayBlackClock(long timeRemaining, int highlight)\r
9025 {\r
9026   HDC hdc;\r
9027   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9028 \r
9029 \r
9030   if(appData.noGUI) return;\r
9031   hdc = GetDC(hwndMain);\r
9032   if (!IsIconic(hwndMain)) {\r
9033     DisplayAClock(hdc, timeRemaining, highlight, \r
9034                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
9035   }\r
9036   if (highlight && iconCurrent == iconWhite) {\r
9037     iconCurrent = iconBlack;\r
9038     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9039     if (IsIconic(hwndMain)) {\r
9040       DrawIcon(hdc, 2, 2, iconCurrent);\r
9041     }\r
9042   }\r
9043   (void) ReleaseDC(hwndMain, hdc);\r
9044   if (hwndConsole)\r
9045     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9046 }\r
9047 \r
9048 \r
9049 int\r
9050 LoadGameTimerRunning()\r
9051 {\r
9052   return loadGameTimerEvent != 0;\r
9053 }\r
9054 \r
9055 int\r
9056 StopLoadGameTimer()\r
9057 {\r
9058   if (loadGameTimerEvent == 0) return FALSE;\r
9059   KillTimer(hwndMain, loadGameTimerEvent);\r
9060   loadGameTimerEvent = 0;\r
9061   return TRUE;\r
9062 }\r
9063 \r
9064 void\r
9065 StartLoadGameTimer(long millisec)\r
9066 {\r
9067   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9068                                 (UINT) millisec, NULL);\r
9069 }\r
9070 \r
9071 void\r
9072 AutoSaveGame()\r
9073 {\r
9074   char *defName;\r
9075   FILE *f;\r
9076   char fileTitle[MSG_SIZ];\r
9077 \r
9078   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9079   f = OpenFileDialog(hwndMain, "a", defName,\r
9080                      appData.oldSaveStyle ? "gam" : "pgn",\r
9081                      GAME_FILT, \r
9082                      _("Save Game to File"), NULL, fileTitle, NULL);\r
9083   if (f != NULL) {\r
9084     SaveGame(f, 0, "");\r
9085     fclose(f);\r
9086   }\r
9087 }\r
9088 \r
9089 \r
9090 void\r
9091 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9092 {\r
9093   if (delayedTimerEvent != 0) {\r
9094     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9095       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9096     }\r
9097     KillTimer(hwndMain, delayedTimerEvent);\r
9098     delayedTimerEvent = 0;\r
9099     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9100     delayedTimerCallback();\r
9101   }\r
9102   delayedTimerCallback = cb;\r
9103   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9104                                 (UINT) millisec, NULL);\r
9105 }\r
9106 \r
9107 DelayedEventCallback\r
9108 GetDelayedEvent()\r
9109 {\r
9110   if (delayedTimerEvent) {\r
9111     return delayedTimerCallback;\r
9112   } else {\r
9113     return NULL;\r
9114   }\r
9115 }\r
9116 \r
9117 void\r
9118 CancelDelayedEvent()\r
9119 {\r
9120   if (delayedTimerEvent) {\r
9121     KillTimer(hwndMain, delayedTimerEvent);\r
9122     delayedTimerEvent = 0;\r
9123   }\r
9124 }\r
9125 \r
9126 DWORD GetWin32Priority(int nice)\r
9127 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9128 /*\r
9129 REALTIME_PRIORITY_CLASS     0x00000100\r
9130 HIGH_PRIORITY_CLASS         0x00000080\r
9131 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9132 NORMAL_PRIORITY_CLASS       0x00000020\r
9133 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9134 IDLE_PRIORITY_CLASS         0x00000040\r
9135 */\r
9136         if (nice < -15) return 0x00000080;\r
9137         if (nice < 0)   return 0x00008000;\r
9138         if (nice == 0)  return 0x00000020;\r
9139         if (nice < 15)  return 0x00004000;\r
9140         return 0x00000040;\r
9141 }\r
9142 \r
9143 void RunCommand(char *cmdLine)\r
9144 {\r
9145   /* Now create the child process. */\r
9146   STARTUPINFO siStartInfo;\r
9147   PROCESS_INFORMATION piProcInfo;\r
9148 \r
9149   siStartInfo.cb = sizeof(STARTUPINFO);\r
9150   siStartInfo.lpReserved = NULL;\r
9151   siStartInfo.lpDesktop = NULL;\r
9152   siStartInfo.lpTitle = NULL;\r
9153   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9154   siStartInfo.cbReserved2 = 0;\r
9155   siStartInfo.lpReserved2 = NULL;\r
9156   siStartInfo.hStdInput = NULL;\r
9157   siStartInfo.hStdOutput = NULL;\r
9158   siStartInfo.hStdError = NULL;\r
9159 \r
9160   CreateProcess(NULL,\r
9161                 cmdLine,           /* command line */\r
9162                 NULL,      /* process security attributes */\r
9163                 NULL,      /* primary thread security attrs */\r
9164                 TRUE,      /* handles are inherited */\r
9165                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9166                 NULL,      /* use parent's environment */\r
9167                 NULL,\r
9168                 &siStartInfo, /* STARTUPINFO pointer */\r
9169                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9170 \r
9171   CloseHandle(piProcInfo.hThread);\r
9172 }\r
9173 \r
9174 /* Start a child process running the given program.\r
9175    The process's standard output can be read from "from", and its\r
9176    standard input can be written to "to".\r
9177    Exit with fatal error if anything goes wrong.\r
9178    Returns an opaque pointer that can be used to destroy the process\r
9179    later.\r
9180 */\r
9181 int\r
9182 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9183 {\r
9184 #define BUFSIZE 4096\r
9185 \r
9186   HANDLE hChildStdinRd, hChildStdinWr,\r
9187     hChildStdoutRd, hChildStdoutWr;\r
9188   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9189   SECURITY_ATTRIBUTES saAttr;\r
9190   BOOL fSuccess;\r
9191   PROCESS_INFORMATION piProcInfo;\r
9192   STARTUPINFO siStartInfo;\r
9193   ChildProc *cp;\r
9194   char buf[MSG_SIZ];\r
9195   DWORD err;\r
9196 \r
9197   if (appData.debugMode) {\r
9198     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9199   }\r
9200 \r
9201   *pr = NoProc;\r
9202 \r
9203   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9204   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9205   saAttr.bInheritHandle = TRUE;\r
9206   saAttr.lpSecurityDescriptor = NULL;\r
9207 \r
9208   /*\r
9209    * The steps for redirecting child's STDOUT:\r
9210    *     1. Create anonymous pipe to be STDOUT for child.\r
9211    *     2. Create a noninheritable duplicate of read handle,\r
9212    *         and close the inheritable read handle.\r
9213    */\r
9214 \r
9215   /* Create a pipe for the child's STDOUT. */\r
9216   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9217     return GetLastError();\r
9218   }\r
9219 \r
9220   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9221   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9222                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9223                              FALSE,     /* not inherited */\r
9224                              DUPLICATE_SAME_ACCESS);\r
9225   if (! fSuccess) {\r
9226     return GetLastError();\r
9227   }\r
9228   CloseHandle(hChildStdoutRd);\r
9229 \r
9230   /*\r
9231    * The steps for redirecting child's STDIN:\r
9232    *     1. Create anonymous pipe to be STDIN for child.\r
9233    *     2. Create a noninheritable duplicate of write handle,\r
9234    *         and close the inheritable write handle.\r
9235    */\r
9236 \r
9237   /* Create a pipe for the child's STDIN. */\r
9238   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9239     return GetLastError();\r
9240   }\r
9241 \r
9242   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9243   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9244                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9245                              FALSE,     /* not inherited */\r
9246                              DUPLICATE_SAME_ACCESS);\r
9247   if (! fSuccess) {\r
9248     return GetLastError();\r
9249   }\r
9250   CloseHandle(hChildStdinWr);\r
9251 \r
9252   /* Arrange to (1) look in dir for the child .exe file, and\r
9253    * (2) have dir be the child's working directory.  Interpret\r
9254    * dir relative to the directory WinBoard loaded from. */\r
9255   GetCurrentDirectory(MSG_SIZ, buf);\r
9256   SetCurrentDirectory(installDir);\r
9257   SetCurrentDirectory(dir);\r
9258 \r
9259   /* Now create the child process. */\r
9260 \r
9261   siStartInfo.cb = sizeof(STARTUPINFO);\r
9262   siStartInfo.lpReserved = NULL;\r
9263   siStartInfo.lpDesktop = NULL;\r
9264   siStartInfo.lpTitle = NULL;\r
9265   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9266   siStartInfo.cbReserved2 = 0;\r
9267   siStartInfo.lpReserved2 = NULL;\r
9268   siStartInfo.hStdInput = hChildStdinRd;\r
9269   siStartInfo.hStdOutput = hChildStdoutWr;\r
9270   siStartInfo.hStdError = hChildStdoutWr;\r
9271 \r
9272   fSuccess = CreateProcess(NULL,\r
9273                            cmdLine,        /* command line */\r
9274                            NULL,           /* process security attributes */\r
9275                            NULL,           /* primary thread security attrs */\r
9276                            TRUE,           /* handles are inherited */\r
9277                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9278                            NULL,           /* use parent's environment */\r
9279                            NULL,\r
9280                            &siStartInfo, /* STARTUPINFO pointer */\r
9281                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9282 \r
9283   err = GetLastError();\r
9284   SetCurrentDirectory(buf); /* return to prev directory */\r
9285   if (! fSuccess) {\r
9286     return err;\r
9287   }\r
9288 \r
9289   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9290     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9291     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9292   }\r
9293 \r
9294   /* Close the handles we don't need in the parent */\r
9295   CloseHandle(piProcInfo.hThread);\r
9296   CloseHandle(hChildStdinRd);\r
9297   CloseHandle(hChildStdoutWr);\r
9298 \r
9299   /* Prepare return value */\r
9300   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9301   cp->kind = CPReal;\r
9302   cp->hProcess = piProcInfo.hProcess;\r
9303   cp->pid = piProcInfo.dwProcessId;\r
9304   cp->hFrom = hChildStdoutRdDup;\r
9305   cp->hTo = hChildStdinWrDup;\r
9306 \r
9307   *pr = (void *) cp;\r
9308 \r
9309   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9310      2000 where engines sometimes don't see the initial command(s)\r
9311      from WinBoard and hang.  I don't understand how that can happen,\r
9312      but the Sleep is harmless, so I've put it in.  Others have also\r
9313      reported what may be the same problem, so hopefully this will fix\r
9314      it for them too.  */\r
9315   Sleep(500);\r
9316 \r
9317   return NO_ERROR;\r
9318 }\r
9319 \r
9320 \r
9321 void\r
9322 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9323 {\r
9324   ChildProc *cp; int result;\r
9325 \r
9326   cp = (ChildProc *) pr;\r
9327   if (cp == NULL) return;\r
9328 \r
9329   switch (cp->kind) {\r
9330   case CPReal:\r
9331     /* TerminateProcess is considered harmful, so... */\r
9332     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9333     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9334     /* The following doesn't work because the chess program\r
9335        doesn't "have the same console" as WinBoard.  Maybe\r
9336        we could arrange for this even though neither WinBoard\r
9337        nor the chess program uses a console for stdio? */\r
9338     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9339 \r
9340     /* [AS] Special termination modes for misbehaving programs... */\r
9341     if( signal & 8 ) { \r
9342         result = TerminateProcess( cp->hProcess, 0 );\r
9343 \r
9344         if ( appData.debugMode) {\r
9345             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9346         }\r
9347     }\r
9348     else if( signal & 4 ) {\r
9349         DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most\r
9350 \r
9351         if( dw != WAIT_OBJECT_0 ) {\r
9352             result = TerminateProcess( cp->hProcess, 0 );\r
9353 \r
9354             if ( appData.debugMode) {\r
9355                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9356             }\r
9357 \r
9358         }\r
9359     }\r
9360 \r
9361     CloseHandle(cp->hProcess);\r
9362     break;\r
9363 \r
9364   case CPComm:\r
9365     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9366     break;\r
9367 \r
9368   case CPSock:\r
9369     closesocket(cp->sock);\r
9370     WSACleanup();\r
9371     break;\r
9372 \r
9373   case CPRcmd:\r
9374     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9375     closesocket(cp->sock);\r
9376     closesocket(cp->sock2);\r
9377     WSACleanup();\r
9378     break;\r
9379   }\r
9380   free(cp);\r
9381 }\r
9382 \r
9383 void\r
9384 InterruptChildProcess(ProcRef pr)\r
9385 {\r
9386   ChildProc *cp;\r
9387 \r
9388   cp = (ChildProc *) pr;\r
9389   if (cp == NULL) return;\r
9390   switch (cp->kind) {\r
9391   case CPReal:\r
9392     /* The following doesn't work because the chess program\r
9393        doesn't "have the same console" as WinBoard.  Maybe\r
9394        we could arrange for this even though neither WinBoard\r
9395        nor the chess program uses a console for stdio */\r
9396     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9397     break;\r
9398 \r
9399   case CPComm:\r
9400   case CPSock:\r
9401     /* Can't interrupt */\r
9402     break;\r
9403 \r
9404   case CPRcmd:\r
9405     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9406     break;\r
9407   }\r
9408 }\r
9409 \r
9410 \r
9411 int\r
9412 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9413 {\r
9414   char cmdLine[MSG_SIZ];\r
9415 \r
9416   if (port[0] == NULLCHAR) {\r
9417     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9418   } else {\r
9419     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9420   }\r
9421   return StartChildProcess(cmdLine, "", pr);\r
9422 }\r
9423 \r
9424 \r
9425 /* Code to open TCP sockets */\r
9426 \r
9427 int\r
9428 OpenTCP(char *host, char *port, ProcRef *pr)\r
9429 {\r
9430   ChildProc *cp;\r
9431   int err;\r
9432   SOCKET s;\r
9433 \r
9434   struct sockaddr_in sa, mysa;\r
9435   struct hostent FAR *hp;\r
9436   unsigned short uport;\r
9437   WORD wVersionRequested;\r
9438   WSADATA wsaData;\r
9439 \r
9440   /* Initialize socket DLL */\r
9441   wVersionRequested = MAKEWORD(1, 1);\r
9442   err = WSAStartup(wVersionRequested, &wsaData);\r
9443   if (err != 0) return err;\r
9444 \r
9445   /* Make socket */\r
9446   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9447     err = WSAGetLastError();\r
9448     WSACleanup();\r
9449     return err;\r
9450   }\r
9451 \r
9452   /* Bind local address using (mostly) don't-care values.\r
9453    */\r
9454   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9455   mysa.sin_family = AF_INET;\r
9456   mysa.sin_addr.s_addr = INADDR_ANY;\r
9457   uport = (unsigned short) 0;\r
9458   mysa.sin_port = htons(uport);\r
9459   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9460       == SOCKET_ERROR) {\r
9461     err = WSAGetLastError();\r
9462     WSACleanup();\r
9463     return err;\r
9464   }\r
9465 \r
9466   /* Resolve remote host name */\r
9467   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9468   if (!(hp = gethostbyname(host))) {\r
9469     unsigned int b0, b1, b2, b3;\r
9470 \r
9471     err = WSAGetLastError();\r
9472 \r
9473     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9474       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9475       hp->h_addrtype = AF_INET;\r
9476       hp->h_length = 4;\r
9477       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9478       hp->h_addr_list[0] = (char *) malloc(4);\r
9479       hp->h_addr_list[0][0] = (char) b0;\r
9480       hp->h_addr_list[0][1] = (char) b1;\r
9481       hp->h_addr_list[0][2] = (char) b2;\r
9482       hp->h_addr_list[0][3] = (char) b3;\r
9483     } else {\r
9484       WSACleanup();\r
9485       return err;\r
9486     }\r
9487   }\r
9488   sa.sin_family = hp->h_addrtype;\r
9489   uport = (unsigned short) atoi(port);\r
9490   sa.sin_port = htons(uport);\r
9491   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9492 \r
9493   /* Make connection */\r
9494   if (connect(s, (struct sockaddr *) &sa,\r
9495               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9496     err = WSAGetLastError();\r
9497     WSACleanup();\r
9498     return err;\r
9499   }\r
9500 \r
9501   /* Prepare return value */\r
9502   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9503   cp->kind = CPSock;\r
9504   cp->sock = s;\r
9505   *pr = (ProcRef *) cp;\r
9506 \r
9507   return NO_ERROR;\r
9508 }\r
9509 \r
9510 int\r
9511 OpenCommPort(char *name, ProcRef *pr)\r
9512 {\r
9513   HANDLE h;\r
9514   COMMTIMEOUTS ct;\r
9515   ChildProc *cp;\r
9516   char fullname[MSG_SIZ];\r
9517 \r
9518   if (*name != '\\')\r
9519     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9520   else\r
9521     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9522 \r
9523   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9524                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9525   if (h == (HANDLE) -1) {\r
9526     return GetLastError();\r
9527   }\r
9528   hCommPort = h;\r
9529 \r
9530   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9531 \r
9532   /* Accumulate characters until a 100ms pause, then parse */\r
9533   ct.ReadIntervalTimeout = 100;\r
9534   ct.ReadTotalTimeoutMultiplier = 0;\r
9535   ct.ReadTotalTimeoutConstant = 0;\r
9536   ct.WriteTotalTimeoutMultiplier = 0;\r
9537   ct.WriteTotalTimeoutConstant = 0;\r
9538   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9539 \r
9540   /* Prepare return value */\r
9541   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9542   cp->kind = CPComm;\r
9543   cp->hFrom = h;\r
9544   cp->hTo = h;\r
9545   *pr = (ProcRef *) cp;\r
9546 \r
9547   return NO_ERROR;\r
9548 }\r
9549 \r
9550 int\r
9551 OpenLoopback(ProcRef *pr)\r
9552 {\r
9553   DisplayFatalError(_("Not implemented"), 0, 1);\r
9554   return NO_ERROR;\r
9555 }\r
9556 \r
9557 \r
9558 int\r
9559 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9560 {\r
9561   ChildProc *cp;\r
9562   int err;\r
9563   SOCKET s, s2, s3;\r
9564   struct sockaddr_in sa, mysa;\r
9565   struct hostent FAR *hp;\r
9566   unsigned short uport;\r
9567   WORD wVersionRequested;\r
9568   WSADATA wsaData;\r
9569   int fromPort;\r
9570   char stderrPortStr[MSG_SIZ];\r
9571 \r
9572   /* Initialize socket DLL */\r
9573   wVersionRequested = MAKEWORD(1, 1);\r
9574   err = WSAStartup(wVersionRequested, &wsaData);\r
9575   if (err != 0) return err;\r
9576 \r
9577   /* Resolve remote host name */\r
9578   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9579   if (!(hp = gethostbyname(host))) {\r
9580     unsigned int b0, b1, b2, b3;\r
9581 \r
9582     err = WSAGetLastError();\r
9583 \r
9584     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9585       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9586       hp->h_addrtype = AF_INET;\r
9587       hp->h_length = 4;\r
9588       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9589       hp->h_addr_list[0] = (char *) malloc(4);\r
9590       hp->h_addr_list[0][0] = (char) b0;\r
9591       hp->h_addr_list[0][1] = (char) b1;\r
9592       hp->h_addr_list[0][2] = (char) b2;\r
9593       hp->h_addr_list[0][3] = (char) b3;\r
9594     } else {\r
9595       WSACleanup();\r
9596       return err;\r
9597     }\r
9598   }\r
9599   sa.sin_family = hp->h_addrtype;\r
9600   uport = (unsigned short) 514;\r
9601   sa.sin_port = htons(uport);\r
9602   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9603 \r
9604   /* Bind local socket to unused "privileged" port address\r
9605    */\r
9606   s = INVALID_SOCKET;\r
9607   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9608   mysa.sin_family = AF_INET;\r
9609   mysa.sin_addr.s_addr = INADDR_ANY;\r
9610   for (fromPort = 1023;; fromPort--) {\r
9611     if (fromPort < 0) {\r
9612       WSACleanup();\r
9613       return WSAEADDRINUSE;\r
9614     }\r
9615     if (s == INVALID_SOCKET) {\r
9616       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9617         err = WSAGetLastError();\r
9618         WSACleanup();\r
9619         return err;\r
9620       }\r
9621     }\r
9622     uport = (unsigned short) fromPort;\r
9623     mysa.sin_port = htons(uport);\r
9624     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9625         == SOCKET_ERROR) {\r
9626       err = WSAGetLastError();\r
9627       if (err == WSAEADDRINUSE) continue;\r
9628       WSACleanup();\r
9629       return err;\r
9630     }\r
9631     if (connect(s, (struct sockaddr *) &sa,\r
9632       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9633       err = WSAGetLastError();\r
9634       if (err == WSAEADDRINUSE) {\r
9635         closesocket(s);\r
9636         s = -1;\r
9637         continue;\r
9638       }\r
9639       WSACleanup();\r
9640       return err;\r
9641     }\r
9642     break;\r
9643   }\r
9644 \r
9645   /* Bind stderr local socket to unused "privileged" port address\r
9646    */\r
9647   s2 = INVALID_SOCKET;\r
9648   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9649   mysa.sin_family = AF_INET;\r
9650   mysa.sin_addr.s_addr = INADDR_ANY;\r
9651   for (fromPort = 1023;; fromPort--) {\r
9652     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9653     if (fromPort < 0) {\r
9654       (void) closesocket(s);\r
9655       WSACleanup();\r
9656       return WSAEADDRINUSE;\r
9657     }\r
9658     if (s2 == INVALID_SOCKET) {\r
9659       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9660         err = WSAGetLastError();\r
9661         closesocket(s);\r
9662         WSACleanup();\r
9663         return err;\r
9664       }\r
9665     }\r
9666     uport = (unsigned short) fromPort;\r
9667     mysa.sin_port = htons(uport);\r
9668     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9669         == SOCKET_ERROR) {\r
9670       err = WSAGetLastError();\r
9671       if (err == WSAEADDRINUSE) continue;\r
9672       (void) closesocket(s);\r
9673       WSACleanup();\r
9674       return err;\r
9675     }\r
9676     if (listen(s2, 1) == SOCKET_ERROR) {\r
9677       err = WSAGetLastError();\r
9678       if (err == WSAEADDRINUSE) {\r
9679         closesocket(s2);\r
9680         s2 = INVALID_SOCKET;\r
9681         continue;\r
9682       }\r
9683       (void) closesocket(s);\r
9684       (void) closesocket(s2);\r
9685       WSACleanup();\r
9686       return err;\r
9687     }\r
9688     break;\r
9689   }\r
9690   prevStderrPort = fromPort; // remember port used\r
9691   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9692 \r
9693   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9694     err = WSAGetLastError();\r
9695     (void) closesocket(s);\r
9696     (void) closesocket(s2);\r
9697     WSACleanup();\r
9698     return err;\r
9699   }\r
9700 \r
9701   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9702     err = WSAGetLastError();\r
9703     (void) closesocket(s);\r
9704     (void) closesocket(s2);\r
9705     WSACleanup();\r
9706     return err;\r
9707   }\r
9708   if (*user == NULLCHAR) user = UserName();\r
9709   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9710     err = WSAGetLastError();\r
9711     (void) closesocket(s);\r
9712     (void) closesocket(s2);\r
9713     WSACleanup();\r
9714     return err;\r
9715   }\r
9716   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9717     err = WSAGetLastError();\r
9718     (void) closesocket(s);\r
9719     (void) closesocket(s2);\r
9720     WSACleanup();\r
9721     return err;\r
9722   }\r
9723 \r
9724   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9725     err = WSAGetLastError();\r
9726     (void) closesocket(s);\r
9727     (void) closesocket(s2);\r
9728     WSACleanup();\r
9729     return err;\r
9730   }\r
9731   (void) closesocket(s2);  /* Stop listening */\r
9732 \r
9733   /* Prepare return value */\r
9734   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9735   cp->kind = CPRcmd;\r
9736   cp->sock = s;\r
9737   cp->sock2 = s3;\r
9738   *pr = (ProcRef *) cp;\r
9739 \r
9740   return NO_ERROR;\r
9741 }\r
9742 \r
9743 \r
9744 InputSourceRef\r
9745 AddInputSource(ProcRef pr, int lineByLine,\r
9746                InputCallback func, VOIDSTAR closure)\r
9747 {\r
9748   InputSource *is, *is2 = NULL;\r
9749   ChildProc *cp = (ChildProc *) pr;\r
9750 \r
9751   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9752   is->lineByLine = lineByLine;\r
9753   is->func = func;\r
9754   is->closure = closure;\r
9755   is->second = NULL;\r
9756   is->next = is->buf;\r
9757   if (pr == NoProc) {\r
9758     is->kind = CPReal;\r
9759     consoleInputSource = is;\r
9760   } else {\r
9761     is->kind = cp->kind;\r
9762     /* \r
9763         [AS] Try to avoid a race condition if the thread is given control too early:\r
9764         we create all threads suspended so that the is->hThread variable can be\r
9765         safely assigned, then let the threads start with ResumeThread.\r
9766     */\r
9767     switch (cp->kind) {\r
9768     case CPReal:\r
9769       is->hFile = cp->hFrom;\r
9770       cp->hFrom = NULL; /* now owned by InputThread */\r
9771       is->hThread =\r
9772         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9773                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9774       break;\r
9775 \r
9776     case CPComm:\r
9777       is->hFile = cp->hFrom;\r
9778       cp->hFrom = NULL; /* now owned by InputThread */\r
9779       is->hThread =\r
9780         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9781                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9782       break;\r
9783 \r
9784     case CPSock:\r
9785       is->sock = cp->sock;\r
9786       is->hThread =\r
9787         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9788                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9789       break;\r
9790 \r
9791     case CPRcmd:\r
9792       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9793       *is2 = *is;\r
9794       is->sock = cp->sock;\r
9795       is->second = is2;\r
9796       is2->sock = cp->sock2;\r
9797       is2->second = is2;\r
9798       is->hThread =\r
9799         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9800                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9801       is2->hThread =\r
9802         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9803                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9804       break;\r
9805     }\r
9806 \r
9807     if( is->hThread != NULL ) {\r
9808         ResumeThread( is->hThread );\r
9809     }\r
9810 \r
9811     if( is2 != NULL && is2->hThread != NULL ) {\r
9812         ResumeThread( is2->hThread );\r
9813     }\r
9814   }\r
9815 \r
9816   return (InputSourceRef) is;\r
9817 }\r
9818 \r
9819 void\r
9820 RemoveInputSource(InputSourceRef isr)\r
9821 {\r
9822   InputSource *is;\r
9823 \r
9824   is = (InputSource *) isr;\r
9825   is->hThread = NULL;  /* tell thread to stop */\r
9826   CloseHandle(is->hThread);\r
9827   if (is->second != NULL) {\r
9828     is->second->hThread = NULL;\r
9829     CloseHandle(is->second->hThread);\r
9830   }\r
9831 }\r
9832 \r
9833 int no_wrap(char *message, int count)\r
9834 {\r
9835     ConsoleOutput(message, count, FALSE);\r
9836     return count;\r
9837 }\r
9838 \r
9839 int\r
9840 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9841 {\r
9842   DWORD dOutCount;\r
9843   int outCount = SOCKET_ERROR;\r
9844   ChildProc *cp = (ChildProc *) pr;\r
9845   static OVERLAPPED ovl;\r
9846 \r
9847   static int line = 0;\r
9848 \r
9849   if (pr == NoProc)\r
9850   {\r
9851     if (appData.noJoin || !appData.useInternalWrap)\r
9852       return no_wrap(message, count);\r
9853     else\r
9854     {\r
9855       int width = get_term_width();\r
9856       int len = wrap(NULL, message, count, width, &line);\r
9857       char *msg = malloc(len);\r
9858       int dbgchk;\r
9859 \r
9860       if (!msg)\r
9861         return no_wrap(message, count);\r
9862       else\r
9863       {\r
9864         dbgchk = wrap(msg, message, count, width, &line);\r
9865         if (dbgchk != len && appData.debugMode)\r
9866             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9867         ConsoleOutput(msg, len, FALSE);\r
9868         free(msg);\r
9869         return len;\r
9870       }\r
9871     }\r
9872   }\r
9873 \r
9874   if (ovl.hEvent == NULL) {\r
9875     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9876   }\r
9877   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9878 \r
9879   switch (cp->kind) {\r
9880   case CPSock:\r
9881   case CPRcmd:\r
9882     outCount = send(cp->sock, message, count, 0);\r
9883     if (outCount == SOCKET_ERROR) {\r
9884       *outError = WSAGetLastError();\r
9885     } else {\r
9886       *outError = NO_ERROR;\r
9887     }\r
9888     break;\r
9889 \r
9890   case CPReal:\r
9891     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9892                   &dOutCount, NULL)) {\r
9893       *outError = NO_ERROR;\r
9894       outCount = (int) dOutCount;\r
9895     } else {\r
9896       *outError = GetLastError();\r
9897     }\r
9898     break;\r
9899 \r
9900   case CPComm:\r
9901     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9902                             &dOutCount, &ovl);\r
9903     if (*outError == NO_ERROR) {\r
9904       outCount = (int) dOutCount;\r
9905     }\r
9906     break;\r
9907   }\r
9908   return outCount;\r
9909 }\r
9910 \r
9911 void\r
9912 DoSleep(int n)\r
9913 {\r
9914     if(n != 0) Sleep(n);\r
9915 }\r
9916 \r
9917 int\r
9918 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9919                        long msdelay)\r
9920 {\r
9921   /* Ignore delay, not implemented for WinBoard */\r
9922   return OutputToProcess(pr, message, count, outError);\r
9923 }\r
9924 \r
9925 \r
9926 void\r
9927 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9928                         char *buf, int count, int error)\r
9929 {\r
9930   DisplayFatalError(_("Not implemented"), 0, 1);\r
9931 }\r
9932 \r
9933 /* see wgamelist.c for Game List functions */\r
9934 /* see wedittags.c for Edit Tags functions */\r
9935 \r
9936 \r
9937 int\r
9938 ICSInitScript()\r
9939 {\r
9940   FILE *f;\r
9941   char buf[MSG_SIZ];\r
9942   char *dummy;\r
9943 \r
9944   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9945     f = fopen(buf, "r");\r
9946     if (f != NULL) {\r
9947       ProcessICSInitScript(f);\r
9948       fclose(f);\r
9949       return TRUE;\r
9950     }\r
9951   }\r
9952   return FALSE;\r
9953 }\r
9954 \r
9955 \r
9956 VOID\r
9957 StartAnalysisClock()\r
9958 {\r
9959   if (analysisTimerEvent) return;\r
9960   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9961                                         (UINT) 2000, NULL);\r
9962 }\r
9963 \r
9964 VOID\r
9965 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9966 {\r
9967   highlightInfo.sq[0].x = fromX;\r
9968   highlightInfo.sq[0].y = fromY;\r
9969   highlightInfo.sq[1].x = toX;\r
9970   highlightInfo.sq[1].y = toY;\r
9971 }\r
9972 \r
9973 VOID\r
9974 ClearHighlights()\r
9975 {\r
9976   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9977     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9978 }\r
9979 \r
9980 VOID\r
9981 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9982 {\r
9983   premoveHighlightInfo.sq[0].x = fromX;\r
9984   premoveHighlightInfo.sq[0].y = fromY;\r
9985   premoveHighlightInfo.sq[1].x = toX;\r
9986   premoveHighlightInfo.sq[1].y = toY;\r
9987 }\r
9988 \r
9989 VOID\r
9990 ClearPremoveHighlights()\r
9991 {\r
9992   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9993     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9994 }\r
9995 \r
9996 VOID\r
9997 ShutDownFrontEnd()\r
9998 {\r
9999   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10000   DeleteClipboardTempFiles();\r
10001 }\r
10002 \r
10003 void\r
10004 BoardToTop()\r
10005 {\r
10006     if (IsIconic(hwndMain))\r
10007       ShowWindow(hwndMain, SW_RESTORE);\r
10008 \r
10009     SetActiveWindow(hwndMain);\r
10010 }\r
10011 \r
10012 /*\r
10013  * Prototypes for animation support routines\r
10014  */\r
10015 static void ScreenSquare(int column, int row, POINT * pt);\r
10016 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10017      POINT frames[], int * nFrames);\r
10018 \r
10019 \r
10020 #define kFactor 4\r
10021 \r
10022 void\r
10023 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
10024 {       // [HGM] atomic: animate blast wave\r
10025         int i;\r
10026 \r
10027         explodeInfo.fromX = fromX;\r
10028         explodeInfo.fromY = fromY;\r
10029         explodeInfo.toX = toX;\r
10030         explodeInfo.toY = toY;\r
10031         for(i=1; i<4*kFactor; i++) {\r
10032             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
10033             DrawPosition(FALSE, board);\r
10034             Sleep(appData.animSpeed);\r
10035         }\r
10036         explodeInfo.radius = 0;\r
10037         DrawPosition(TRUE, board);\r
10038 }\r
10039 \r
10040 void\r
10041 AnimateMove(board, fromX, fromY, toX, toY)\r
10042      Board board;\r
10043      int fromX;\r
10044      int fromY;\r
10045      int toX;\r
10046      int toY;\r
10047 {\r
10048   ChessSquare piece;\r
10049   int x = toX, y = toY;\r
10050   POINT start, finish, mid;\r
10051   POINT frames[kFactor * 2 + 1];\r
10052   int nFrames, n;\r
10053 \r
10054   if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();\r
10055 \r
10056   if (!appData.animate) return;\r
10057   if (doingSizing) return;\r
10058   if (fromY < 0 || fromX < 0) return;\r
10059   piece = board[fromY][fromX];\r
10060   if (piece >= EmptySquare) return;\r
10061 \r
10062   if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square\r
10063 \r
10064 again:\r
10065 \r
10066   ScreenSquare(fromX, fromY, &start);\r
10067   ScreenSquare(toX, toY, &finish);\r
10068 \r
10069   /* All moves except knight jumps move in straight line */\r
10070   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
10071     mid.x = start.x + (finish.x - start.x) / 2;\r
10072     mid.y = start.y + (finish.y - start.y) / 2;\r
10073   } else {\r
10074     /* Knight: make straight movement then diagonal */\r
10075     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10076        mid.x = start.x + (finish.x - start.x) / 2;\r
10077        mid.y = start.y;\r
10078      } else {\r
10079        mid.x = start.x;\r
10080        mid.y = start.y + (finish.y - start.y) / 2;\r
10081      }\r
10082   }\r
10083   \r
10084   /* Don't use as many frames for very short moves */\r
10085   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10086     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10087   else\r
10088     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10089 \r
10090   animInfo.from.x = fromX;\r
10091   animInfo.from.y = fromY;\r
10092   animInfo.to.x = toX;\r
10093   animInfo.to.y = toY;\r
10094   animInfo.lastpos = start;\r
10095   animInfo.piece = piece;\r
10096   for (n = 0; n < nFrames; n++) {\r
10097     animInfo.pos = frames[n];\r
10098     DrawPosition(FALSE, NULL);\r
10099     animInfo.lastpos = animInfo.pos;\r
10100     Sleep(appData.animSpeed);\r
10101   }\r
10102   animInfo.pos = finish;\r
10103   DrawPosition(FALSE, NULL);\r
10104 \r
10105   if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg\r
10106 \r
10107   animInfo.piece = EmptySquare;\r
10108   Explode(board, fromX, fromY, toX, toY);\r
10109 }\r
10110 \r
10111 /*      Convert board position to corner of screen rect and color       */\r
10112 \r
10113 static void\r
10114 ScreenSquare(column, row, pt)\r
10115      int column; int row; POINT * pt;\r
10116 {\r
10117   if (flipView) {\r
10118     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
10119     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
10120   } else {\r
10121     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
10122     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
10123   }\r
10124 }\r
10125 \r
10126 /*      Generate a series of frame coords from start->mid->finish.\r
10127         The movement rate doubles until the half way point is\r
10128         reached, then halves back down to the final destination,\r
10129         which gives a nice slow in/out effect. The algorithmn\r
10130         may seem to generate too many intermediates for short\r
10131         moves, but remember that the purpose is to attract the\r
10132         viewers attention to the piece about to be moved and\r
10133         then to where it ends up. Too few frames would be less\r
10134         noticeable.                                             */\r
10135 \r
10136 static void\r
10137 Tween(start, mid, finish, factor, frames, nFrames)\r
10138      POINT * start; POINT * mid;\r
10139      POINT * finish; int factor;\r
10140      POINT frames[]; int * nFrames;\r
10141 {\r
10142   int n, fraction = 1, count = 0;\r
10143 \r
10144   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10145   for (n = 0; n < factor; n++)\r
10146     fraction *= 2;\r
10147   for (n = 0; n < factor; n++) {\r
10148     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10149     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10150     count ++;\r
10151     fraction = fraction / 2;\r
10152   }\r
10153   \r
10154   /* Midpoint */\r
10155   frames[count] = *mid;\r
10156   count ++;\r
10157   \r
10158   /* Slow out, stepping 1/2, then 1/4, ... */\r
10159   fraction = 2;\r
10160   for (n = 0; n < factor; n++) {\r
10161     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10162     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10163     count ++;\r
10164     fraction = fraction * 2;\r
10165   }\r
10166   *nFrames = count;\r
10167 }\r
10168 \r
10169 void\r
10170 SettingsPopUp(ChessProgramState *cps)\r
10171 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10172       EngineOptionsPopup(savedHwnd, cps);\r
10173 }\r
10174 \r
10175 int flock(int fid, int code)\r
10176 {\r
10177     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10178     OVERLAPPED ov;\r
10179     ov.hEvent = NULL;\r
10180     ov.Offset = 0;\r
10181     ov.OffsetHigh = 0;\r
10182     switch(code) {\r
10183       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10184 \r
10185       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10186       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10187       default: return -1;\r
10188     }\r
10189     return 0;\r
10190 }\r
10191 \r
10192 char *\r
10193 Col2Text (int n)\r
10194 {\r
10195     static int i=0;\r
10196     static char col[8][20];\r
10197     COLORREF color = *(COLORREF *) colorVariable[n];\r
10198     i = i+1 & 7;\r
10199     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10200     return col[i];\r
10201 }\r
10202 \r
10203 void\r
10204 ActivateTheme (int new)\r
10205 {   // Redo initialization of features depending on options that can occur in themes\r
10206    InitTextures();\r
10207    if(new) InitDrawingColors();\r
10208    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10209    InitDrawingSizes(boardSize, 0);\r
10210    InvalidateRect(hwndMain, NULL, TRUE);\r
10211 }\r