Fix WB New Variant dialog
[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 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 #include <io.h>\r
77 \r
78 #if __GNUC__\r
79 #include <errno.h>\r
80 #include <string.h>\r
81 #endif\r
82 \r
83 #include "common.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "winboard.h"\r
87 #include "moves.h"\r
88 #include "wclipbrd.h"\r
89 #include "woptions.h"\r
90 #include "wsockerr.h"\r
91 #include "defaults.h"\r
92 #include "help.h"\r
93 #include "wsnap.h"\r
94 \r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
96 \r
97   int myrandom(void);\r
98   void mysrandom(unsigned int seed);\r
99 \r
100 extern int whiteFlag, blackFlag;\r
101 Boolean flipClock = FALSE;\r
102 extern HANDLE chatHandle[];\r
103 extern enum ICS_TYPE ics_type;\r
104 \r
105 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
106 int  MyGetFullPathName P((char *name, char *fullname));\r
107 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
108 VOID NewVariantPopup(HWND hwnd);\r
109 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
110                    /*char*/int promoChar));\r
111 void DisplayMove P((int moveNumber));\r
112 void ChatPopUp P((char *s));\r
113 typedef struct {\r
114   ChessSquare piece;  \r
115   POINT pos;      /* window coordinates of current pos */\r
116   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
117   POINT from;     /* board coordinates of the piece's orig pos */\r
118   POINT to;       /* board coordinates of the piece's new pos */\r
119 } AnimInfo;\r
120 \r
121 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
122 \r
123 typedef struct {\r
124   POINT start;    /* window coordinates of start pos */\r
125   POINT pos;      /* window coordinates of current pos */\r
126   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
127   POINT from;     /* board coordinates of the piece's orig pos */\r
128   ChessSquare piece;\r
129 } DragInfo;\r
130 \r
131 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
132 \r
133 typedef struct {\r
134   POINT sq[2];    /* board coordinates of from, to squares */\r
135 } HighlightInfo;\r
136 \r
137 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
138 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
139 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
140 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
141 \r
142 typedef struct { // [HGM] atomic\r
143   int fromX, fromY, toX, toY, radius;\r
144 } ExplodeInfo;\r
145 \r
146 static ExplodeInfo explodeInfo;\r
147 \r
148 /* Window class names */\r
149 char szAppName[] = "WinBoard";\r
150 char szConsoleName[] = "WBConsole";\r
151 \r
152 /* Title bar text */\r
153 char szTitle[] = "WinBoard";\r
154 char szConsoleTitle[] = "I C S Interaction";\r
155 \r
156 char *programName;\r
157 char *settingsFileName;\r
158 Boolean saveSettingsOnExit;\r
159 char installDir[MSG_SIZ];\r
160 int errorExitStatus;\r
161 \r
162 BoardSize boardSize;\r
163 Boolean chessProgram;\r
164 //static int boardX, boardY;\r
165 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
166 int squareSize, lineGap, minorSize, border;\r
167 static int winW, winH;\r
168 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
169 static int logoHeight = 0;\r
170 static char messageText[MESSAGE_TEXT_MAX];\r
171 static int clockTimerEvent = 0;\r
172 static int loadGameTimerEvent = 0;\r
173 static int analysisTimerEvent = 0;\r
174 static DelayedEventCallback delayedTimerCallback;\r
175 static int delayedTimerEvent = 0;\r
176 static int buttonCount = 2;\r
177 char *icsTextMenuString;\r
178 char *icsNames;\r
179 char *firstChessProgramNames;\r
180 char *secondChessProgramNames;\r
181 \r
182 #define PALETTESIZE 256\r
183 \r
184 HINSTANCE hInst;          /* current instance */\r
185 Boolean alwaysOnTop = FALSE;\r
186 RECT boardRect;\r
187 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
188   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
189 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };\r
190 HPALETTE hPal;\r
191 ColorClass currentColorClass;\r
192 \r
193 static HWND savedHwnd;\r
194 HWND hCommPort = NULL;    /* currently open comm port */\r
195 static HWND hwndPause;    /* pause button */\r
196 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
197 static HBRUSH lightSquareBrush, darkSquareBrush,\r
198   blackSquareBrush, /* [HGM] for band between board and holdings */\r
199   explodeBrush,     /* [HGM] atomic */\r
200   markerBrush[8],   /* [HGM] markers */\r
201   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
202 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
203 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
204 static HPEN gridPen = NULL;\r
205 static HPEN highlightPen = NULL;\r
206 static HPEN premovePen = NULL;\r
207 static NPLOGPALETTE pLogPal;\r
208 static BOOL paletteChanged = FALSE;\r
209 static HICON iconWhite, iconBlack, iconCurrent;\r
210 static int doingSizing = FALSE;\r
211 static int lastSizing = 0;\r
212 static int prevStderrPort;\r
213 static HBITMAP userLogo;\r
214 \r
215 static HBITMAP liteBackTexture = NULL;\r
216 static HBITMAP darkBackTexture = NULL;\r
217 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
218 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
219 static int backTextureSquareSize = 0;\r
220 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
221 \r
222 #if __GNUC__ && !defined(_winmajor)\r
223 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
224 #else\r
225 \r
226 #if defined(_winmajor)\r
227 #define oldDialog (_winmajor < 4)\r
228 #else\r
229 #define oldDialog 0\r
230 #endif\r
231 #endif\r
232 \r
233 #define INTERNATIONAL\r
234 \r
235 #ifdef INTERNATIONAL\r
236 #  define _(s) T_(s)\r
237 #  define N_(s) s\r
238 #else\r
239 #  define _(s) s\r
240 #  define N_(s) s\r
241 #  define T_(s) s\r
242 #  define Translate(x, y)\r
243 #  define LoadLanguageFile(s)\r
244 #endif\r
245 \r
246 #ifdef INTERNATIONAL\r
247 \r
248 Boolean barbaric; // flag indicating if translation is needed\r
249 \r
250 // list of item numbers used in each dialog (used to alter language at run time)\r
251 \r
252 #define ABOUTBOX -1  /* not sure why these are needed */\r
253 #define ABOUTBOX2 -1\r
254 \r
255 int dialogItems[][42] = {\r
256 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
257 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
258   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
259 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
260   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL }, \r
261 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
262   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
263 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
264 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
265   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
266 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
267 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
268   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
269 { ABOUTBOX2, IDC_ChessBoard }, \r
270 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
271   OPT_GameListClose, IDC_GameListDoFilter }, \r
272 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
273 { DLG_Error, IDOK }, \r
274 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
275   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
276 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
277 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
278   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
279   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
280 { DLG_IndexNumber, IDC_Index }, \r
281 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
282 { DLG_TypeInName, IDOK, IDCANCEL }, \r
283 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
284   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
285 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
286   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
287   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
288   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
289   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
290   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
291   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
292 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
293   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
294   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
295   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
296   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
297   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
298   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
299   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
300   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
301 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
302   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
303   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
304   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
305   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
306   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
307   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
308   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
309 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
310   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
311   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
312   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
313   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
314   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
315   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
316   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
317   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
318 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
319   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
320   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
321   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
322   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
323 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
324 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
325   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
326 { DLG_MoveHistory }, \r
327 { DLG_EvalGraph }, \r
328 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
329 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
330 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
331   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
332   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
333   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
334 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
335   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
336   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
337 { 0 }\r
338 };\r
339 \r
340 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
341 static int lastChecked;\r
342 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
343 extern int tinyLayout;\r
344 extern char * menuBarText[][10];\r
345 \r
346 void\r
347 LoadLanguageFile(char *name)\r
348 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
349     FILE *f;\r
350     int i=0, j=0, n=0, k;\r
351     char buf[MSG_SIZ];\r
352 \r
353     if(!name || name[0] == NULLCHAR) return;\r
354       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
355     appData.language = oldLanguage;\r
356     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
357     if((f = fopen(buf, "r")) == NULL) return;\r
358     while((k = fgetc(f)) != EOF) {\r
359         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
360         languageBuf[i] = k;\r
361         if(k == '\n') {\r
362             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
363                 char *p;\r
364                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
365                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
366                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
367                         english[j] = languageBuf + n + 1; *p = 0;\r
368                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
369 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
370                     }\r
371                 }\r
372             }\r
373             n = i + 1;\r
374         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
375             switch(k) {\r
376               case 'n': k = '\n'; break;\r
377               case 'r': k = '\r'; break;\r
378               case 't': k = '\t'; break;\r
379             }\r
380             languageBuf[--i] = k;\r
381         }\r
382         i++;\r
383     }\r
384     fclose(f);\r
385     barbaric = (j != 0);\r
386     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
387 }\r
388 \r
389 char *\r
390 T_(char *s)\r
391 {   // return the translation of the given string\r
392     // efficiency can be improved a lot...\r
393     int i=0;\r
394     static char buf[MSG_SIZ];\r
395 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
396     if(!barbaric) return s;\r
397     if(!s) return ""; // sanity\r
398     while(english[i]) {\r
399         if(!strcmp(s, english[i])) return foreign[i];\r
400         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
401             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
402             return buf;\r
403         }\r
404         i++;\r
405     }\r
406     return s;\r
407 }\r
408 \r
409 void\r
410 Translate(HWND hDlg, int dialogID)\r
411 {   // translate all text items in the given dialog\r
412     int i=0, j, k;\r
413     char buf[MSG_SIZ], *s;\r
414     if(!barbaric) return;\r
415     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
416     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
417     GetWindowText( hDlg, buf, MSG_SIZ );\r
418     s = T_(buf);\r
419     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
420     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
421         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
422         if(strlen(buf) == 0) continue;\r
423         s = T_(buf);\r
424         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
425     }\r
426 }\r
427 \r
428 HMENU\r
429 TranslateOneMenu(int i, HMENU subMenu)\r
430 {\r
431     int j;\r
432     static MENUITEMINFO info;\r
433 \r
434     info.cbSize = sizeof(MENUITEMINFO);\r
435     info.fMask = MIIM_STATE | MIIM_TYPE;\r
436           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
437             char buf[MSG_SIZ];\r
438             info.dwTypeData = buf;\r
439             info.cch = sizeof(buf);\r
440             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
441             if(i < 10) {\r
442                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
443                 else menuText[i][j] = strdup(buf); // remember original on first change\r
444             }\r
445             if(buf[0] == NULLCHAR) continue;\r
446             info.dwTypeData = T_(buf);\r
447             info.cch = strlen(buf)+1;\r
448             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
449           }\r
450     return subMenu;\r
451 }\r
452 \r
453 void\r
454 TranslateMenus(int addLanguage)\r
455 {\r
456     int i;\r
457     WIN32_FIND_DATA fileData;\r
458     HANDLE hFind;\r
459 #define IDM_English 1970\r
460     if(1) {\r
461         HMENU mainMenu = GetMenu(hwndMain);\r
462         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
463           HMENU subMenu = GetSubMenu(mainMenu, i);\r
464           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
465                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
466           TranslateOneMenu(i, subMenu);\r
467         }\r
468         DrawMenuBar(hwndMain);\r
469     }\r
470 \r
471     if(!addLanguage) return;\r
472     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
473         HMENU mainMenu = GetMenu(hwndMain);\r
474         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
475         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
476         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
477         i = 0; lastChecked = IDM_English;\r
478         do {\r
479             char *p, *q = fileData.cFileName;\r
480             int checkFlag = MF_UNCHECKED;\r
481             languageFile[i] = strdup(q);\r
482             if(barbaric && !strcmp(oldLanguage, q)) {\r
483                 checkFlag = MF_CHECKED;\r
484                 lastChecked = IDM_English + i + 1;\r
485                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
486             }\r
487             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
488             p = strstr(fileData.cFileName, ".lng");\r
489             if(p) *p = 0;\r
490             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
491         } while(FindNextFile(hFind, &fileData));\r
492         FindClose(hFind);\r
493     }\r
494 }\r
495 \r
496 #endif\r
497 \r
498 #define IDM_RecentEngines 3000\r
499 \r
500 void\r
501 RecentEngineMenu (char *s)\r
502 {\r
503     if(appData.icsActive) return;\r
504     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
505         HMENU mainMenu = GetMenu(hwndMain);\r
506         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
507         int i=IDM_RecentEngines;\r
508         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
509         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
510         while(*s) {\r
511           char *p = strchr(s, '\n');\r
512           if(p == NULL) return; // malformed!\r
513           *p = NULLCHAR;\r
514           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
515           *p = '\n';\r
516           s = p+1;\r
517         }\r
518     }\r
519 }\r
520 \r
521 \r
522 typedef struct {\r
523   char *name;\r
524   int squareSize;\r
525   int lineGap;\r
526   int smallLayout;\r
527   int tinyLayout;\r
528   int cliWidth, cliHeight;\r
529 } SizeInfo;\r
530 \r
531 SizeInfo sizeInfo[] = \r
532 {\r
533   { "tiny",     21, 0, 1, 1, 0, 0 },\r
534   { "teeny",    25, 1, 1, 1, 0, 0 },\r
535   { "dinky",    29, 1, 1, 1, 0, 0 },\r
536   { "petite",   33, 1, 1, 1, 0, 0 },\r
537   { "slim",     37, 2, 1, 0, 0, 0 },\r
538   { "small",    40, 2, 1, 0, 0, 0 },\r
539   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
540   { "middling", 49, 2, 0, 0, 0, 0 },\r
541   { "average",  54, 2, 0, 0, 0, 0 },\r
542   { "moderate", 58, 3, 0, 0, 0, 0 },\r
543   { "medium",   64, 3, 0, 0, 0, 0 },\r
544   { "bulky",    72, 3, 0, 0, 0, 0 },\r
545   { "large",    80, 3, 0, 0, 0, 0 },\r
546   { "big",      87, 3, 0, 0, 0, 0 },\r
547   { "huge",     95, 3, 0, 0, 0, 0 },\r
548   { "giant",    108, 3, 0, 0, 0, 0 },\r
549   { "colossal", 116, 4, 0, 0, 0, 0 },\r
550   { "titanic",  129, 4, 0, 0, 0, 0 },\r
551   { NULL, 0, 0, 0, 0, 0, 0 }\r
552 };\r
553 \r
554 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
555 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
556 {\r
557   { 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
558   { 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
559   { 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
560   { 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
561   { 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
562   { 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
563   { 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
564   { 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
565   { 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
566   { 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
567   { 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
568   { 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
569   { 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
570   { 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
571   { 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
572   { 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
573   { 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
574   { 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
575 };\r
576 \r
577 MyFont *font[NUM_SIZES][NUM_FONTS];\r
578 \r
579 typedef struct {\r
580   char *label;\r
581   int id;\r
582   HWND hwnd;\r
583   WNDPROC wndproc;\r
584 } MyButtonDesc;\r
585 \r
586 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
587 #define N_BUTTONS 5\r
588 \r
589 MyButtonDesc buttonDesc[N_BUTTONS] =\r
590 {\r
591   {"<<", IDM_ToStart, NULL, NULL},\r
592   {"<", IDM_Backward, NULL, NULL},\r
593   {"P", IDM_Pause, NULL, NULL},\r
594   {">", IDM_Forward, NULL, NULL},\r
595   {">>", IDM_ToEnd, NULL, NULL},\r
596 };\r
597 \r
598 int tinyLayout = 0, smallLayout = 0;\r
599 #define MENU_BAR_ITEMS 9\r
600 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
601   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
602   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
603 };\r
604 \r
605 \r
606 MySound sounds[(int)NSoundClasses];\r
607 MyTextAttribs textAttribs[(int)NColorClasses];\r
608 \r
609 MyColorizeAttribs colorizeAttribs[] = {\r
610   { (COLORREF)0, 0, N_("Shout Text") },\r
611   { (COLORREF)0, 0, N_("SShout/CShout") },\r
612   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
613   { (COLORREF)0, 0, N_("Channel Text") },\r
614   { (COLORREF)0, 0, N_("Kibitz Text") },\r
615   { (COLORREF)0, 0, N_("Tell Text") },\r
616   { (COLORREF)0, 0, N_("Challenge Text") },\r
617   { (COLORREF)0, 0, N_("Request Text") },\r
618   { (COLORREF)0, 0, N_("Seek Text") },\r
619   { (COLORREF)0, 0, N_("Normal Text") },\r
620   { (COLORREF)0, 0, N_("None") }\r
621 };\r
622 \r
623 \r
624 \r
625 static char *commentTitle;\r
626 static char *commentText;\r
627 static int commentIndex;\r
628 static Boolean editComment = FALSE;\r
629 \r
630 \r
631 char errorTitle[MSG_SIZ];\r
632 char errorMessage[2*MSG_SIZ];\r
633 HWND errorDialog = NULL;\r
634 BOOLEAN moveErrorMessageUp = FALSE;\r
635 BOOLEAN consoleEcho = TRUE;\r
636 CHARFORMAT consoleCF;\r
637 COLORREF consoleBackgroundColor;\r
638 \r
639 char *programVersion;\r
640 \r
641 #define CPReal 1\r
642 #define CPComm 2\r
643 #define CPSock 3\r
644 #define CPRcmd 4\r
645 typedef int CPKind;\r
646 \r
647 typedef struct {\r
648   CPKind kind;\r
649   HANDLE hProcess;\r
650   DWORD pid;\r
651   HANDLE hTo;\r
652   HANDLE hFrom;\r
653   SOCKET sock;\r
654   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
655 } ChildProc;\r
656 \r
657 #define INPUT_SOURCE_BUF_SIZE 4096\r
658 \r
659 typedef struct _InputSource {\r
660   CPKind kind;\r
661   HANDLE hFile;\r
662   SOCKET sock;\r
663   int lineByLine;\r
664   HANDLE hThread;\r
665   DWORD id;\r
666   char buf[INPUT_SOURCE_BUF_SIZE];\r
667   char *next;\r
668   DWORD count;\r
669   int error;\r
670   InputCallback func;\r
671   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
672   VOIDSTAR closure;\r
673 } InputSource;\r
674 \r
675 InputSource *consoleInputSource;\r
676 \r
677 DCB dcb;\r
678 \r
679 /* forward */\r
680 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
681 VOID ConsoleCreate();\r
682 LRESULT CALLBACK\r
683   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
684 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
685 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
686 VOID ParseCommSettings(char *arg, DCB *dcb);\r
687 LRESULT CALLBACK\r
688   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
689 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
690 void ParseIcsTextMenu(char *icsTextMenuString);\r
691 VOID PopUpNameDialog(char firstchar);\r
692 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
693 \r
694 /* [AS] */\r
695 int NewGameFRC();\r
696 int GameListOptions();\r
697 \r
698 int dummy; // [HGM] for obsolete args\r
699 \r
700 HWND hwndMain = NULL;        /* root window*/\r
701 HWND hwndConsole = NULL;\r
702 HWND commentDialog = NULL;\r
703 HWND moveHistoryDialog = NULL;\r
704 HWND evalGraphDialog = NULL;\r
705 HWND engineOutputDialog = NULL;\r
706 HWND gameListDialog = NULL;\r
707 HWND editTagsDialog = NULL;\r
708 \r
709 int commentUp = FALSE;\r
710 \r
711 WindowPlacement wpMain;\r
712 WindowPlacement wpConsole;\r
713 WindowPlacement wpComment;\r
714 WindowPlacement wpMoveHistory;\r
715 WindowPlacement wpEvalGraph;\r
716 WindowPlacement wpEngineOutput;\r
717 WindowPlacement wpGameList;\r
718 WindowPlacement wpTags;\r
719 \r
720 VOID EngineOptionsPopup(); // [HGM] settings\r
721 \r
722 VOID GothicPopUp(char *title, VariantClass variant);\r
723 /*\r
724  * Setting "frozen" should disable all user input other than deleting\r
725  * the window.  We do this while engines are initializing themselves.\r
726  */\r
727 static int frozen = 0;\r
728 static int oldMenuItemState[MENU_BAR_ITEMS];\r
729 void FreezeUI()\r
730 {\r
731   HMENU hmenu;\r
732   int i;\r
733 \r
734   if (frozen) return;\r
735   frozen = 1;\r
736   hmenu = GetMenu(hwndMain);\r
737   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
738     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
739   }\r
740   DrawMenuBar(hwndMain);\r
741 }\r
742 \r
743 /* Undo a FreezeUI */\r
744 void ThawUI()\r
745 {\r
746   HMENU hmenu;\r
747   int i;\r
748 \r
749   if (!frozen) return;\r
750   frozen = 0;\r
751   hmenu = GetMenu(hwndMain);\r
752   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
753     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
754   }\r
755   DrawMenuBar(hwndMain);\r
756 }\r
757 \r
758 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
759 \r
760 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
761 #ifdef JAWS\r
762 #include "jaws.c"\r
763 #else\r
764 #define JAWS_INIT\r
765 #define JAWS_ARGS\r
766 #define JAWS_ALT_INTERCEPT\r
767 #define JAWS_KBUP_NAVIGATION\r
768 #define JAWS_KBDOWN_NAVIGATION\r
769 #define JAWS_MENU_ITEMS\r
770 #define JAWS_SILENCE\r
771 #define JAWS_REPLAY\r
772 #define JAWS_ACCEL\r
773 #define JAWS_COPYRIGHT\r
774 #define JAWS_DELETE(X) X\r
775 #define SAYMACHINEMOVE()\r
776 #define SAY(X)\r
777 #endif\r
778 \r
779 /*---------------------------------------------------------------------------*\\r
780  *\r
781  * WinMain\r
782  *\r
783 \*---------------------------------------------------------------------------*/\r
784 \r
785 int APIENTRY\r
786 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
787         LPSTR lpCmdLine, int nCmdShow)\r
788 {\r
789   MSG msg;\r
790   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
791 //  INITCOMMONCONTROLSEX ex;\r
792 \r
793   debugFP = stderr;\r
794 \r
795   LoadLibrary("RICHED32.DLL");\r
796   consoleCF.cbSize = sizeof(CHARFORMAT);\r
797 \r
798   if (!InitApplication(hInstance)) {\r
799     return (FALSE);\r
800   }\r
801   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
802     return (FALSE);\r
803   }\r
804 \r
805   JAWS_INIT\r
806   TranslateMenus(1);\r
807 \r
808 //  InitCommonControlsEx(&ex);\r
809   InitCommonControls();\r
810 \r
811   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
812   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
813   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
814 \r
815   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
816 \r
817   while (GetMessage(&msg, /* message structure */\r
818                     NULL, /* handle of window receiving the message */\r
819                     0,    /* lowest message to examine */\r
820                     0))   /* highest message to examine */\r
821     {\r
822 \r
823       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
824         // [HGM] navigate: switch between all windows with tab\r
825         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
826         int i, currentElement = 0;\r
827 \r
828         // first determine what element of the chain we come from (if any)\r
829         if(appData.icsActive) {\r
830             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
831             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
832         }\r
833         if(engineOutputDialog && EngineOutputIsUp()) {\r
834             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
835             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
836         }\r
837         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
838             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
839         }\r
840         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
841         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
842         if(msg.hwnd == e1)                 currentElement = 2; else\r
843         if(msg.hwnd == e2)                 currentElement = 3; else\r
844         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
845         if(msg.hwnd == mh)                currentElement = 4; else\r
846         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
847         if(msg.hwnd == hText)  currentElement = 5; else\r
848         if(msg.hwnd == hInput) currentElement = 6; else\r
849         for (i = 0; i < N_BUTTONS; i++) {\r
850             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
851         }\r
852 \r
853         // determine where to go to\r
854         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
855           do {\r
856             currentElement = (currentElement + direction) % 7;\r
857             switch(currentElement) {\r
858                 case 0:\r
859                   h = hwndMain; break; // passing this case always makes the loop exit\r
860                 case 1:\r
861                   h = buttonDesc[0].hwnd; break; // could be NULL\r
862                 case 2:\r
863                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
864                   h = e1; break;\r
865                 case 3:\r
866                   if(!EngineOutputIsUp()) continue;\r
867                   h = e2; break;\r
868                 case 4:\r
869                   if(!MoveHistoryIsUp()) continue;\r
870                   h = mh; break;\r
871 //              case 6: // input to eval graph does not seem to get here!\r
872 //                if(!EvalGraphIsUp()) continue;\r
873 //                h = evalGraphDialog; break;\r
874                 case 5:\r
875                   if(!appData.icsActive) continue;\r
876                   SAY("display");\r
877                   h = hText; break;\r
878                 case 6:\r
879                   if(!appData.icsActive) continue;\r
880                   SAY("input");\r
881                   h = hInput; break;\r
882             }\r
883           } while(h == 0);\r
884 \r
885           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
886           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
887           SetFocus(h);\r
888 \r
889           continue; // this message now has been processed\r
890         }\r
891       }\r
892 \r
893       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
894           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
895           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
896           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
897           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
898           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
899           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
900           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
901           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
902           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
903         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
904         for(i=0; i<MAX_CHAT; i++) \r
905             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
906                 done = 1; break;\r
907         }\r
908         if(done) continue; // [HGM] chat: end patch\r
909         TranslateMessage(&msg); /* Translates virtual key codes */\r
910         DispatchMessage(&msg);  /* Dispatches message to window */\r
911       }\r
912     }\r
913 \r
914 \r
915   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
916 }\r
917 \r
918 /*---------------------------------------------------------------------------*\\r
919  *\r
920  * Initialization functions\r
921  *\r
922 \*---------------------------------------------------------------------------*/\r
923 \r
924 void\r
925 SetUserLogo()\r
926 {   // update user logo if necessary\r
927     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
928 \r
929     if(appData.autoLogo) {\r
930           curName = UserName();\r
931           if(strcmp(curName, oldUserName)) {\r
932                 GetCurrentDirectory(MSG_SIZ, dir);\r
933                 SetCurrentDirectory(installDir);\r
934                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
935                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
936                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
937                 if(userLogo == NULL)\r
938                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
939                 SetCurrentDirectory(dir); /* return to prev directory */\r
940           }\r
941     }\r
942 }\r
943 \r
944 BOOL\r
945 InitApplication(HINSTANCE hInstance)\r
946 {\r
947   WNDCLASS wc;\r
948 \r
949   /* Fill in window class structure with parameters that describe the */\r
950   /* main window. */\r
951 \r
952   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
953   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
954   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
955   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
956   wc.hInstance     = hInstance;         /* Owner of this class */\r
957   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
958   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
959   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
960   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
961   wc.lpszClassName = szAppName;                 /* Name to register as */\r
962 \r
963   /* Register the window class and return success/failure code. */\r
964   if (!RegisterClass(&wc)) return FALSE;\r
965 \r
966   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
967   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
968   wc.cbClsExtra    = 0;\r
969   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
970   wc.hInstance     = hInstance;\r
971   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
972   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
973   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
974   wc.lpszMenuName  = NULL;\r
975   wc.lpszClassName = szConsoleName;\r
976 \r
977   if (!RegisterClass(&wc)) return FALSE;\r
978   return TRUE;\r
979 }\r
980 \r
981 \r
982 /* Set by InitInstance, used by EnsureOnScreen */\r
983 int screenHeight, screenWidth;\r
984 \r
985 void\r
986 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
987 {\r
988 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
989   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
990   if (*x > screenWidth - 32) *x = 0;\r
991   if (*y > screenHeight - 32) *y = 0;\r
992   if (*x < minX) *x = minX;\r
993   if (*y < minY) *y = minY;\r
994 }\r
995 \r
996 VOID\r
997 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
998 {\r
999   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1000   GetCurrentDirectory(MSG_SIZ, dir);\r
1001   SetCurrentDirectory(installDir);\r
1002   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1003       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1004 \r
1005       if (cps->programLogo == NULL && appData.debugMode) {\r
1006           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1007       }\r
1008   } else if(appData.autoLogo) {\r
1009       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1010         char *opponent = "";\r
1011         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1012         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1013         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1014         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1015             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1016             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1017         }\r
1018       } else\r
1019       if(appData.directory[n] && appData.directory[n][0]) {\r
1020         SetCurrentDirectory(appData.directory[n]);\r
1021         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1022       }\r
1023   }\r
1024   SetCurrentDirectory(dir); /* return to prev directory */\r
1025 }\r
1026 \r
1027 VOID\r
1028 InitTextures()\r
1029 {\r
1030   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1031   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1032   \r
1033   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1034       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1035       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1036       liteBackTextureMode = appData.liteBackTextureMode;\r
1037 \r
1038       if (liteBackTexture == NULL && appData.debugMode) {\r
1039           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1040       }\r
1041   }\r
1042   \r
1043   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1044       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1045       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1046       darkBackTextureMode = appData.darkBackTextureMode;\r
1047 \r
1048       if (darkBackTexture == NULL && appData.debugMode) {\r
1049           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1050       }\r
1051   }\r
1052 }\r
1053 \r
1054 BOOL\r
1055 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1056 {\r
1057   HWND hwnd; /* Main window handle. */\r
1058   int ibs;\r
1059   WINDOWPLACEMENT wp;\r
1060   char *filepart;\r
1061 \r
1062   hInst = hInstance;    /* Store instance handle in our global variable */\r
1063   programName = szAppName;\r
1064 \r
1065   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1066     *filepart = NULLCHAR;\r
1067     SetCurrentDirectory(installDir);\r
1068   } else {\r
1069     GetCurrentDirectory(MSG_SIZ, installDir);\r
1070   }\r
1071   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1072   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
1073   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1074   /* xboard, and older WinBoards, controlled the move sound with the\r
1075      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1076      always turn the option on (so that the backend will call us),\r
1077      then let the user turn the sound off by setting it to silence if\r
1078      desired.  To accommodate old winboard.ini files saved by old\r
1079      versions of WinBoard, we also turn off the sound if the option\r
1080      was initially set to false. [HGM] taken out of InitAppData */\r
1081   if (!appData.ringBellAfterMoves) {\r
1082     sounds[(int)SoundMove].name = strdup("");\r
1083     appData.ringBellAfterMoves = TRUE;\r
1084   }\r
1085   if (appData.debugMode) {\r
1086     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1087     setbuf(debugFP, NULL);\r
1088   }\r
1089 \r
1090   LoadLanguageFile(appData.language);\r
1091 \r
1092   InitBackEnd1();\r
1093 \r
1094 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1095 //  InitEngineUCI( installDir, &second );\r
1096 \r
1097   /* Create a main window for this application instance. */\r
1098   hwnd = CreateWindow(szAppName, szTitle,\r
1099                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1100                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1101                       NULL, NULL, hInstance, NULL);\r
1102   hwndMain = hwnd;\r
1103 \r
1104   /* If window could not be created, return "failure" */\r
1105   if (!hwnd) {\r
1106     return (FALSE);\r
1107   }\r
1108 \r
1109   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1110   LoadLogo(&first, 0, FALSE);\r
1111   LoadLogo(&second, 1, appData.icsActive);\r
1112 \r
1113   SetUserLogo();\r
1114 \r
1115   iconWhite = LoadIcon(hInstance, "icon_white");\r
1116   iconBlack = LoadIcon(hInstance, "icon_black");\r
1117   iconCurrent = iconWhite;\r
1118   InitDrawingColors();\r
1119   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1120   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1121   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1122   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1123     /* Compute window size for each board size, and use the largest\r
1124        size that fits on this screen as the default. */\r
1125     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1126     if (boardSize == (BoardSize)-1 &&\r
1127         winH <= screenHeight\r
1128            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1129         && winW <= screenWidth) {\r
1130       boardSize = (BoardSize)ibs;\r
1131     }\r
1132   }\r
1133 \r
1134   InitDrawingSizes(boardSize, 0);\r
1135   RecentEngineMenu(appData.recentEngineList);\r
1136   InitMenuChecks();\r
1137   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1138 \r
1139   /* [AS] Load textures if specified */\r
1140   InitTextures();\r
1141 \r
1142   mysrandom( (unsigned) time(NULL) );\r
1143 \r
1144   /* [AS] Restore layout */\r
1145   if( wpMoveHistory.visible ) {\r
1146       MoveHistoryPopUp();\r
1147   }\r
1148 \r
1149   if( wpEvalGraph.visible ) {\r
1150       EvalGraphPopUp();\r
1151   }\r
1152 \r
1153   if( wpEngineOutput.visible ) {\r
1154       EngineOutputPopUp();\r
1155   }\r
1156 \r
1157   /* Make the window visible; update its client area; and return "success" */\r
1158   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1159   wp.length = sizeof(WINDOWPLACEMENT);\r
1160   wp.flags = 0;\r
1161   wp.showCmd = nCmdShow;\r
1162   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1163   wp.rcNormalPosition.left = wpMain.x;\r
1164   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1165   wp.rcNormalPosition.top = wpMain.y;\r
1166   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1167   SetWindowPlacement(hwndMain, &wp);\r
1168 \r
1169   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1170 \r
1171   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1172                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1173 \r
1174   if (hwndConsole) {\r
1175 #if AOT_CONSOLE\r
1176     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1177                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1178 #endif\r
1179     ShowWindow(hwndConsole, nCmdShow);\r
1180     SetActiveWindow(hwndConsole);\r
1181   }\r
1182   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1183   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1184 \r
1185   return TRUE;\r
1186 \r
1187 }\r
1188 \r
1189 VOID\r
1190 InitMenuChecks()\r
1191 {\r
1192   HMENU hmenu = GetMenu(hwndMain);\r
1193 \r
1194   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1195                         MF_BYCOMMAND|((appData.icsActive &&\r
1196                                        *appData.icsCommPort != NULLCHAR) ?\r
1197                                       MF_ENABLED : MF_GRAYED));\r
1198   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1199                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1200                                      MF_CHECKED : MF_UNCHECKED));\r
1201 }\r
1202 \r
1203 //---------------------------------------------------------------------------------------------------------\r
1204 \r
1205 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1206 #define XBOARD FALSE\r
1207 \r
1208 #define OPTCHAR "/"\r
1209 #define SEPCHAR "="\r
1210 #define TOPLEVEL 0\r
1211 \r
1212 #include "args.h"\r
1213 \r
1214 // front-end part of option handling\r
1215 \r
1216 VOID\r
1217 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1218 {\r
1219   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1220   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1221   DeleteDC(hdc);\r
1222   lf->lfWidth = 0;\r
1223   lf->lfEscapement = 0;\r
1224   lf->lfOrientation = 0;\r
1225   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1226   lf->lfItalic = mfp->italic;\r
1227   lf->lfUnderline = mfp->underline;\r
1228   lf->lfStrikeOut = mfp->strikeout;\r
1229   lf->lfCharSet = mfp->charset;\r
1230   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1231   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1232   lf->lfQuality = DEFAULT_QUALITY;\r
1233   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1234     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1235 }\r
1236 \r
1237 void\r
1238 CreateFontInMF(MyFont *mf)\r
1239\r
1240   LFfromMFP(&mf->lf, &mf->mfp);\r
1241   if (mf->hf) DeleteObject(mf->hf);\r
1242   mf->hf = CreateFontIndirect(&mf->lf);\r
1243 }\r
1244 \r
1245 // [HGM] This platform-dependent table provides the location for storing the color info\r
1246 void *\r
1247 colorVariable[] = {\r
1248   &whitePieceColor, \r
1249   &blackPieceColor, \r
1250   &lightSquareColor,\r
1251   &darkSquareColor, \r
1252   &highlightSquareColor,\r
1253   &premoveHighlightColor,\r
1254   NULL,\r
1255   &consoleBackgroundColor,\r
1256   &appData.fontForeColorWhite,\r
1257   &appData.fontBackColorWhite,\r
1258   &appData.fontForeColorBlack,\r
1259   &appData.fontBackColorBlack,\r
1260   &appData.evalHistColorWhite,\r
1261   &appData.evalHistColorBlack,\r
1262   &appData.highlightArrowColor,\r
1263 };\r
1264 \r
1265 /* Command line font name parser.  NULL name means do nothing.\r
1266    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1267    For backward compatibility, syntax without the colon is also\r
1268    accepted, but font names with digits in them won't work in that case.\r
1269 */\r
1270 VOID\r
1271 ParseFontName(char *name, MyFontParams *mfp)\r
1272 {\r
1273   char *p, *q;\r
1274   if (name == NULL) return;\r
1275   p = name;\r
1276   q = strchr(p, ':');\r
1277   if (q) {\r
1278     if (q - p >= sizeof(mfp->faceName))\r
1279       ExitArgError(_("Font name too long:"), name, TRUE);\r
1280     memcpy(mfp->faceName, p, q - p);\r
1281     mfp->faceName[q - p] = NULLCHAR;\r
1282     p = q + 1;\r
1283   } else {\r
1284     q = mfp->faceName;\r
1285 \r
1286     while (*p && !isdigit(*p)) {\r
1287       *q++ = *p++;\r
1288       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1289         ExitArgError(_("Font name too long:"), name, TRUE);\r
1290     }\r
1291     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1292     *q = NULLCHAR;\r
1293   }\r
1294   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1295   mfp->pointSize = (float) atof(p);\r
1296   mfp->bold = (strchr(p, 'b') != NULL);\r
1297   mfp->italic = (strchr(p, 'i') != NULL);\r
1298   mfp->underline = (strchr(p, 'u') != NULL);\r
1299   mfp->strikeout = (strchr(p, 's') != NULL);\r
1300   mfp->charset = DEFAULT_CHARSET;\r
1301   q = strchr(p, 'c');\r
1302   if (q)\r
1303     mfp->charset = (BYTE) atoi(q+1);\r
1304 }\r
1305 \r
1306 void\r
1307 ParseFont(char *name, int number)\r
1308 { // wrapper to shield back-end from 'font'\r
1309   ParseFontName(name, &font[boardSize][number]->mfp);\r
1310 }\r
1311 \r
1312 void\r
1313 SetFontDefaults()\r
1314 { // in WB  we have a 2D array of fonts; this initializes their description\r
1315   int i, j;\r
1316   /* Point font array elements to structures and\r
1317      parse default font names */\r
1318   for (i=0; i<NUM_FONTS; i++) {\r
1319     for (j=0; j<NUM_SIZES; j++) {\r
1320       font[j][i] = &fontRec[j][i];\r
1321       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1322     }\r
1323   }\r
1324 }\r
1325 \r
1326 void\r
1327 CreateFonts()\r
1328 { // here we create the actual fonts from the selected descriptions\r
1329   int i, j;\r
1330   for (i=0; i<NUM_FONTS; i++) {\r
1331     for (j=0; j<NUM_SIZES; j++) {\r
1332       CreateFontInMF(font[j][i]);\r
1333     }\r
1334   }\r
1335 }\r
1336 /* Color name parser.\r
1337    X version accepts X color names, but this one\r
1338    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1339 COLORREF\r
1340 ParseColorName(char *name)\r
1341 {\r
1342   int red, green, blue, count;\r
1343   char buf[MSG_SIZ];\r
1344 \r
1345   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1346   if (count != 3) {\r
1347     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1348       &red, &green, &blue);\r
1349   }\r
1350   if (count != 3) {\r
1351     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1352     DisplayError(buf, 0);\r
1353     return RGB(0, 0, 0);\r
1354   }\r
1355   return PALETTERGB(red, green, blue);\r
1356 }\r
1357 \r
1358 void\r
1359 ParseColor(int n, char *name)\r
1360 { // for WinBoard the color is an int, which needs to be derived from the string\r
1361   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1362 }\r
1363 \r
1364 void\r
1365 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1366 {\r
1367   char *e = argValue;\r
1368   int eff = 0;\r
1369 \r
1370   while (*e) {\r
1371     if (*e == 'b')      eff |= CFE_BOLD;\r
1372     else if (*e == 'i') eff |= CFE_ITALIC;\r
1373     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1374     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1375     else if (*e == '#' || isdigit(*e)) break;\r
1376     e++;\r
1377   }\r
1378   *effects = eff;\r
1379   *color   = ParseColorName(e);\r
1380 }\r
1381 \r
1382 void\r
1383 ParseTextAttribs(ColorClass cc, char *s)\r
1384 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1385     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1386     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1387 }\r
1388 \r
1389 void\r
1390 ParseBoardSize(void *addr, char *name)\r
1391 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1392   BoardSize bs = SizeTiny;\r
1393   while (sizeInfo[bs].name != NULL) {\r
1394     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1395         *(BoardSize *)addr = bs;\r
1396         return;\r
1397     }\r
1398     bs++;\r
1399   }\r
1400   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1401 }\r
1402 \r
1403 void\r
1404 LoadAllSounds()\r
1405 { // [HGM] import name from appData first\r
1406   ColorClass cc;\r
1407   SoundClass sc;\r
1408   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1409     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1410     textAttribs[cc].sound.data = NULL;\r
1411     MyLoadSound(&textAttribs[cc].sound);\r
1412   }\r
1413   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1414     textAttribs[cc].sound.name = strdup("");\r
1415     textAttribs[cc].sound.data = NULL;\r
1416   }\r
1417   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1418     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1419     sounds[sc].data = NULL;\r
1420     MyLoadSound(&sounds[sc]);\r
1421   }\r
1422 }\r
1423 \r
1424 void\r
1425 SetCommPortDefaults()\r
1426 {\r
1427    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1428   dcb.DCBlength = sizeof(DCB);\r
1429   dcb.BaudRate = 9600;\r
1430   dcb.fBinary = TRUE;\r
1431   dcb.fParity = FALSE;\r
1432   dcb.fOutxCtsFlow = FALSE;\r
1433   dcb.fOutxDsrFlow = FALSE;\r
1434   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1435   dcb.fDsrSensitivity = FALSE;\r
1436   dcb.fTXContinueOnXoff = TRUE;\r
1437   dcb.fOutX = FALSE;\r
1438   dcb.fInX = FALSE;\r
1439   dcb.fNull = FALSE;\r
1440   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1441   dcb.fAbortOnError = FALSE;\r
1442   dcb.ByteSize = 7;\r
1443   dcb.Parity = SPACEPARITY;\r
1444   dcb.StopBits = ONESTOPBIT;\r
1445 }\r
1446 \r
1447 // [HGM] args: these three cases taken out to stay in front-end\r
1448 void\r
1449 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1450 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1451         // while the curent board size determines the element. This system should be ported to XBoard.\r
1452         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1453         int bs;\r
1454         for (bs=0; bs<NUM_SIZES; bs++) {\r
1455           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1456           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1457           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1458             ad->argName, mfp->faceName, mfp->pointSize,\r
1459             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1460             mfp->bold ? "b" : "",\r
1461             mfp->italic ? "i" : "",\r
1462             mfp->underline ? "u" : "",\r
1463             mfp->strikeout ? "s" : "",\r
1464             (int)mfp->charset);\r
1465         }\r
1466       }\r
1467 \r
1468 void\r
1469 ExportSounds()\r
1470 { // [HGM] copy the names from the internal WB variables to appData\r
1471   ColorClass cc;\r
1472   SoundClass sc;\r
1473   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1474     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1475   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1476     (&appData.soundMove)[sc] = sounds[sc].name;\r
1477 }\r
1478 \r
1479 void\r
1480 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1481 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1482         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1483         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1484           (ta->effects & CFE_BOLD) ? "b" : "",\r
1485           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1486           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1487           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1488           (ta->effects) ? " " : "",\r
1489           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1490       }\r
1491 \r
1492 void\r
1493 SaveColor(FILE *f, ArgDescriptor *ad)\r
1494 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1495         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1496         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1497           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1498 }\r
1499 \r
1500 void\r
1501 SaveBoardSize(FILE *f, char *name, void *addr)\r
1502 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1503   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1504 }\r
1505 \r
1506 void\r
1507 ParseCommPortSettings(char *s)\r
1508 { // wrapper to keep dcb from back-end\r
1509   ParseCommSettings(s, &dcb);\r
1510 }\r
1511 \r
1512 void\r
1513 GetWindowCoords()\r
1514 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1515   GetActualPlacement(hwndMain, &wpMain);\r
1516   GetActualPlacement(hwndConsole, &wpConsole);\r
1517   GetActualPlacement(commentDialog, &wpComment);\r
1518   GetActualPlacement(editTagsDialog, &wpTags);\r
1519   GetActualPlacement(gameListDialog, &wpGameList);\r
1520   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1521   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1522   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1523 }\r
1524 \r
1525 void\r
1526 PrintCommPortSettings(FILE *f, char *name)\r
1527 { // wrapper to shield back-end from DCB\r
1528       PrintCommSettings(f, name, &dcb);\r
1529 }\r
1530 \r
1531 int\r
1532 MySearchPath(char *installDir, char *name, char *fullname)\r
1533 {\r
1534   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1535   if(name[0]== '%') {\r
1536     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1537     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1538       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1539       *strchr(buf, '%') = 0;\r
1540       strcat(fullname, getenv(buf));\r
1541       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1542     }\r
1543     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1544     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1545     return (int) strlen(fullname);\r
1546   }\r
1547   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1548 }\r
1549 \r
1550 int\r
1551 MyGetFullPathName(char *name, char *fullname)\r
1552 {\r
1553   char *dummy;\r
1554   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1555 }\r
1556 \r
1557 int\r
1558 MainWindowUp()\r
1559 { // [HGM] args: allows testing if main window is realized from back-end\r
1560   return hwndMain != NULL;\r
1561 }\r
1562 \r
1563 void\r
1564 PopUpStartupDialog()\r
1565 {\r
1566     FARPROC lpProc;\r
1567     \r
1568     LoadLanguageFile(appData.language);\r
1569     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1570     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1571     FreeProcInstance(lpProc);\r
1572 }\r
1573 \r
1574 /*---------------------------------------------------------------------------*\\r
1575  *\r
1576  * GDI board drawing routines\r
1577  *\r
1578 \*---------------------------------------------------------------------------*/\r
1579 \r
1580 /* [AS] Draw square using background texture */\r
1581 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1582 {\r
1583     XFORM   x;\r
1584 \r
1585     if( mode == 0 ) {\r
1586         return; /* Should never happen! */\r
1587     }\r
1588 \r
1589     SetGraphicsMode( dst, GM_ADVANCED );\r
1590 \r
1591     switch( mode ) {\r
1592     case 1:\r
1593         /* Identity */\r
1594         break;\r
1595     case 2:\r
1596         /* X reflection */\r
1597         x.eM11 = -1.0;\r
1598         x.eM12 = 0;\r
1599         x.eM21 = 0;\r
1600         x.eM22 = 1.0;\r
1601         x.eDx = (FLOAT) dw + dx - 1;\r
1602         x.eDy = 0;\r
1603         dx = 0;\r
1604         SetWorldTransform( dst, &x );\r
1605         break;\r
1606     case 3:\r
1607         /* Y reflection */\r
1608         x.eM11 = 1.0;\r
1609         x.eM12 = 0;\r
1610         x.eM21 = 0;\r
1611         x.eM22 = -1.0;\r
1612         x.eDx = 0;\r
1613         x.eDy = (FLOAT) dh + dy - 1;\r
1614         dy = 0;\r
1615         SetWorldTransform( dst, &x );\r
1616         break;\r
1617     case 4:\r
1618         /* X/Y flip */\r
1619         x.eM11 = 0;\r
1620         x.eM12 = 1.0;\r
1621         x.eM21 = 1.0;\r
1622         x.eM22 = 0;\r
1623         x.eDx = (FLOAT) dx;\r
1624         x.eDy = (FLOAT) dy;\r
1625         dx = 0;\r
1626         dy = 0;\r
1627         SetWorldTransform( dst, &x );\r
1628         break;\r
1629     }\r
1630 \r
1631     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1632 \r
1633     x.eM11 = 1.0;\r
1634     x.eM12 = 0;\r
1635     x.eM21 = 0;\r
1636     x.eM22 = 1.0;\r
1637     x.eDx = 0;\r
1638     x.eDy = 0;\r
1639     SetWorldTransform( dst, &x );\r
1640 \r
1641     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1642 }\r
1643 \r
1644 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1645 enum {\r
1646     PM_WP = (int) WhitePawn, \r
1647     PM_WN = (int) WhiteKnight, \r
1648     PM_WB = (int) WhiteBishop, \r
1649     PM_WR = (int) WhiteRook, \r
1650     PM_WQ = (int) WhiteQueen, \r
1651     PM_WF = (int) WhiteFerz, \r
1652     PM_WW = (int) WhiteWazir, \r
1653     PM_WE = (int) WhiteAlfil, \r
1654     PM_WM = (int) WhiteMan, \r
1655     PM_WO = (int) WhiteCannon, \r
1656     PM_WU = (int) WhiteUnicorn, \r
1657     PM_WH = (int) WhiteNightrider, \r
1658     PM_WA = (int) WhiteAngel, \r
1659     PM_WC = (int) WhiteMarshall, \r
1660     PM_WAB = (int) WhiteCardinal, \r
1661     PM_WD = (int) WhiteDragon, \r
1662     PM_WL = (int) WhiteLance, \r
1663     PM_WS = (int) WhiteCobra, \r
1664     PM_WV = (int) WhiteFalcon, \r
1665     PM_WSG = (int) WhiteSilver, \r
1666     PM_WG = (int) WhiteGrasshopper, \r
1667     PM_WK = (int) WhiteKing,\r
1668     PM_BP = (int) BlackPawn, \r
1669     PM_BN = (int) BlackKnight, \r
1670     PM_BB = (int) BlackBishop, \r
1671     PM_BR = (int) BlackRook, \r
1672     PM_BQ = (int) BlackQueen, \r
1673     PM_BF = (int) BlackFerz, \r
1674     PM_BW = (int) BlackWazir, \r
1675     PM_BE = (int) BlackAlfil, \r
1676     PM_BM = (int) BlackMan,\r
1677     PM_BO = (int) BlackCannon, \r
1678     PM_BU = (int) BlackUnicorn, \r
1679     PM_BH = (int) BlackNightrider, \r
1680     PM_BA = (int) BlackAngel, \r
1681     PM_BC = (int) BlackMarshall, \r
1682     PM_BG = (int) BlackGrasshopper, \r
1683     PM_BAB = (int) BlackCardinal,\r
1684     PM_BD = (int) BlackDragon,\r
1685     PM_BL = (int) BlackLance,\r
1686     PM_BS = (int) BlackCobra,\r
1687     PM_BV = (int) BlackFalcon,\r
1688     PM_BSG = (int) BlackSilver,\r
1689     PM_BK = (int) BlackKing\r
1690 };\r
1691 \r
1692 static HFONT hPieceFont = NULL;\r
1693 static HBITMAP hPieceMask[(int) EmptySquare];\r
1694 static HBITMAP hPieceFace[(int) EmptySquare];\r
1695 static int fontBitmapSquareSize = 0;\r
1696 static char pieceToFontChar[(int) EmptySquare] =\r
1697                               { 'p', 'n', 'b', 'r', 'q', \r
1698                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1699                       'k', 'o', 'm', 'v', 't', 'w', \r
1700                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1701                                                               'l' };\r
1702 \r
1703 extern BOOL SetCharTable( char *table, const char * map );\r
1704 /* [HGM] moved to backend.c */\r
1705 \r
1706 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1707 {\r
1708     HBRUSH hbrush;\r
1709     BYTE r1 = GetRValue( color );\r
1710     BYTE g1 = GetGValue( color );\r
1711     BYTE b1 = GetBValue( color );\r
1712     BYTE r2 = r1 / 2;\r
1713     BYTE g2 = g1 / 2;\r
1714     BYTE b2 = b1 / 2;\r
1715     RECT rc;\r
1716 \r
1717     /* Create a uniform background first */\r
1718     hbrush = CreateSolidBrush( color );\r
1719     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1720     FillRect( hdc, &rc, hbrush );\r
1721     DeleteObject( hbrush );\r
1722     \r
1723     if( mode == 1 ) {\r
1724         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1725         int steps = squareSize / 2;\r
1726         int i;\r
1727 \r
1728         for( i=0; i<steps; i++ ) {\r
1729             BYTE r = r1 - (r1-r2) * i / steps;\r
1730             BYTE g = g1 - (g1-g2) * i / steps;\r
1731             BYTE b = b1 - (b1-b2) * i / steps;\r
1732 \r
1733             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1734             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1735             FillRect( hdc, &rc, hbrush );\r
1736             DeleteObject(hbrush);\r
1737         }\r
1738     }\r
1739     else if( mode == 2 ) {\r
1740         /* Diagonal gradient, good more or less for every piece */\r
1741         POINT triangle[3];\r
1742         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1743         HBRUSH hbrush_old;\r
1744         int steps = squareSize;\r
1745         int i;\r
1746 \r
1747         triangle[0].x = squareSize - steps;\r
1748         triangle[0].y = squareSize;\r
1749         triangle[1].x = squareSize;\r
1750         triangle[1].y = squareSize;\r
1751         triangle[2].x = squareSize;\r
1752         triangle[2].y = squareSize - steps;\r
1753 \r
1754         for( i=0; i<steps; i++ ) {\r
1755             BYTE r = r1 - (r1-r2) * i / steps;\r
1756             BYTE g = g1 - (g1-g2) * i / steps;\r
1757             BYTE b = b1 - (b1-b2) * i / steps;\r
1758 \r
1759             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1760             hbrush_old = SelectObject( hdc, hbrush );\r
1761             Polygon( hdc, triangle, 3 );\r
1762             SelectObject( hdc, hbrush_old );\r
1763             DeleteObject(hbrush);\r
1764             triangle[0].x++;\r
1765             triangle[2].y++;\r
1766         }\r
1767 \r
1768         SelectObject( hdc, hpen );\r
1769     }\r
1770 }\r
1771 \r
1772 /*\r
1773     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1774     seems to work ok. The main problem here is to find the "inside" of a chess\r
1775     piece: follow the steps as explained below.\r
1776 */\r
1777 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1778 {\r
1779     HBITMAP hbm;\r
1780     HBITMAP hbm_old;\r
1781     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1782     RECT rc;\r
1783     SIZE sz;\r
1784     POINT pt;\r
1785     int backColor = whitePieceColor; \r
1786     int foreColor = blackPieceColor;\r
1787     \r
1788     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1789         backColor = appData.fontBackColorWhite;\r
1790         foreColor = appData.fontForeColorWhite;\r
1791     }\r
1792     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1793         backColor = appData.fontBackColorBlack;\r
1794         foreColor = appData.fontForeColorBlack;\r
1795     }\r
1796 \r
1797     /* Mask */\r
1798     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1799 \r
1800     hbm_old = SelectObject( hdc, hbm );\r
1801 \r
1802     rc.left = 0;\r
1803     rc.top = 0;\r
1804     rc.right = squareSize;\r
1805     rc.bottom = squareSize;\r
1806 \r
1807     /* Step 1: background is now black */\r
1808     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1809 \r
1810     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1811 \r
1812     pt.x = (squareSize - sz.cx) / 2;\r
1813     pt.y = (squareSize - sz.cy) / 2;\r
1814 \r
1815     SetBkMode( hdc, TRANSPARENT );\r
1816     SetTextColor( hdc, chroma );\r
1817     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1818     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1819 \r
1820     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1821     /* Step 3: the area outside the piece is filled with white */\r
1822 //    FloodFill( hdc, 0, 0, chroma );\r
1823     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1824     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1825     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1826     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1827     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1828     /* \r
1829         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1830         but if the start point is not inside the piece we're lost!\r
1831         There should be a better way to do this... if we could create a region or path\r
1832         from the fill operation we would be fine for example.\r
1833     */\r
1834 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1835     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1836 \r
1837     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1838         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1839         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1840 \r
1841         SelectObject( dc2, bm2 );\r
1842         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1843         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1844         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1845         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1846         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1847 \r
1848         DeleteDC( dc2 );\r
1849         DeleteObject( bm2 );\r
1850     }\r
1851 \r
1852     SetTextColor( hdc, 0 );\r
1853     /* \r
1854         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1855         draw the piece again in black for safety.\r
1856     */\r
1857     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1858 \r
1859     SelectObject( hdc, hbm_old );\r
1860 \r
1861     if( hPieceMask[index] != NULL ) {\r
1862         DeleteObject( hPieceMask[index] );\r
1863     }\r
1864 \r
1865     hPieceMask[index] = hbm;\r
1866 \r
1867     /* Face */\r
1868     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1869 \r
1870     SelectObject( hdc, hbm );\r
1871 \r
1872     {\r
1873         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1874         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1875         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1876 \r
1877         SelectObject( dc1, hPieceMask[index] );\r
1878         SelectObject( dc2, bm2 );\r
1879         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1880         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1881         \r
1882         /* \r
1883             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1884             the piece background and deletes (makes transparent) the rest.\r
1885             Thanks to that mask, we are free to paint the background with the greates\r
1886             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1887             We use this, to make gradients and give the pieces a "roundish" look.\r
1888         */\r
1889         SetPieceBackground( hdc, backColor, 2 );\r
1890         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1891 \r
1892         DeleteDC( dc2 );\r
1893         DeleteDC( dc1 );\r
1894         DeleteObject( bm2 );\r
1895     }\r
1896 \r
1897     SetTextColor( hdc, foreColor );\r
1898     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1899 \r
1900     SelectObject( hdc, hbm_old );\r
1901 \r
1902     if( hPieceFace[index] != NULL ) {\r
1903         DeleteObject( hPieceFace[index] );\r
1904     }\r
1905 \r
1906     hPieceFace[index] = hbm;\r
1907 }\r
1908 \r
1909 static int TranslatePieceToFontPiece( int piece )\r
1910 {\r
1911     switch( piece ) {\r
1912     case BlackPawn:\r
1913         return PM_BP;\r
1914     case BlackKnight:\r
1915         return PM_BN;\r
1916     case BlackBishop:\r
1917         return PM_BB;\r
1918     case BlackRook:\r
1919         return PM_BR;\r
1920     case BlackQueen:\r
1921         return PM_BQ;\r
1922     case BlackKing:\r
1923         return PM_BK;\r
1924     case WhitePawn:\r
1925         return PM_WP;\r
1926     case WhiteKnight:\r
1927         return PM_WN;\r
1928     case WhiteBishop:\r
1929         return PM_WB;\r
1930     case WhiteRook:\r
1931         return PM_WR;\r
1932     case WhiteQueen:\r
1933         return PM_WQ;\r
1934     case WhiteKing:\r
1935         return PM_WK;\r
1936 \r
1937     case BlackAngel:\r
1938         return PM_BA;\r
1939     case BlackMarshall:\r
1940         return PM_BC;\r
1941     case BlackFerz:\r
1942         return PM_BF;\r
1943     case BlackNightrider:\r
1944         return PM_BH;\r
1945     case BlackAlfil:\r
1946         return PM_BE;\r
1947     case BlackWazir:\r
1948         return PM_BW;\r
1949     case BlackUnicorn:\r
1950         return PM_BU;\r
1951     case BlackCannon:\r
1952         return PM_BO;\r
1953     case BlackGrasshopper:\r
1954         return PM_BG;\r
1955     case BlackMan:\r
1956         return PM_BM;\r
1957     case BlackSilver:\r
1958         return PM_BSG;\r
1959     case BlackLance:\r
1960         return PM_BL;\r
1961     case BlackFalcon:\r
1962         return PM_BV;\r
1963     case BlackCobra:\r
1964         return PM_BS;\r
1965     case BlackCardinal:\r
1966         return PM_BAB;\r
1967     case BlackDragon:\r
1968         return PM_BD;\r
1969 \r
1970     case WhiteAngel:\r
1971         return PM_WA;\r
1972     case WhiteMarshall:\r
1973         return PM_WC;\r
1974     case WhiteFerz:\r
1975         return PM_WF;\r
1976     case WhiteNightrider:\r
1977         return PM_WH;\r
1978     case WhiteAlfil:\r
1979         return PM_WE;\r
1980     case WhiteWazir:\r
1981         return PM_WW;\r
1982     case WhiteUnicorn:\r
1983         return PM_WU;\r
1984     case WhiteCannon:\r
1985         return PM_WO;\r
1986     case WhiteGrasshopper:\r
1987         return PM_WG;\r
1988     case WhiteMan:\r
1989         return PM_WM;\r
1990     case WhiteSilver:\r
1991         return PM_WSG;\r
1992     case WhiteLance:\r
1993         return PM_WL;\r
1994     case WhiteFalcon:\r
1995         return PM_WV;\r
1996     case WhiteCobra:\r
1997         return PM_WS;\r
1998     case WhiteCardinal:\r
1999         return PM_WAB;\r
2000     case WhiteDragon:\r
2001         return PM_WD;\r
2002     }\r
2003 \r
2004     return 0;\r
2005 }\r
2006 \r
2007 void CreatePiecesFromFont()\r
2008 {\r
2009     LOGFONT lf;\r
2010     HDC hdc_window = NULL;\r
2011     HDC hdc = NULL;\r
2012     HFONT hfont_old;\r
2013     int fontHeight;\r
2014     int i;\r
2015 \r
2016     if( fontBitmapSquareSize < 0 ) {\r
2017         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2018         return;\r
2019     }\r
2020 \r
2021     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2022             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2023         fontBitmapSquareSize = -1;\r
2024         return;\r
2025     }\r
2026 \r
2027     if( fontBitmapSquareSize != squareSize ) {\r
2028         hdc_window = GetDC( hwndMain );\r
2029         hdc = CreateCompatibleDC( hdc_window );\r
2030 \r
2031         if( hPieceFont != NULL ) {\r
2032             DeleteObject( hPieceFont );\r
2033         }\r
2034         else {\r
2035             for( i=0; i<=(int)BlackKing; i++ ) {\r
2036                 hPieceMask[i] = NULL;\r
2037                 hPieceFace[i] = NULL;\r
2038             }\r
2039         }\r
2040 \r
2041         fontHeight = 75;\r
2042 \r
2043         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2044             fontHeight = appData.fontPieceSize;\r
2045         }\r
2046 \r
2047         fontHeight = (fontHeight * squareSize) / 100;\r
2048 \r
2049         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2050         lf.lfWidth = 0;\r
2051         lf.lfEscapement = 0;\r
2052         lf.lfOrientation = 0;\r
2053         lf.lfWeight = FW_NORMAL;\r
2054         lf.lfItalic = 0;\r
2055         lf.lfUnderline = 0;\r
2056         lf.lfStrikeOut = 0;\r
2057         lf.lfCharSet = DEFAULT_CHARSET;\r
2058         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2059         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2060         lf.lfQuality = PROOF_QUALITY;\r
2061         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2062         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2063         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2064 \r
2065         hPieceFont = CreateFontIndirect( &lf );\r
2066 \r
2067         if( hPieceFont == NULL ) {\r
2068             fontBitmapSquareSize = -2;\r
2069         }\r
2070         else {\r
2071             /* Setup font-to-piece character table */\r
2072             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2073                 /* No (or wrong) global settings, try to detect the font */\r
2074                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2075                     /* Alpha */\r
2076                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2077                 }\r
2078                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2079                     /* DiagramTT* family */\r
2080                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2081                 }\r
2082                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2083                     /* Fairy symbols */\r
2084                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2085                 }\r
2086                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2087                     /* Good Companion (Some characters get warped as literal :-( */\r
2088                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2089                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2090                     SetCharTable(pieceToFontChar, s);\r
2091                 }\r
2092                 else {\r
2093                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2094                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2095                 }\r
2096             }\r
2097 \r
2098             /* Create bitmaps */\r
2099             hfont_old = SelectObject( hdc, hPieceFont );\r
2100             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2101                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2102                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2103 \r
2104             SelectObject( hdc, hfont_old );\r
2105 \r
2106             fontBitmapSquareSize = squareSize;\r
2107         }\r
2108     }\r
2109 \r
2110     if( hdc != NULL ) {\r
2111         DeleteDC( hdc );\r
2112     }\r
2113 \r
2114     if( hdc_window != NULL ) {\r
2115         ReleaseDC( hwndMain, hdc_window );\r
2116     }\r
2117 }\r
2118 \r
2119 HBITMAP\r
2120 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2121 {\r
2122   char name[128], buf[MSG_SIZ];\r
2123 \r
2124     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2125   if(appData.pieceDirectory[0]) {\r
2126     HBITMAP res;\r
2127     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2128     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2129     if(res) return res;\r
2130   }\r
2131   if (gameInfo.event &&\r
2132       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2133       strcmp(name, "k80s") == 0) {\r
2134     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2135   }\r
2136   return LoadBitmap(hinst, name);\r
2137 }\r
2138 \r
2139 \r
2140 /* Insert a color into the program's logical palette\r
2141    structure.  This code assumes the given color is\r
2142    the result of the RGB or PALETTERGB macro, and it\r
2143    knows how those macros work (which is documented).\r
2144 */\r
2145 VOID\r
2146 InsertInPalette(COLORREF color)\r
2147 {\r
2148   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2149 \r
2150   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2151     DisplayFatalError(_("Too many colors"), 0, 1);\r
2152     pLogPal->palNumEntries--;\r
2153     return;\r
2154   }\r
2155 \r
2156   pe->peFlags = (char) 0;\r
2157   pe->peRed = (char) (0xFF & color);\r
2158   pe->peGreen = (char) (0xFF & (color >> 8));\r
2159   pe->peBlue = (char) (0xFF & (color >> 16));\r
2160   return;\r
2161 }\r
2162 \r
2163 \r
2164 VOID\r
2165 InitDrawingColors()\r
2166 {\r
2167   int i;\r
2168   if (pLogPal == NULL) {\r
2169     /* Allocate enough memory for a logical palette with\r
2170      * PALETTESIZE entries and set the size and version fields\r
2171      * of the logical palette structure.\r
2172      */\r
2173     pLogPal = (NPLOGPALETTE)\r
2174       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2175                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2176     pLogPal->palVersion    = 0x300;\r
2177   }\r
2178   pLogPal->palNumEntries = 0;\r
2179 \r
2180   InsertInPalette(lightSquareColor);\r
2181   InsertInPalette(darkSquareColor);\r
2182   InsertInPalette(whitePieceColor);\r
2183   InsertInPalette(blackPieceColor);\r
2184   InsertInPalette(highlightSquareColor);\r
2185   InsertInPalette(premoveHighlightColor);\r
2186 \r
2187   /*  create a logical color palette according the information\r
2188    *  in the LOGPALETTE structure.\r
2189    */\r
2190   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2191 \r
2192   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2193   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2194   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2195   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2196   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2197   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2198   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2199     for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers\r
2200 \r
2201    /* [AS] Force rendering of the font-based pieces */\r
2202   if( fontBitmapSquareSize > 0 ) {\r
2203     fontBitmapSquareSize = 0;\r
2204   }\r
2205 }\r
2206 \r
2207 \r
2208 int\r
2209 BoardWidth(int boardSize, int n)\r
2210 { /* [HGM] argument n added to allow different width and height */\r
2211   int lineGap = sizeInfo[boardSize].lineGap;\r
2212 \r
2213   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2214       lineGap = appData.overrideLineGap;\r
2215   }\r
2216 \r
2217   return (n + 1) * lineGap +\r
2218           n * sizeInfo[boardSize].squareSize;\r
2219 }\r
2220 \r
2221 /* Respond to board resize by dragging edge */\r
2222 VOID\r
2223 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2224 {\r
2225   BoardSize newSize = NUM_SIZES - 1;\r
2226   static int recurse = 0;\r
2227   if (IsIconic(hwndMain)) return;\r
2228   if (recurse > 0) return;\r
2229   recurse++;\r
2230   while (newSize > 0) {\r
2231         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2232         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2233            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2234     newSize--;\r
2235   } \r
2236   boardSize = newSize;\r
2237   InitDrawingSizes(boardSize, flags);\r
2238   recurse--;\r
2239 }\r
2240 \r
2241 \r
2242 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2243 \r
2244 VOID\r
2245 InitDrawingSizes(BoardSize boardSize, int flags)\r
2246 {\r
2247   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2248   ChessSquare piece;\r
2249   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2250   HDC hdc;\r
2251   SIZE clockSize, messageSize;\r
2252   HFONT oldFont;\r
2253   char buf[MSG_SIZ];\r
2254   char *str;\r
2255   HMENU hmenu = GetMenu(hwndMain);\r
2256   RECT crect, wrect, oldRect;\r
2257   int offby;\r
2258   LOGBRUSH logbrush;\r
2259   VariantClass v = gameInfo.variant;\r
2260 \r
2261   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2262   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2263 \r
2264   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2265   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2266   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2267   oldBoardSize = boardSize;\r
2268 \r
2269   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2270   { // correct board size to one where built-in pieces exist\r
2271     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2272        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2273       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2274       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2275       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {\r
2276       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2277       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2278                                    boardSize = SizeMiddling;\r
2279     }\r
2280   }\r
2281   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2282 \r
2283   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2284   oldRect.top = wpMain.y;\r
2285   oldRect.right = wpMain.x + wpMain.width;\r
2286   oldRect.bottom = wpMain.y + wpMain.height;\r
2287 \r
2288   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2289   smallLayout = sizeInfo[boardSize].smallLayout;\r
2290   squareSize = sizeInfo[boardSize].squareSize;\r
2291   lineGap = sizeInfo[boardSize].lineGap;\r
2292   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2293   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2294 \r
2295   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2296       lineGap = appData.overrideLineGap;\r
2297   }\r
2298 \r
2299   if (tinyLayout != oldTinyLayout) {\r
2300     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2301     if (tinyLayout) {\r
2302       style &= ~WS_SYSMENU;\r
2303       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2304                  "&Minimize\tCtrl+F4");\r
2305     } else {\r
2306       style |= WS_SYSMENU;\r
2307       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2308     }\r
2309     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2310 \r
2311     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2312       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2313         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2314     }\r
2315     DrawMenuBar(hwndMain);\r
2316   }\r
2317 \r
2318   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2319   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2320 \r
2321   /* Get text area sizes */\r
2322   hdc = GetDC(hwndMain);\r
2323   if (appData.clockMode) {\r
2324     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2325   } else {\r
2326     snprintf(buf, MSG_SIZ, _("White"));\r
2327   }\r
2328   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2329   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2330   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2331   str = _("We only care about the height here");\r
2332   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2333   SelectObject(hdc, oldFont);\r
2334   ReleaseDC(hwndMain, hdc);\r
2335 \r
2336   /* Compute where everything goes */\r
2337   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2338         /* [HGM] logo: if either logo is on, reserve space for it */\r
2339         logoHeight =  2*clockSize.cy;\r
2340         leftLogoRect.left   = OUTER_MARGIN;\r
2341         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2342         leftLogoRect.top    = OUTER_MARGIN;\r
2343         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2344 \r
2345         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2346         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2347         rightLogoRect.top    = OUTER_MARGIN;\r
2348         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2349 \r
2350 \r
2351     whiteRect.left = leftLogoRect.right;\r
2352     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2353     whiteRect.top = OUTER_MARGIN;\r
2354     whiteRect.bottom = whiteRect.top + logoHeight;\r
2355 \r
2356     blackRect.right = rightLogoRect.left;\r
2357     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2358     blackRect.top = whiteRect.top;\r
2359     blackRect.bottom = whiteRect.bottom;\r
2360   } else {\r
2361     whiteRect.left = OUTER_MARGIN;\r
2362     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2363     whiteRect.top = OUTER_MARGIN;\r
2364     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2365 \r
2366     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2367     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2368     blackRect.top = whiteRect.top;\r
2369     blackRect.bottom = whiteRect.bottom;\r
2370 \r
2371     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2372   }\r
2373 \r
2374   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2375   if (appData.showButtonBar) {\r
2376     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2377       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2378   } else {\r
2379     messageRect.right = OUTER_MARGIN + boardWidth;\r
2380   }\r
2381   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2382   messageRect.bottom = messageRect.top + messageSize.cy;\r
2383 \r
2384   boardRect.left = OUTER_MARGIN;\r
2385   boardRect.right = boardRect.left + boardWidth;\r
2386   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2387   boardRect.bottom = boardRect.top + boardHeight;\r
2388 \r
2389   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2390   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2391   oldTinyLayout = tinyLayout;\r
2392   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2393   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2394     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2395   winW *= 1 + twoBoards;\r
2396   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2397   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2398   wpMain.height = winH; //       without disturbing window attachments\r
2399   GetWindowRect(hwndMain, &wrect);\r
2400   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2401                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2402 \r
2403   // [HGM] placement: let attached windows follow size change.\r
2404   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2405   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2406   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2407   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2408   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2409 \r
2410   /* compensate if menu bar wrapped */\r
2411   GetClientRect(hwndMain, &crect);\r
2412   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2413   wpMain.height += offby;\r
2414   switch (flags) {\r
2415   case WMSZ_TOPLEFT:\r
2416     SetWindowPos(hwndMain, NULL, \r
2417                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2418                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2419     break;\r
2420 \r
2421   case WMSZ_TOPRIGHT:\r
2422   case WMSZ_TOP:\r
2423     SetWindowPos(hwndMain, NULL, \r
2424                  wrect.left, wrect.bottom - wpMain.height, \r
2425                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2426     break;\r
2427 \r
2428   case WMSZ_BOTTOMLEFT:\r
2429   case WMSZ_LEFT:\r
2430     SetWindowPos(hwndMain, NULL, \r
2431                  wrect.right - wpMain.width, wrect.top, \r
2432                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2433     break;\r
2434 \r
2435   case WMSZ_BOTTOMRIGHT:\r
2436   case WMSZ_BOTTOM:\r
2437   case WMSZ_RIGHT:\r
2438   default:\r
2439     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2440                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2441     break;\r
2442   }\r
2443 \r
2444   hwndPause = NULL;\r
2445   for (i = 0; i < N_BUTTONS; i++) {\r
2446     if (buttonDesc[i].hwnd != NULL) {\r
2447       DestroyWindow(buttonDesc[i].hwnd);\r
2448       buttonDesc[i].hwnd = NULL;\r
2449     }\r
2450     if (appData.showButtonBar) {\r
2451       buttonDesc[i].hwnd =\r
2452         CreateWindow("BUTTON", buttonDesc[i].label,\r
2453                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2454                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2455                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2456                      (HMENU) buttonDesc[i].id,\r
2457                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2458       if (tinyLayout) {\r
2459         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2460                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2461                     MAKELPARAM(FALSE, 0));\r
2462       }\r
2463       if (buttonDesc[i].id == IDM_Pause)\r
2464         hwndPause = buttonDesc[i].hwnd;\r
2465       buttonDesc[i].wndproc = (WNDPROC)\r
2466         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2467     }\r
2468   }\r
2469   if (gridPen != NULL) DeleteObject(gridPen);\r
2470   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2471   if (premovePen != NULL) DeleteObject(premovePen);\r
2472   if (lineGap != 0) {\r
2473     logbrush.lbStyle = BS_SOLID;\r
2474     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2475     gridPen =\r
2476       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2477                    lineGap, &logbrush, 0, NULL);\r
2478     logbrush.lbColor = highlightSquareColor;\r
2479     highlightPen =\r
2480       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2481                    lineGap, &logbrush, 0, NULL);\r
2482 \r
2483     logbrush.lbColor = premoveHighlightColor; \r
2484     premovePen =\r
2485       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2486                    lineGap, &logbrush, 0, NULL);\r
2487 \r
2488     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2489     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2490       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2491       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2492         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2493       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2494         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2495       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2496     }\r
2497     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2498       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2499       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2500         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2501         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2502       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2503         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2504       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2505     }\r
2506   }\r
2507 \r
2508   /* [HGM] Licensing requirement */\r
2509 #ifdef GOTHIC\r
2510   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2511 #endif\r
2512 #ifdef FALCON\r
2513   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2514 #endif\r
2515   GothicPopUp( "", VariantNormal);\r
2516 \r
2517 \r
2518 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2519 \r
2520   /* Load piece bitmaps for this board size */\r
2521   for (i=0; i<=2; i++) {\r
2522     for (piece = WhitePawn;\r
2523          (int) piece < (int) BlackPawn;\r
2524          piece = (ChessSquare) ((int) piece + 1)) {\r
2525       if (pieceBitmap[i][piece] != NULL)\r
2526         DeleteObject(pieceBitmap[i][piece]);\r
2527     }\r
2528   }\r
2529 \r
2530   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2531   // Orthodox Chess pieces\r
2532   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2533   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2534   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2535   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2536   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2537   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2538   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2539   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2540   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2541   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2542   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2543   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2544   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2545   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2546   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2547   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2548     // in Shogi, Hijack the unused Queen for Lance\r
2549     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2550     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2551     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2552   } else {\r
2553     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2554     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2555     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2556   }\r
2557 \r
2558   if(squareSize <= 72 && squareSize >= 33) { \r
2559     /* A & C are available in most sizes now */\r
2560     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2561       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2562       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2563       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2564       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2565       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2566       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2567       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2568       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2569       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2570       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2571       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2572       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2573     } else { // Smirf-like\r
2574       if(gameInfo.variant == VariantSChess) {\r
2575         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2576         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2577         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2578       } else {\r
2579         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2580         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2581         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2582       }\r
2583     }\r
2584     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2585       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2586       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2587       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2588     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2589       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2590       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2591       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2592     } else { // WinBoard standard\r
2593       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2594       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2595       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2596     }\r
2597   }\r
2598 \r
2599 \r
2600   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2601     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2602     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2603     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2604     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2605     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2606     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2607     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2608     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2609     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2610     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2611     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2612     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2613     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2614     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2615     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2616     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2617     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2618     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2619     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2620     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2621     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2622     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2623     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2624     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2625     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2626     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2627     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2628     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2629     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2630     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2631     pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2632     pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2633     pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2634 \r
2635     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2636       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2637       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2638       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2639       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2640       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2641       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2642       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2643       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2644       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2645       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2646       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2647       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2648     } else {\r
2649       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2650       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2651       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2652       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2653       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2654       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2655       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2656       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2657       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2658       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2659       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2660       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2661     }\r
2662 \r
2663   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2664     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2665     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2666     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2667     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2668     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2669     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2670     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2671     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2672     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2673     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2674     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2675     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2676     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2677     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2678   }\r
2679 \r
2680 \r
2681   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2682   /* special Shogi support in this size */\r
2683   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2684       for (piece = WhitePawn;\r
2685            (int) piece < (int) BlackPawn;\r
2686            piece = (ChessSquare) ((int) piece + 1)) {\r
2687         if (pieceBitmap[i][piece] != NULL)\r
2688           DeleteObject(pieceBitmap[i][piece]);\r
2689       }\r
2690     }\r
2691   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2692   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2693   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2694   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2695   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2696   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2697   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2698   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2699   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2700   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2701   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2702   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2703   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2704   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2705   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2706   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2707   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2708   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2709   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2710   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2711   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2712   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2713   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2714   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2715   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2716   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2717   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2718   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2719   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2720   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2721   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2722   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2723   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2724   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2725   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2726   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2727   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2728   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2729   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2730   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2731   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2732   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2733   minorSize = 0;\r
2734   }\r
2735 }\r
2736 \r
2737 HBITMAP\r
2738 PieceBitmap(ChessSquare p, int kind)\r
2739 {\r
2740   if ((int) p >= (int) BlackPawn)\r
2741     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2742 \r
2743   return pieceBitmap[kind][(int) p];\r
2744 }\r
2745 \r
2746 /***************************************************************/\r
2747 \r
2748 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2749 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2750 /*\r
2751 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2752 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2753 */\r
2754 \r
2755 VOID\r
2756 SquareToPos(int row, int column, int * x, int * y)\r
2757 {\r
2758   if (flipView) {\r
2759     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2760     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2761   } else {\r
2762     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2763     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2764   }\r
2765 }\r
2766 \r
2767 VOID\r
2768 DrawCoordsOnDC(HDC hdc)\r
2769 {\r
2770   static char files[] = "0123456789012345678901221098765432109876543210";\r
2771   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2772   char str[2] = { NULLCHAR, NULLCHAR };\r
2773   int oldMode, oldAlign, x, y, start, i;\r
2774   HFONT oldFont;\r
2775   HBRUSH oldBrush;\r
2776 \r
2777   if (!appData.showCoords)\r
2778     return;\r
2779 \r
2780   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2781 \r
2782   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2783   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2784   oldAlign = GetTextAlign(hdc);\r
2785   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2786 \r
2787   y = boardRect.top + lineGap;\r
2788   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2789 \r
2790   if(border) {\r
2791     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2792     x += border - lineGap - 4; y += squareSize - 6;\r
2793   } else\r
2794   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2795   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2796     str[0] = files[start + i];\r
2797     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2798     y += squareSize + lineGap;\r
2799   }\r
2800 \r
2801   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2802 \r
2803   if(border) {\r
2804     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2805     x += -border + 4; y += border - squareSize + 6;\r
2806   } else\r
2807   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2808   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2809     str[0] = ranks[start + i];\r
2810     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2811     x += squareSize + lineGap;\r
2812   }    \r
2813 \r
2814   SelectObject(hdc, oldBrush);\r
2815   SetBkMode(hdc, oldMode);\r
2816   SetTextAlign(hdc, oldAlign);\r
2817   SelectObject(hdc, oldFont);\r
2818 }\r
2819 \r
2820 VOID\r
2821 DrawGridOnDC(HDC hdc)\r
2822 {\r
2823   HPEN oldPen;\r
2824  \r
2825   if (lineGap != 0) {\r
2826     oldPen = SelectObject(hdc, gridPen);\r
2827     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2828     SelectObject(hdc, oldPen);\r
2829   }\r
2830 }\r
2831 \r
2832 #define HIGHLIGHT_PEN 0\r
2833 #define PREMOVE_PEN   1\r
2834 \r
2835 VOID\r
2836 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2837 {\r
2838   int x1, y1;\r
2839   HPEN oldPen, hPen;\r
2840   if (lineGap == 0) return;\r
2841   if (flipView) {\r
2842     x1 = boardRect.left +\r
2843       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2844     y1 = boardRect.top +\r
2845       lineGap/2 + y * (squareSize + lineGap) + border;\r
2846   } else {\r
2847     x1 = boardRect.left +\r
2848       lineGap/2 + x * (squareSize + lineGap) + border;\r
2849     y1 = boardRect.top +\r
2850       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2851   }\r
2852   hPen = pen ? premovePen : highlightPen;\r
2853   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2854   MoveToEx(hdc, x1, y1, NULL);\r
2855   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2856   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2857   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2858   LineTo(hdc, x1, y1);\r
2859   SelectObject(hdc, oldPen);\r
2860 }\r
2861 \r
2862 VOID\r
2863 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2864 {\r
2865   int i;\r
2866   for (i=0; i<2; i++) {\r
2867     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2868       DrawHighlightOnDC(hdc, TRUE,\r
2869                         h->sq[i].x, h->sq[i].y,\r
2870                         pen);\r
2871   }\r
2872 }\r
2873 \r
2874 /* Note: sqcolor is used only in monoMode */\r
2875 /* Note that this code is largely duplicated in woptions.c,\r
2876    function DrawSampleSquare, so that needs to be updated too */\r
2877 VOID\r
2878 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2879 {\r
2880   HBITMAP oldBitmap;\r
2881   HBRUSH oldBrush;\r
2882   int tmpSize;\r
2883 \r
2884   if (appData.blindfold) return;\r
2885 \r
2886   /* [AS] Use font-based pieces if needed */\r
2887   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2888     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2889     CreatePiecesFromFont();\r
2890 \r
2891     if( fontBitmapSquareSize == squareSize ) {\r
2892         int index = TranslatePieceToFontPiece(piece);\r
2893 \r
2894         SelectObject( tmphdc, hPieceMask[ index ] );\r
2895 \r
2896       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2897         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2898       else\r
2899         BitBlt( hdc,\r
2900             x, y,\r
2901             squareSize, squareSize,\r
2902             tmphdc,\r
2903             0, 0,\r
2904             SRCAND );\r
2905 \r
2906         SelectObject( tmphdc, hPieceFace[ index ] );\r
2907 \r
2908       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2909         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2910       else\r
2911         BitBlt( hdc,\r
2912             x, y,\r
2913             squareSize, squareSize,\r
2914             tmphdc,\r
2915             0, 0,\r
2916             SRCPAINT );\r
2917 \r
2918         return;\r
2919     }\r
2920   }\r
2921 \r
2922   if (appData.monoMode) {\r
2923     SelectObject(tmphdc, PieceBitmap(piece, \r
2924       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2925     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2926            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2927   } else {\r
2928     HBRUSH xBrush = whitePieceBrush;\r
2929     tmpSize = squareSize;\r
2930     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2931     if(minorSize &&\r
2932         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2933          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2934       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2935       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2936       x += (squareSize - minorSize)>>1;\r
2937       y += squareSize - minorSize - 2;\r
2938       tmpSize = minorSize;\r
2939     }\r
2940     if (color || appData.allWhite ) {\r
2941       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2942       if( color )\r
2943               oldBrush = SelectObject(hdc, xBrush);\r
2944       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2945       if(appData.upsideDown && color==flipView)\r
2946         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2947       else\r
2948         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2949       /* Use black for outline of white pieces */\r
2950       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2951       if(appData.upsideDown && color==flipView)\r
2952         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2953       else\r
2954         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2955     } else if(appData.pieceDirectory[0]) {\r
2956       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2957       oldBrush = SelectObject(hdc, xBrush);\r
2958       if(appData.upsideDown && color==flipView)\r
2959         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2960       else\r
2961         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2962       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2963       if(appData.upsideDown && color==flipView)\r
2964         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2965       else\r
2966         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2967     } else {\r
2968       /* Use square color for details of black pieces */\r
2969       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2970       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2971       if(appData.upsideDown && !flipView)\r
2972         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2973       else\r
2974         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2975     }\r
2976     SelectObject(hdc, oldBrush);\r
2977     SelectObject(tmphdc, oldBitmap);\r
2978   }\r
2979 }\r
2980 \r
2981 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2982 int GetBackTextureMode( int algo )\r
2983 {\r
2984     int result = BACK_TEXTURE_MODE_DISABLED;\r
2985 \r
2986     switch( algo ) \r
2987     {\r
2988         case BACK_TEXTURE_MODE_PLAIN:\r
2989             result = 1; /* Always use identity map */\r
2990             break;\r
2991         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2992             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2993             break;\r
2994     }\r
2995 \r
2996     return result;\r
2997 }\r
2998 \r
2999 /* \r
3000     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3001     to handle redraws cleanly (as random numbers would always be different).\r
3002 */\r
3003 VOID RebuildTextureSquareInfo()\r
3004 {\r
3005     BITMAP bi;\r
3006     int lite_w = 0;\r
3007     int lite_h = 0;\r
3008     int dark_w = 0;\r
3009     int dark_h = 0;\r
3010     int row;\r
3011     int col;\r
3012 \r
3013     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3014 \r
3015     if( liteBackTexture != NULL ) {\r
3016         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3017             lite_w = bi.bmWidth;\r
3018             lite_h = bi.bmHeight;\r
3019         }\r
3020     }\r
3021 \r
3022     if( darkBackTexture != NULL ) {\r
3023         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3024             dark_w = bi.bmWidth;\r
3025             dark_h = bi.bmHeight;\r
3026         }\r
3027     }\r
3028 \r
3029     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3030         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3031             if( (col + row) & 1 ) {\r
3032                 /* Lite square */\r
3033                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3034                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3035                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3036                   else\r
3037                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3038                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3039                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3040                   else\r
3041                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3042                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3043                 }\r
3044             }\r
3045             else {\r
3046                 /* Dark square */\r
3047                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3048                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3049                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3050                   else\r
3051                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3052                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3053                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3054                   else\r
3055                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3056                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3057                 }\r
3058             }\r
3059         }\r
3060     }\r
3061 }\r
3062 \r
3063 /* [AS] Arrow highlighting support */\r
3064 \r
3065 static double A_WIDTH = 5; /* Width of arrow body */\r
3066 \r
3067 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3068 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3069 \r
3070 static double Sqr( double x )\r
3071 {\r
3072     return x*x;\r
3073 }\r
3074 \r
3075 static int Round( double x )\r
3076 {\r
3077     return (int) (x + 0.5);\r
3078 }\r
3079 \r
3080 /* Draw an arrow between two points using current settings */\r
3081 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3082 {\r
3083     POINT arrow[7];\r
3084     double dx, dy, j, k, x, y;\r
3085 \r
3086     if( d_x == s_x ) {\r
3087         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3088 \r
3089         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3090         arrow[0].y = s_y;\r
3091 \r
3092         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3093         arrow[1].y = d_y - h;\r
3094 \r
3095         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3096         arrow[2].y = d_y - h;\r
3097 \r
3098         arrow[3].x = d_x;\r
3099         arrow[3].y = d_y;\r
3100 \r
3101         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3102         arrow[5].y = d_y - h;\r
3103 \r
3104         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3105         arrow[4].y = d_y - h;\r
3106 \r
3107         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3108         arrow[6].y = s_y;\r
3109     }\r
3110     else if( d_y == s_y ) {\r
3111         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3112 \r
3113         arrow[0].x = s_x;\r
3114         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3115 \r
3116         arrow[1].x = d_x - w;\r
3117         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3118 \r
3119         arrow[2].x = d_x - w;\r
3120         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3121 \r
3122         arrow[3].x = d_x;\r
3123         arrow[3].y = d_y;\r
3124 \r
3125         arrow[5].x = d_x - w;\r
3126         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3127 \r
3128         arrow[4].x = d_x - w;\r
3129         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3130 \r
3131         arrow[6].x = s_x;\r
3132         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3133     }\r
3134     else {\r
3135         /* [AS] Needed a lot of paper for this! :-) */\r
3136         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3137         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3138   \r
3139         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3140 \r
3141         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3142 \r
3143         x = s_x;\r
3144         y = s_y;\r
3145 \r
3146         arrow[0].x = Round(x - j);\r
3147         arrow[0].y = Round(y + j*dx);\r
3148 \r
3149         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3150         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3151 \r
3152         if( d_x > s_x ) {\r
3153             x = (double) d_x - k;\r
3154             y = (double) d_y - k*dy;\r
3155         }\r
3156         else {\r
3157             x = (double) d_x + k;\r
3158             y = (double) d_y + k*dy;\r
3159         }\r
3160 \r
3161         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3162 \r
3163         arrow[6].x = Round(x - j);\r
3164         arrow[6].y = Round(y + j*dx);\r
3165 \r
3166         arrow[2].x = Round(arrow[6].x + 2*j);\r
3167         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3168 \r
3169         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3170         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3171 \r
3172         arrow[4].x = d_x;\r
3173         arrow[4].y = d_y;\r
3174 \r
3175         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3176         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3177     }\r
3178 \r
3179     Polygon( hdc, arrow, 7 );\r
3180 }\r
3181 \r
3182 /* [AS] Draw an arrow between two squares */\r
3183 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3184 {\r
3185     int s_x, s_y, d_x, d_y;\r
3186     HPEN hpen;\r
3187     HPEN holdpen;\r
3188     HBRUSH hbrush;\r
3189     HBRUSH holdbrush;\r
3190     LOGBRUSH stLB;\r
3191 \r
3192     if( s_col == d_col && s_row == d_row ) {\r
3193         return;\r
3194     }\r
3195 \r
3196     /* Get source and destination points */\r
3197     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3198     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3199 \r
3200     if( d_y > s_y ) {\r
3201         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3202     }\r
3203     else if( d_y < s_y ) {\r
3204         d_y += squareSize / 2 + squareSize / 4;\r
3205     }\r
3206     else {\r
3207         d_y += squareSize / 2;\r
3208     }\r
3209 \r
3210     if( d_x > s_x ) {\r
3211         d_x += squareSize / 2 - squareSize / 4;\r
3212     }\r
3213     else if( d_x < s_x ) {\r
3214         d_x += squareSize / 2 + squareSize / 4;\r
3215     }\r
3216     else {\r
3217         d_x += squareSize / 2;\r
3218     }\r
3219 \r
3220     s_x += squareSize / 2;\r
3221     s_y += squareSize / 2;\r
3222 \r
3223     /* Adjust width */\r
3224     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3225 \r
3226     /* Draw */\r
3227     stLB.lbStyle = BS_SOLID;\r
3228     stLB.lbColor = appData.highlightArrowColor;\r
3229     stLB.lbHatch = 0;\r
3230 \r
3231     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3232     holdpen = SelectObject( hdc, hpen );\r
3233     hbrush = CreateBrushIndirect( &stLB );\r
3234     holdbrush = SelectObject( hdc, hbrush );\r
3235 \r
3236     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3237 \r
3238     SelectObject( hdc, holdpen );\r
3239     SelectObject( hdc, holdbrush );\r
3240     DeleteObject( hpen );\r
3241     DeleteObject( hbrush );\r
3242 }\r
3243 \r
3244 BOOL HasHighlightInfo()\r
3245 {\r
3246     BOOL result = FALSE;\r
3247 \r
3248     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3249         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3250     {\r
3251         result = TRUE;\r
3252     }\r
3253 \r
3254     return result;\r
3255 \r
3256 \r
3257 \r
3258 }\r
3259 \r
3260 BOOL IsDrawArrowEnabled()\r
3261 {\r
3262     BOOL result = FALSE;\r
3263 \r
3264     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3265         result = TRUE;\r
3266     }\r
3267 \r
3268     return result;\r
3269 }\r
3270 \r
3271 VOID DrawArrowHighlight( HDC hdc )\r
3272 {\r
3273     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3274         DrawArrowBetweenSquares( hdc,\r
3275             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3276             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3277     }\r
3278 }\r
3279 \r
3280 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3281 {\r
3282     HRGN result = NULL;\r
3283 \r
3284     if( HasHighlightInfo() ) {\r
3285         int x1, y1, x2, y2;\r
3286         int sx, sy, dx, dy;\r
3287 \r
3288         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3289         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3290 \r
3291         sx = MIN( x1, x2 );\r
3292         sy = MIN( y1, y2 );\r
3293         dx = MAX( x1, x2 ) + squareSize;\r
3294         dy = MAX( y1, y2 ) + squareSize;\r
3295 \r
3296         result = CreateRectRgn( sx, sy, dx, dy );\r
3297     }\r
3298 \r
3299     return result;\r
3300 }\r
3301 \r
3302 /*\r
3303     Warning: this function modifies the behavior of several other functions. \r
3304     \r
3305     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3306     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3307     repaint is scattered all over the place, which is not good for features such as\r
3308     "arrow highlighting" that require a full repaint of the board.\r
3309 \r
3310     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3311     user interaction, when speed is not so important) but especially to avoid errors\r
3312     in the displayed graphics.\r
3313 \r
3314     In such patched places, I always try refer to this function so there is a single\r
3315     place to maintain knowledge.\r
3316     \r
3317     To restore the original behavior, just return FALSE unconditionally.\r
3318 */\r
3319 BOOL IsFullRepaintPreferrable()\r
3320 {\r
3321     BOOL result = FALSE;\r
3322 \r
3323     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3324         /* Arrow may appear on the board */\r
3325         result = TRUE;\r
3326     }\r
3327 \r
3328     return result;\r
3329 }\r
3330 \r
3331 /* \r
3332     This function is called by DrawPosition to know whether a full repaint must\r
3333     be forced or not.\r
3334 \r
3335     Only DrawPosition may directly call this function, which makes use of \r
3336     some state information. Other function should call DrawPosition specifying \r
3337     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3338 */\r
3339 BOOL DrawPositionNeedsFullRepaint()\r
3340 {\r
3341     BOOL result = FALSE;\r
3342 \r
3343     /* \r
3344         Probably a slightly better policy would be to trigger a full repaint\r
3345         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3346         but animation is fast enough that it's difficult to notice.\r
3347     */\r
3348     if( animInfo.piece == EmptySquare ) {\r
3349         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3350             result = TRUE;\r
3351         }\r
3352     }\r
3353 \r
3354     return result;\r
3355 }\r
3356 \r
3357 static HBITMAP borderBitmap;\r
3358 \r
3359 VOID\r
3360 DrawBackgroundOnDC(HDC hdc)\r
3361 {\r
3362   \r
3363   BITMAP bi;\r
3364   HDC tmphdc;\r
3365   HBITMAP hbm;\r
3366   static char oldBorder[MSG_SIZ];\r
3367   int w = 600, h = 600, mode;\r
3368 \r
3369   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3370     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3371     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3372   }\r
3373   if(borderBitmap == NULL) { // loading failed, use white\r
3374     FillRect( hdc, &boardRect, whitePieceBrush );\r
3375     return;\r
3376   }\r
3377   tmphdc = CreateCompatibleDC(hdc);\r
3378   hbm = SelectObject(tmphdc, borderBitmap);\r
3379   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3380             w = bi.bmWidth;\r
3381             h = bi.bmHeight;\r
3382   }\r
3383   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3384   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3385                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3386   SetStretchBltMode(hdc, mode);\r
3387   SelectObject(tmphdc, hbm);\r
3388   DeleteDC(tmphdc);\r
3389 }\r
3390 \r
3391 VOID\r
3392 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3393 {\r
3394   int row, column, x, y, square_color, piece_color;\r
3395   ChessSquare piece;\r
3396   HBRUSH oldBrush;\r
3397   HDC texture_hdc = NULL;\r
3398 \r
3399   /* [AS] Initialize background textures if needed */\r
3400   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3401       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3402       if( backTextureSquareSize != squareSize \r
3403        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3404           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3405           backTextureSquareSize = squareSize;\r
3406           RebuildTextureSquareInfo();\r
3407       }\r
3408 \r
3409       texture_hdc = CreateCompatibleDC( hdc );\r
3410   }\r
3411 \r
3412   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3413     for (column = 0; column < BOARD_WIDTH; column++) {\r
3414   \r
3415       SquareToPos(row, column, &x, &y);\r
3416 \r
3417       piece = board[row][column];\r
3418 \r
3419       square_color = ((column + row) % 2) == 1;\r
3420       if( gameInfo.variant == VariantXiangqi ) {\r
3421           square_color = !InPalace(row, column);\r
3422           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3423           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3424       }\r
3425       piece_color = (int) piece < (int) BlackPawn;\r
3426 \r
3427 \r
3428       /* [HGM] holdings file: light square or black */\r
3429       if(column == BOARD_LEFT-2) {\r
3430             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3431                 square_color = 1;\r
3432             else {\r
3433                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3434                 continue;\r
3435             }\r
3436       } else\r
3437       if(column == BOARD_RGHT + 1 ) {\r
3438             if( row < gameInfo.holdingsSize )\r
3439                 square_color = 1;\r
3440             else {\r
3441                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3442                 continue;\r
3443             }\r
3444       }\r
3445       if(column == BOARD_LEFT-1 ) /* left align */\r
3446             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3447       else if( column == BOARD_RGHT) /* right align */\r
3448             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3449       else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);\r
3450       else\r
3451       if (appData.monoMode) {\r
3452         if (piece == EmptySquare) {\r
3453           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3454                  square_color ? WHITENESS : BLACKNESS);\r
3455         } else {\r
3456           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3457         }\r
3458       } \r
3459       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3460           /* [AS] Draw the square using a texture bitmap */\r
3461           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3462           int r = row, c = column; // [HGM] do not flip board in flipView\r
3463           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3464 \r
3465           DrawTile( x, y, \r
3466               squareSize, squareSize, \r
3467               hdc, \r
3468               texture_hdc,\r
3469               backTextureSquareInfo[r][c].mode,\r
3470               backTextureSquareInfo[r][c].x,\r
3471               backTextureSquareInfo[r][c].y );\r
3472 \r
3473           SelectObject( texture_hdc, hbm );\r
3474 \r
3475           if (piece != EmptySquare) {\r
3476               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3477           }\r
3478       }\r
3479       else {\r
3480         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3481 \r
3482         oldBrush = SelectObject(hdc, brush );\r
3483         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3484         SelectObject(hdc, oldBrush);\r
3485         if (piece != EmptySquare)\r
3486           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3487       }\r
3488     }\r
3489   }\r
3490 \r
3491   if( texture_hdc != NULL ) {\r
3492     DeleteDC( texture_hdc );\r
3493   }\r
3494 }\r
3495 \r
3496 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3497 void fputDW(FILE *f, int x)\r
3498 {\r
3499         fputc(x     & 255, f);\r
3500         fputc(x>>8  & 255, f);\r
3501         fputc(x>>16 & 255, f);\r
3502         fputc(x>>24 & 255, f);\r
3503 }\r
3504 \r
3505 #define MAX_CLIPS 200   /* more than enough */\r
3506 \r
3507 VOID\r
3508 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3509 {\r
3510 //  HBITMAP bufferBitmap;\r
3511   BITMAP bi;\r
3512 //  RECT Rect;\r
3513   HDC tmphdc;\r
3514   HBITMAP hbm;\r
3515   int w = 100, h = 50;\r
3516 \r
3517   if(logo == NULL) {\r
3518     if(!logoHeight) return;\r
3519     FillRect( hdc, &logoRect, whitePieceBrush );\r
3520   }\r
3521 //  GetClientRect(hwndMain, &Rect);\r
3522 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3523 //                                      Rect.bottom-Rect.top+1);\r
3524   tmphdc = CreateCompatibleDC(hdc);\r
3525   hbm = SelectObject(tmphdc, logo);\r
3526   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3527             w = bi.bmWidth;\r
3528             h = bi.bmHeight;\r
3529   }\r
3530   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3531                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3532   SelectObject(tmphdc, hbm);\r
3533   DeleteDC(tmphdc);\r
3534 }\r
3535 \r
3536 VOID\r
3537 DisplayLogos()\r
3538 {\r
3539   if(logoHeight) {\r
3540         HDC hdc = GetDC(hwndMain);\r
3541         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3542         if(appData.autoLogo) {\r
3543           \r
3544           switch(gameMode) { // pick logos based on game mode\r
3545             case IcsObserving:\r
3546                 whiteLogo = second.programLogo; // ICS logo\r
3547                 blackLogo = second.programLogo;\r
3548             default:\r
3549                 break;\r
3550             case IcsPlayingWhite:\r
3551                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3552                 blackLogo = second.programLogo; // ICS logo\r
3553                 break;\r
3554             case IcsPlayingBlack:\r
3555                 whiteLogo = second.programLogo; // ICS logo\r
3556                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3557                 break;\r
3558             case TwoMachinesPlay:\r
3559                 if(first.twoMachinesColor[0] == 'b') {\r
3560                     whiteLogo = second.programLogo;\r
3561                     blackLogo = first.programLogo;\r
3562                 }\r
3563                 break;\r
3564             case MachinePlaysWhite:\r
3565                 blackLogo = userLogo;\r
3566                 break;\r
3567             case MachinePlaysBlack:\r
3568                 whiteLogo = userLogo;\r
3569                 blackLogo = first.programLogo;\r
3570           }\r
3571         }\r
3572         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3573         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3574         ReleaseDC(hwndMain, hdc);\r
3575   }\r
3576 }\r
3577 \r
3578 void\r
3579 UpdateLogos(int display)\r
3580 { // called after loading new engine(s), in tourney or from menu\r
3581   LoadLogo(&first, 0, FALSE);\r
3582   LoadLogo(&second, 1, appData.icsActive);\r
3583   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3584   if(display) DisplayLogos();\r
3585 }\r
3586 \r
3587 static HDC hdcSeek;\r
3588 \r
3589 // [HGM] seekgraph\r
3590 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3591 {\r
3592     POINT stPt;\r
3593     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3594     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3595     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3596     SelectObject( hdcSeek, hp );\r
3597 }\r
3598 \r
3599 // front-end wrapper for drawing functions to do rectangles\r
3600 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3601 {\r
3602     HPEN hp;\r
3603     RECT rc;\r
3604 \r
3605     if (hdcSeek == NULL) {\r
3606     hdcSeek = GetDC(hwndMain);\r
3607       if (!appData.monoMode) {\r
3608         SelectPalette(hdcSeek, hPal, FALSE);\r
3609         RealizePalette(hdcSeek);\r
3610       }\r
3611     }\r
3612     hp = SelectObject( hdcSeek, gridPen );\r
3613     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3614     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3615     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3616     SelectObject( hdcSeek, hp );\r
3617 }\r
3618 \r
3619 // front-end wrapper for putting text in graph\r
3620 void DrawSeekText(char *buf, int x, int y)\r
3621 {\r
3622         SIZE stSize;\r
3623         SetBkMode( hdcSeek, TRANSPARENT );\r
3624         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3625         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3626 }\r
3627 \r
3628 void DrawSeekDot(int x, int y, int color)\r
3629 {\r
3630         int square = color & 0x80;\r
3631         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3632                         color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);\r
3633         color &= 0x7F;\r
3634         if(square)\r
3635             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3636                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3637         else\r
3638             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3639                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3640             SelectObject(hdcSeek, oldBrush);\r
3641 }\r
3642 \r
3643 void DrawSeekOpen()\r
3644 {\r
3645 }\r
3646 \r
3647 void DrawSeekClose()\r
3648 {\r
3649 }\r
3650 \r
3651 VOID\r
3652 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3653 {\r
3654   static Board lastReq[2], lastDrawn[2];\r
3655   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3656   static int lastDrawnFlipView = 0;\r
3657   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3658   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3659   HDC tmphdc;\r
3660   HDC hdcmem;\r
3661   HBITMAP bufferBitmap;\r
3662   HBITMAP oldBitmap;\r
3663   RECT Rect;\r
3664   HRGN clips[MAX_CLIPS];\r
3665   ChessSquare dragged_piece = EmptySquare;\r
3666   int nr = twoBoards*partnerUp;\r
3667 \r
3668   /* I'm undecided on this - this function figures out whether a full\r
3669    * repaint is necessary on its own, so there's no real reason to have the\r
3670    * caller tell it that.  I think this can safely be set to FALSE - but\r
3671    * if we trust the callers not to request full repaints unnessesarily, then\r
3672    * we could skip some clipping work.  In other words, only request a full\r
3673    * redraw when the majority of pieces have changed positions (ie. flip, \r
3674    * gamestart and similar)  --Hawk\r
3675    */\r
3676   Boolean fullrepaint = repaint;\r
3677 \r
3678   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3679 \r
3680   if( DrawPositionNeedsFullRepaint() ) {\r
3681       fullrepaint = TRUE;\r
3682   }\r
3683 \r
3684   if (board == NULL) {\r
3685     if (!lastReqValid[nr]) {\r
3686       return;\r
3687     }\r
3688     board = lastReq[nr];\r
3689   } else {\r
3690     CopyBoard(lastReq[nr], board);\r
3691     lastReqValid[nr] = 1;\r
3692   }\r
3693 \r
3694   if (doingSizing) {\r
3695     return;\r
3696   }\r
3697 \r
3698   if (IsIconic(hwndMain)) {\r
3699     return;\r
3700   }\r
3701 \r
3702   if (hdc == NULL) {\r
3703     hdc = GetDC(hwndMain);\r
3704     if (!appData.monoMode) {\r
3705       SelectPalette(hdc, hPal, FALSE);\r
3706       RealizePalette(hdc);\r
3707     }\r
3708     releaseDC = TRUE;\r
3709   } else {\r
3710     releaseDC = FALSE;\r
3711   }\r
3712 \r
3713   /* Create some work-DCs */\r
3714   hdcmem = CreateCompatibleDC(hdc);\r
3715   tmphdc = CreateCompatibleDC(hdc);\r
3716 \r
3717   /* If dragging is in progress, we temporarely remove the piece */\r
3718   /* [HGM] or temporarily decrease count if stacked              */\r
3719   /*       !! Moved to before board compare !!                   */\r
3720   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3721     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3722     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3723             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3724         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3725     } else \r
3726     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3727             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3728         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3729     } else \r
3730         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3731   }\r
3732 \r
3733   /* Figure out which squares need updating by comparing the \r
3734    * newest board with the last drawn board and checking if\r
3735    * flipping has changed.\r
3736    */\r
3737   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3738     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3739       for (column = 0; column < BOARD_WIDTH; column++) {\r
3740         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3741           SquareToPos(row, column, &x, &y);\r
3742           clips[num_clips++] =\r
3743             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3744         }\r
3745       }\r
3746     }\r
3747    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3748     for (i=0; i<2; i++) {\r
3749       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3750           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3751         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3752             lastDrawnHighlight.sq[i].y >= 0) {\r
3753           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3754                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3755           clips[num_clips++] =\r
3756             CreateRectRgn(x - lineGap, y - lineGap, \r
3757                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3758         }\r
3759         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3760           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3761           clips[num_clips++] =\r
3762             CreateRectRgn(x - lineGap, y - lineGap, \r
3763                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3764         }\r
3765       }\r
3766     }\r
3767     for (i=0; i<2; i++) {\r
3768       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3769           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3770         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3771             lastDrawnPremove.sq[i].y >= 0) {\r
3772           SquareToPos(lastDrawnPremove.sq[i].y,\r
3773                       lastDrawnPremove.sq[i].x, &x, &y);\r
3774           clips[num_clips++] =\r
3775             CreateRectRgn(x - lineGap, y - lineGap, \r
3776                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3777         }\r
3778         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3779             premoveHighlightInfo.sq[i].y >= 0) {\r
3780           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3781                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3782           clips[num_clips++] =\r
3783             CreateRectRgn(x - lineGap, y - lineGap, \r
3784                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3785         }\r
3786       }\r
3787     }\r
3788    } else { // nr == 1\r
3789         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3790         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3791         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3792         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3793       for (i=0; i<2; i++) {\r
3794         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3795             partnerHighlightInfo.sq[i].y >= 0) {\r
3796           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3797                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3798           clips[num_clips++] =\r
3799             CreateRectRgn(x - lineGap, y - lineGap, \r
3800                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3801         }\r
3802         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3803             oldPartnerHighlight.sq[i].y >= 0) {\r
3804           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3805                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3806           clips[num_clips++] =\r
3807             CreateRectRgn(x - lineGap, y - lineGap, \r
3808                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3809         }\r
3810       }\r
3811    }\r
3812   } else {\r
3813     fullrepaint = TRUE;\r
3814   }\r
3815 \r
3816   /* Create a buffer bitmap - this is the actual bitmap\r
3817    * being written to.  When all the work is done, we can\r
3818    * copy it to the real DC (the screen).  This avoids\r
3819    * the problems with flickering.\r
3820    */\r
3821   GetClientRect(hwndMain, &Rect);\r
3822   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3823                                         Rect.bottom-Rect.top+1);\r
3824   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3825   if (!appData.monoMode) {\r
3826     SelectPalette(hdcmem, hPal, FALSE);\r
3827   }\r
3828 \r
3829   /* Create clips for dragging */\r
3830   if (!fullrepaint) {\r
3831     if (dragInfo.from.x >= 0) {\r
3832       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3833       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3834     }\r
3835     if (dragInfo.start.x >= 0) {\r
3836       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3837       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3838     }\r
3839     if (dragInfo.pos.x >= 0) {\r
3840       x = dragInfo.pos.x - squareSize / 2;\r
3841       y = dragInfo.pos.y - squareSize / 2;\r
3842       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3843     }\r
3844     if (dragInfo.lastpos.x >= 0) {\r
3845       x = dragInfo.lastpos.x - squareSize / 2;\r
3846       y = dragInfo.lastpos.y - squareSize / 2;\r
3847       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3848     }\r
3849   }\r
3850 \r
3851   /* Are we animating a move?  \r
3852    * If so, \r
3853    *   - remove the piece from the board (temporarely)\r
3854    *   - calculate the clipping region\r
3855    */\r
3856   if (!fullrepaint) {\r
3857     if (animInfo.piece != EmptySquare) {\r
3858       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3859       x = boardRect.left + animInfo.lastpos.x;\r
3860       y = boardRect.top + animInfo.lastpos.y;\r
3861       x2 = boardRect.left + animInfo.pos.x;\r
3862       y2 = boardRect.top + animInfo.pos.y;\r
3863       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3864       /* Slight kludge.  The real problem is that after AnimateMove is\r
3865          done, the position on the screen does not match lastDrawn.\r
3866          This currently causes trouble only on e.p. captures in\r
3867          atomic, where the piece moves to an empty square and then\r
3868          explodes.  The old and new positions both had an empty square\r
3869          at the destination, but animation has drawn a piece there and\r
3870          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3871       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3872     }\r
3873   }\r
3874 \r
3875   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3876   if (num_clips == 0)\r
3877     fullrepaint = TRUE;\r
3878 \r
3879   /* Set clipping on the memory DC */\r
3880   if (!fullrepaint) {\r
3881     SelectClipRgn(hdcmem, clips[0]);\r
3882     for (x = 1; x < num_clips; x++) {\r
3883       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3884         abort();  // this should never ever happen!\r
3885     }\r
3886   }\r
3887 \r
3888   /* Do all the drawing to the memory DC */\r
3889   if(explodeInfo.radius) { // [HGM] atomic\r
3890         HBRUSH oldBrush;\r
3891         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3892         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3893         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3894         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3895         x += squareSize/2;\r
3896         y += squareSize/2;\r
3897         if(!fullrepaint) {\r
3898           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3899           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3900         }\r
3901         DrawGridOnDC(hdcmem);\r
3902         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3903         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3904         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3905         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3906         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3907         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3908         SelectObject(hdcmem, oldBrush);\r
3909   } else {\r
3910     if(border) DrawBackgroundOnDC(hdcmem);\r
3911     DrawGridOnDC(hdcmem);\r
3912     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3913         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3914         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3915     } else {\r
3916         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3917         oldPartnerHighlight = partnerHighlightInfo;\r
3918     }\r
3919     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3920   }\r
3921   if(nr == 0) // [HGM] dual: markers only on left board\r
3922   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3923     for (column = 0; column < BOARD_WIDTH; column++) {\r
3924         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3925             HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);\r
3926             SquareToPos(row, column, &x, &y);\r
3927             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3928                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3929             SelectObject(hdcmem, oldBrush);\r
3930         }\r
3931     }\r
3932   }\r
3933 \r
3934   if( appData.highlightMoveWithArrow ) {\r
3935     DrawArrowHighlight(hdcmem);\r
3936   }\r
3937 \r
3938   DrawCoordsOnDC(hdcmem);\r
3939 \r
3940   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3941                  /* to make sure lastDrawn contains what is actually drawn */\r
3942 \r
3943   /* Put the dragged piece back into place and draw it (out of place!) */\r
3944     if (dragged_piece != EmptySquare) {\r
3945     /* [HGM] or restack */\r
3946     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3947                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3948     else\r
3949     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3950                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3951     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3952     x = dragInfo.pos.x - squareSize / 2;\r
3953     y = dragInfo.pos.y - squareSize / 2;\r
3954     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3955                   ((int) dragInfo.piece < (int) BlackPawn), \r
3956                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3957   }   \r
3958   \r
3959   /* Put the animated piece back into place and draw it */\r
3960   if (animInfo.piece != EmptySquare) {\r
3961     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3962     x = boardRect.left + animInfo.pos.x;\r
3963     y = boardRect.top + animInfo.pos.y;\r
3964     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3965                   ((int) animInfo.piece < (int) BlackPawn),\r
3966                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3967   }\r
3968 \r
3969   /* Release the bufferBitmap by selecting in the old bitmap \r
3970    * and delete the memory DC\r
3971    */\r
3972   SelectObject(hdcmem, oldBitmap);\r
3973   DeleteDC(hdcmem);\r
3974 \r
3975   /* Set clipping on the target DC */\r
3976   if (!fullrepaint) {\r
3977     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3978         RECT rect;\r
3979         GetRgnBox(clips[x], &rect);\r
3980         DeleteObject(clips[x]);\r
3981         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3982                           rect.right + wpMain.width/2, rect.bottom);\r
3983     }\r
3984     SelectClipRgn(hdc, clips[0]);\r
3985     for (x = 1; x < num_clips; x++) {\r
3986       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3987         abort();   // this should never ever happen!\r
3988     } \r
3989   }\r
3990 \r
3991   /* Copy the new bitmap onto the screen in one go.\r
3992    * This way we avoid any flickering\r
3993    */\r
3994   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3995   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3996          boardRect.right - boardRect.left,\r
3997          boardRect.bottom - boardRect.top,\r
3998          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3999   if(saveDiagFlag) { \r
4000     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
4001     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4002 \r
4003     GetObject(bufferBitmap, sizeof(b), &b);\r
4004     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
4005         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4006         bih.biWidth = b.bmWidth;\r
4007         bih.biHeight = b.bmHeight;\r
4008         bih.biPlanes = 1;\r
4009         bih.biBitCount = b.bmBitsPixel;\r
4010         bih.biCompression = 0;\r
4011         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4012         bih.biXPelsPerMeter = 0;\r
4013         bih.biYPelsPerMeter = 0;\r
4014         bih.biClrUsed = 0;\r
4015         bih.biClrImportant = 0;\r
4016 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4017 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4018         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4019 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4020 \r
4021         wb = b.bmWidthBytes;\r
4022         // count colors\r
4023         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4024                 int k = ((int*) pData)[i];\r
4025                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4026                 if(j >= 16) break;\r
4027                 color[j] = k;\r
4028                 if(j >= nrColors) nrColors = j+1;\r
4029         }\r
4030         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4031                 INT p = 0;\r
4032                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4033                     for(w=0; w<(wb>>2); w+=2) {\r
4034                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4035                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4036                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4037                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4038                         pData[p++] = m | j<<4;\r
4039                     }\r
4040                     while(p&3) pData[p++] = 0;\r
4041                 }\r
4042                 fac = 3;\r
4043                 wb = ((wb+31)>>5)<<2;\r
4044         }\r
4045         // write BITMAPFILEHEADER\r
4046         fprintf(diagFile, "BM");\r
4047         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4048         fputDW(diagFile, 0);\r
4049         fputDW(diagFile, 0x36 + (fac?64:0));\r
4050         // write BITMAPINFOHEADER\r
4051         fputDW(diagFile, 40);\r
4052         fputDW(diagFile, b.bmWidth);\r
4053         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4054         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4055         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4056         fputDW(diagFile, 0);\r
4057         fputDW(diagFile, 0);\r
4058         fputDW(diagFile, 0);\r
4059         fputDW(diagFile, 0);\r
4060         fputDW(diagFile, 0);\r
4061         fputDW(diagFile, 0);\r
4062         // write color table\r
4063         if(fac)\r
4064         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4065         // write bitmap data\r
4066         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4067                 fputc(pData[i], diagFile);\r
4068         free(pData);\r
4069      }\r
4070   }\r
4071 \r
4072   SelectObject(tmphdc, oldBitmap);\r
4073 \r
4074   /* Massive cleanup */\r
4075   for (x = 0; x < num_clips; x++)\r
4076     DeleteObject(clips[x]);\r
4077 \r
4078   DeleteDC(tmphdc);\r
4079   DeleteObject(bufferBitmap);\r
4080 \r
4081   if (releaseDC) \r
4082     ReleaseDC(hwndMain, hdc);\r
4083   \r
4084   if (lastDrawnFlipView != flipView && nr == 0) {\r
4085     if (flipView)\r
4086       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4087     else\r
4088       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4089   }\r
4090 \r
4091 /*  CopyBoard(lastDrawn, board);*/\r
4092   lastDrawnHighlight = highlightInfo;\r
4093   lastDrawnPremove   = premoveHighlightInfo;\r
4094   lastDrawnFlipView = flipView;\r
4095   lastDrawnValid[nr] = 1;\r
4096 }\r
4097 \r
4098 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4099 int\r
4100 SaveDiagram(f)\r
4101      FILE *f;\r
4102 {\r
4103     saveDiagFlag = 1; diagFile = f;\r
4104     HDCDrawPosition(NULL, TRUE, NULL);\r
4105     saveDiagFlag = 0;\r
4106 \r
4107     fclose(f);\r
4108     return TRUE;\r
4109 }\r
4110 \r
4111 \r
4112 /*---------------------------------------------------------------------------*\\r
4113 | CLIENT PAINT PROCEDURE\r
4114 |   This is the main event-handler for the WM_PAINT message.\r
4115 |\r
4116 \*---------------------------------------------------------------------------*/\r
4117 VOID\r
4118 PaintProc(HWND hwnd)\r
4119 {\r
4120   HDC         hdc;\r
4121   PAINTSTRUCT ps;\r
4122   HFONT       oldFont;\r
4123 \r
4124   if((hdc = BeginPaint(hwnd, &ps))) {\r
4125     if (IsIconic(hwnd)) {\r
4126       DrawIcon(hdc, 2, 2, iconCurrent);\r
4127     } else {\r
4128       if (!appData.monoMode) {\r
4129         SelectPalette(hdc, hPal, FALSE);\r
4130         RealizePalette(hdc);\r
4131       }\r
4132       HDCDrawPosition(hdc, 1, NULL);\r
4133       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4134         flipView = !flipView; partnerUp = !partnerUp;\r
4135         HDCDrawPosition(hdc, 1, NULL);\r
4136         flipView = !flipView; partnerUp = !partnerUp;\r
4137       }\r
4138       oldFont =\r
4139         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4140       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4141                  ETO_CLIPPED|ETO_OPAQUE,\r
4142                  &messageRect, messageText, strlen(messageText), NULL);\r
4143       SelectObject(hdc, oldFont);\r
4144       DisplayBothClocks();\r
4145       DisplayLogos();\r
4146     }\r
4147     EndPaint(hwnd,&ps);\r
4148   }\r
4149 \r
4150   return;\r
4151 }\r
4152 \r
4153 \r
4154 /*\r
4155  * If the user selects on a border boundary, return -1; if off the board,\r
4156  *   return -2.  Otherwise map the event coordinate to the square.\r
4157  * The offset boardRect.left or boardRect.top must already have been\r
4158  *   subtracted from x.\r
4159  */\r
4160 int EventToSquare(x, limit)\r
4161      int x, limit;\r
4162 {\r
4163   if (x <= border)\r
4164     return -2;\r
4165   if (x < lineGap + border)\r
4166     return -1;\r
4167   x -= lineGap + border;\r
4168   if ((x % (squareSize + lineGap)) >= squareSize)\r
4169     return -1;\r
4170   x /= (squareSize + lineGap);\r
4171     if (x >= limit)\r
4172     return -2;\r
4173   return x;\r
4174 }\r
4175 \r
4176 typedef struct {\r
4177   char piece;\r
4178   int command;\r
4179   char* name;\r
4180 } DropEnable;\r
4181 \r
4182 DropEnable dropEnables[] = {\r
4183   { 'P', DP_Pawn, N_("Pawn") },\r
4184   { 'N', DP_Knight, N_("Knight") },\r
4185   { 'B', DP_Bishop, N_("Bishop") },\r
4186   { 'R', DP_Rook, N_("Rook") },\r
4187   { 'Q', DP_Queen, N_("Queen") },\r
4188 };\r
4189 \r
4190 VOID\r
4191 SetupDropMenu(HMENU hmenu)\r
4192 {\r
4193   int i, count, enable;\r
4194   char *p;\r
4195   extern char white_holding[], black_holding[];\r
4196   char item[MSG_SIZ];\r
4197 \r
4198   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4199     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4200                dropEnables[i].piece);\r
4201     count = 0;\r
4202     while (p && *p++ == dropEnables[i].piece) count++;\r
4203       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4204     enable = count > 0 || !appData.testLegality\r
4205       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4206                       && !appData.icsActive);\r
4207     ModifyMenu(hmenu, dropEnables[i].command,\r
4208                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4209                dropEnables[i].command, item);\r
4210   }\r
4211 }\r
4212 \r
4213 void DragPieceBegin(int x, int y, Boolean instantly)\r
4214 {\r
4215       dragInfo.lastpos.x = boardRect.left + x;\r
4216       dragInfo.lastpos.y = boardRect.top + y;\r
4217       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4218       dragInfo.from.x = fromX;\r
4219       dragInfo.from.y = fromY;\r
4220       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4221       dragInfo.start = dragInfo.from;\r
4222       SetCapture(hwndMain);\r
4223 }\r
4224 \r
4225 void DragPieceEnd(int x, int y)\r
4226 {\r
4227     ReleaseCapture();\r
4228     dragInfo.start.x = dragInfo.start.y = -1;\r
4229     dragInfo.from = dragInfo.start;\r
4230     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4231 }\r
4232 \r
4233 void ChangeDragPiece(ChessSquare piece)\r
4234 {\r
4235     dragInfo.piece = piece;\r
4236 }\r
4237 \r
4238 /* Event handler for mouse messages */\r
4239 VOID\r
4240 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4241 {\r
4242   int x, y, menuNr;\r
4243   POINT pt;\r
4244   static int recursive = 0;\r
4245   HMENU hmenu;\r
4246   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4247 \r
4248   if (recursive) {\r
4249     if (message == WM_MBUTTONUP) {\r
4250       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4251          to the middle button: we simulate pressing the left button too!\r
4252          */\r
4253       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4254       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4255     }\r
4256     return;\r
4257   }\r
4258   recursive++;\r
4259   \r
4260   pt.x = LOWORD(lParam);\r
4261   pt.y = HIWORD(lParam);\r
4262   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4263   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4264   if (!flipView && y >= 0) {\r
4265     y = BOARD_HEIGHT - 1 - y;\r
4266   }\r
4267   if (flipView && x >= 0) {\r
4268     x = BOARD_WIDTH - 1 - x;\r
4269   }\r
4270 \r
4271   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4272   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4273 \r
4274   switch (message) {\r
4275   case WM_LBUTTONDOWN:\r
4276       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4277         ClockClick(flipClock); break;\r
4278       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4279         ClockClick(!flipClock); break;\r
4280       }\r
4281     if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging\r
4282       dragInfo.start.x = dragInfo.start.y = -1;\r
4283       dragInfo.from = dragInfo.start;\r
4284     }\r
4285     if(fromX == -1 && frozen) { // not sure where this is for\r
4286                 fromX = fromY = -1; \r
4287       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4288       break;\r
4289     }\r
4290       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4291       DrawPosition(TRUE, NULL);\r
4292     break;\r
4293 \r
4294   case WM_LBUTTONUP:\r
4295       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4296       DrawPosition(TRUE, NULL);\r
4297     break;\r
4298 \r
4299   case WM_MOUSEMOVE:\r
4300     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4301     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4302     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4303     if ((appData.animateDragging || appData.highlightDragging)\r
4304         && (wParam & MK_LBUTTON || dragging == 2)\r
4305         && dragInfo.from.x >= 0) \r
4306     {\r
4307       BOOL full_repaint = FALSE;\r
4308 \r
4309       if (appData.animateDragging) {\r
4310         dragInfo.pos = pt;\r
4311       }\r
4312       if (appData.highlightDragging) {\r
4313         HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);\r
4314         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4315             full_repaint = TRUE;\r
4316         }\r
4317       }\r
4318       \r
4319       DrawPosition( full_repaint, NULL);\r
4320       \r
4321       dragInfo.lastpos = dragInfo.pos;\r
4322     }\r
4323     break;\r
4324 \r
4325   case WM_MOUSEWHEEL: // [DM]\r
4326     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4327        /* Mouse Wheel is being rolled forward\r
4328         * Play moves forward\r
4329         */\r
4330        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4331                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4332        /* Mouse Wheel is being rolled backward\r
4333         * Play moves backward\r
4334         */\r
4335        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4336                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4337     }\r
4338     break;\r
4339 \r
4340   case WM_MBUTTONUP:\r
4341   case WM_RBUTTONUP:\r
4342     ReleaseCapture();\r
4343     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4344     break;\r
4345  \r
4346   case WM_MBUTTONDOWN:\r
4347   case WM_RBUTTONDOWN:\r
4348     ErrorPopDown();\r
4349     ReleaseCapture();\r
4350     fromX = fromY = -1;\r
4351     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4352     dragInfo.start.x = dragInfo.start.y = -1;\r
4353     dragInfo.from = dragInfo.start;\r
4354     dragInfo.lastpos = dragInfo.pos;\r
4355     if (appData.highlightDragging) {\r
4356       ClearHighlights();\r
4357     }\r
4358     if(y == -2) {\r
4359       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4360       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4361           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4362       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4363           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4364       }\r
4365       break;\r
4366     }\r
4367     DrawPosition(TRUE, NULL);\r
4368 \r
4369     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4370     switch (menuNr) {\r
4371     case 0:\r
4372       if (message == WM_MBUTTONDOWN) {\r
4373         buttonCount = 3;  /* even if system didn't think so */\r
4374         if (wParam & MK_SHIFT) \r
4375           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4376         else\r
4377           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4378       } else { /* message == WM_RBUTTONDOWN */\r
4379         /* Just have one menu, on the right button.  Windows users don't\r
4380            think to try the middle one, and sometimes other software steals\r
4381            it, or it doesn't really exist. */\r
4382         if(gameInfo.variant != VariantShogi)\r
4383             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4384         else\r
4385             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4386       }\r
4387       break;\r
4388     case 2:\r
4389       SetCapture(hwndMain);\r
4390       break;\r
4391     case 1:\r
4392       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4393       SetupDropMenu(hmenu);\r
4394       MenuPopup(hwnd, pt, hmenu, -1);\r
4395     default:\r
4396       break;\r
4397     }\r
4398     break;\r
4399   }\r
4400 \r
4401   recursive--;\r
4402 }\r
4403 \r
4404 /* Preprocess messages for buttons in main window */\r
4405 LRESULT CALLBACK\r
4406 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4407 {\r
4408   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4409   int i, dir;\r
4410 \r
4411   for (i=0; i<N_BUTTONS; i++) {\r
4412     if (buttonDesc[i].id == id) break;\r
4413   }\r
4414   if (i == N_BUTTONS) return 0;\r
4415   switch (message) {\r
4416   case WM_KEYDOWN:\r
4417     switch (wParam) {\r
4418     case VK_LEFT:\r
4419     case VK_RIGHT:\r
4420       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4421       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4422       return TRUE;\r
4423     }\r
4424     break;\r
4425   case WM_CHAR:\r
4426     switch (wParam) {\r
4427     case '\r':\r
4428       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4429       return TRUE;\r
4430     default:\r
4431       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4432         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4433         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4434         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4435         SetFocus(h);\r
4436         SendMessage(h, WM_CHAR, wParam, lParam);\r
4437         return TRUE;\r
4438       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4439         TypeInEvent((char)wParam);\r
4440       }\r
4441       break;\r
4442     }\r
4443     break;\r
4444   }\r
4445   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4446 }\r
4447 \r
4448 static int promoStyle;\r
4449 \r
4450 /* Process messages for Promotion dialog box */\r
4451 LRESULT CALLBACK\r
4452 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4453 {\r
4454   char promoChar;\r
4455 \r
4456   switch (message) {\r
4457   case WM_INITDIALOG: /* message: initialize dialog box */\r
4458     /* Center the dialog over the application window */\r
4459     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4460     Translate(hDlg, DLG_PromotionKing);\r
4461     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4462       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4463        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4464        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4465                SW_SHOW : SW_HIDE);\r
4466     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4467     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4468        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4469          PieceToChar(WhiteAngel) != '~') ||\r
4470         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4471          PieceToChar(BlackAngel) != '~')   ) ?\r
4472                SW_SHOW : SW_HIDE);\r
4473     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4474        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4475          PieceToChar(WhiteMarshall) != '~') ||\r
4476         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4477          PieceToChar(BlackMarshall) != '~')   ) ?\r
4478                SW_SHOW : SW_HIDE);\r
4479     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4480     ShowWindow(GetDlgItem(hDlg, PB_Rook),   !promoStyle ? SW_SHOW : SW_HIDE);\r
4481     ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);\r
4482     if(promoStyle) {\r
4483         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4484         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4485         SetWindowText(hDlg, "Promote?");\r
4486     }\r
4487     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4488        gameInfo.variant == VariantSuper ?\r
4489                SW_SHOW : SW_HIDE);\r
4490     return TRUE;\r
4491 \r
4492   case WM_COMMAND: /* message: received a command */\r
4493     switch (LOWORD(wParam)) {\r
4494     case IDCANCEL:\r
4495       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4496       ClearHighlights();\r
4497       DrawPosition(FALSE, NULL);\r
4498       return TRUE;\r
4499     case PB_King:\r
4500       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4501       break;\r
4502     case PB_Queen:\r
4503       promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4504       break;\r
4505     case PB_Rook:\r
4506       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4507       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4508       break;\r
4509     case PB_Bishop:\r
4510       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4511       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4512       break;\r
4513     case PB_Chancellor:\r
4514       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4515       break;\r
4516     case PB_Archbishop:\r
4517       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4518       break;\r
4519     case PB_Knight:\r
4520       promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR : \r
4521                   ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));\r
4522       break;\r
4523     default:\r
4524       return FALSE;\r
4525     }\r
4526     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4527     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4528     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4529     fromX = fromY = -1;\r
4530     if (!appData.highlightLastMove) {\r
4531       ClearHighlights();\r
4532       DrawPosition(FALSE, NULL);\r
4533     }\r
4534     return TRUE;\r
4535   }\r
4536   return FALSE;\r
4537 }\r
4538 \r
4539 /* Pop up promotion dialog */\r
4540 VOID\r
4541 PromotionPopup(HWND hwnd)\r
4542 {\r
4543   FARPROC lpProc;\r
4544 \r
4545   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4546   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4547     hwnd, (DLGPROC)lpProc);\r
4548   FreeProcInstance(lpProc);\r
4549 }\r
4550 \r
4551 void\r
4552 PromotionPopUp(char choice)\r
4553 {\r
4554   promoStyle = (choice == '+');\r
4555   DrawPosition(TRUE, NULL);\r
4556   PromotionPopup(hwndMain);\r
4557 }\r
4558 \r
4559 VOID\r
4560 LoadGameDialog(HWND hwnd, char* title)\r
4561 {\r
4562   UINT number = 0;\r
4563   FILE *f;\r
4564   char fileTitle[MSG_SIZ];\r
4565   f = OpenFileDialog(hwnd, "rb", "",\r
4566                      appData.oldSaveStyle ? "gam" : "pgn",\r
4567                      GAME_FILT,\r
4568                      title, &number, fileTitle, NULL);\r
4569   if (f != NULL) {\r
4570     cmailMsgLoaded = FALSE;\r
4571     if (number == 0) {\r
4572       int error = GameListBuild(f);\r
4573       if (error) {\r
4574         DisplayError(_("Cannot build game list"), error);\r
4575       } else if (!ListEmpty(&gameList) &&\r
4576                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4577         GameListPopUp(f, fileTitle);\r
4578         return;\r
4579       }\r
4580       GameListDestroy();\r
4581       number = 1;\r
4582     }\r
4583     LoadGame(f, number, fileTitle, FALSE);\r
4584   }\r
4585 }\r
4586 \r
4587 int get_term_width()\r
4588 {\r
4589     HDC hdc;\r
4590     TEXTMETRIC tm;\r
4591     RECT rc;\r
4592     HFONT hfont, hold_font;\r
4593     LOGFONT lf;\r
4594     HWND hText;\r
4595 \r
4596     if (hwndConsole)\r
4597         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4598     else\r
4599         return 79;\r
4600 \r
4601     // get the text metrics\r
4602     hdc = GetDC(hText);\r
4603     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4604     if (consoleCF.dwEffects & CFE_BOLD)\r
4605         lf.lfWeight = FW_BOLD;\r
4606     if (consoleCF.dwEffects & CFE_ITALIC)\r
4607         lf.lfItalic = TRUE;\r
4608     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4609         lf.lfStrikeOut = TRUE;\r
4610     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4611         lf.lfUnderline = TRUE;\r
4612     hfont = CreateFontIndirect(&lf);\r
4613     hold_font = SelectObject(hdc, hfont);\r
4614     GetTextMetrics(hdc, &tm);\r
4615     SelectObject(hdc, hold_font);\r
4616     DeleteObject(hfont);\r
4617     ReleaseDC(hText, hdc);\r
4618 \r
4619     // get the rectangle\r
4620     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4621 \r
4622     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4623 }\r
4624 \r
4625 void UpdateICSWidth(HWND hText)\r
4626 {\r
4627     LONG old_width, new_width;\r
4628 \r
4629     new_width = get_term_width(hText, FALSE);\r
4630     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4631     if (new_width != old_width)\r
4632     {\r
4633         ics_update_width(new_width);\r
4634         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4635     }\r
4636 }\r
4637 \r
4638 VOID\r
4639 ChangedConsoleFont()\r
4640 {\r
4641   CHARFORMAT cfmt;\r
4642   CHARRANGE tmpsel, sel;\r
4643   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4644   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4645   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4646   PARAFORMAT paraf;\r
4647 \r
4648   cfmt.cbSize = sizeof(CHARFORMAT);\r
4649   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4650     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4651                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4652   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4653    * size.  This was undocumented in the version of MSVC++ that I had\r
4654    * when I wrote the code, but is apparently documented now.\r
4655    */\r
4656   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4657   cfmt.bCharSet = f->lf.lfCharSet;\r
4658   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4659   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4660   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4661   /* Why are the following seemingly needed too? */\r
4662   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4663   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4664   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4665   tmpsel.cpMin = 0;\r
4666   tmpsel.cpMax = -1; /*999999?*/\r
4667   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4668   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4669   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4670    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4671    */\r
4672   paraf.cbSize = sizeof(paraf);\r
4673   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4674   paraf.dxStartIndent = 0;\r
4675   paraf.dxOffset = WRAP_INDENT;\r
4676   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4677   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4678   UpdateICSWidth(hText);\r
4679 }\r
4680 \r
4681 /*---------------------------------------------------------------------------*\\r
4682  *\r
4683  * Window Proc for main window\r
4684  *\r
4685 \*---------------------------------------------------------------------------*/\r
4686 \r
4687 /* Process messages for main window, etc. */\r
4688 LRESULT CALLBACK\r
4689 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4690 {\r
4691   FARPROC lpProc;\r
4692   int wmId, wmEvent;\r
4693   char *defName;\r
4694   FILE *f;\r
4695   UINT number;\r
4696   char fileTitle[MSG_SIZ];\r
4697   static SnapData sd;\r
4698   static int peek=0;\r
4699 \r
4700   switch (message) {\r
4701 \r
4702   case WM_PAINT: /* message: repaint portion of window */\r
4703     PaintProc(hwnd);\r
4704     break;\r
4705 \r
4706   case WM_ERASEBKGND:\r
4707     if (IsIconic(hwnd)) {\r
4708       /* Cheat; change the message */\r
4709       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4710     } else {\r
4711       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4712     }\r
4713     break;\r
4714 \r
4715   case WM_LBUTTONDOWN:\r
4716   case WM_MBUTTONDOWN:\r
4717   case WM_RBUTTONDOWN:\r
4718   case WM_LBUTTONUP:\r
4719   case WM_MBUTTONUP:\r
4720   case WM_RBUTTONUP:\r
4721   case WM_MOUSEMOVE:\r
4722   case WM_MOUSEWHEEL:\r
4723     MouseEvent(hwnd, message, wParam, lParam);\r
4724     break;\r
4725 \r
4726   case WM_KEYUP:\r
4727     if((char)wParam == '\b') {\r
4728       ForwardEvent(); peek = 0;\r
4729     }\r
4730 \r
4731     JAWS_KBUP_NAVIGATION\r
4732 \r
4733     break;\r
4734 \r
4735   case WM_KEYDOWN:\r
4736     if((char)wParam == '\b') {\r
4737       if(!peek) BackwardEvent(), peek = 1;\r
4738     }\r
4739 \r
4740     JAWS_KBDOWN_NAVIGATION\r
4741 \r
4742     break;\r
4743 \r
4744   case WM_CHAR:\r
4745     \r
4746     JAWS_ALT_INTERCEPT\r
4747 \r
4748     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4749         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4750         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4751         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4752         SetFocus(h);\r
4753         SendMessage(h, message, wParam, lParam);\r
4754     } else if(lParam != KF_REPEAT) {\r
4755         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4756                 TypeInEvent((char)wParam);\r
4757         } else if((char)wParam == 003) CopyGameToClipboard();\r
4758          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4759     }\r
4760 \r
4761     break;\r
4762 \r
4763   case WM_PALETTECHANGED:\r
4764     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4765       int nnew;\r
4766       HDC hdc = GetDC(hwndMain);\r
4767       SelectPalette(hdc, hPal, TRUE);\r
4768       nnew = RealizePalette(hdc);\r
4769       if (nnew > 0) {\r
4770         paletteChanged = TRUE;\r
4771 \r
4772         InvalidateRect(hwnd, &boardRect, FALSE);\r
4773       }\r
4774       ReleaseDC(hwnd, hdc);\r
4775     }\r
4776     break;\r
4777 \r
4778   case WM_QUERYNEWPALETTE:\r
4779     if (!appData.monoMode /*&& paletteChanged*/) {\r
4780       int nnew;\r
4781       HDC hdc = GetDC(hwndMain);\r
4782       paletteChanged = FALSE;\r
4783       SelectPalette(hdc, hPal, FALSE);\r
4784       nnew = RealizePalette(hdc);\r
4785       if (nnew > 0) {\r
4786         InvalidateRect(hwnd, &boardRect, FALSE);\r
4787       }\r
4788       ReleaseDC(hwnd, hdc);\r
4789       return TRUE;\r
4790     }\r
4791     return FALSE;\r
4792 \r
4793   case WM_COMMAND: /* message: command from application menu */\r
4794     wmId    = LOWORD(wParam);\r
4795     wmEvent = HIWORD(wParam);\r
4796 \r
4797     switch (wmId) {\r
4798     case IDM_NewGame:\r
4799       ResetGameEvent();\r
4800       SAY("new game enter a move to play against the computer with white");\r
4801       break;\r
4802 \r
4803     case IDM_NewGameFRC:\r
4804       if( NewGameFRC() == 0 ) {\r
4805         ResetGameEvent();\r
4806       }\r
4807       break;\r
4808 \r
4809     case IDM_NewVariant:\r
4810       NewVariantPopup(hwnd);\r
4811       break;\r
4812 \r
4813     case IDM_LoadGame:\r
4814       LoadGameDialog(hwnd, _("Load Game from File"));\r
4815       break;\r
4816 \r
4817     case IDM_LoadNextGame:\r
4818       ReloadGame(1);\r
4819       break;\r
4820 \r
4821     case IDM_LoadPrevGame:\r
4822       ReloadGame(-1);\r
4823       break;\r
4824 \r
4825     case IDM_ReloadGame:\r
4826       ReloadGame(0);\r
4827       break;\r
4828 \r
4829     case IDM_LoadPosition:\r
4830       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4831         Reset(FALSE, TRUE);\r
4832       }\r
4833       number = 1;\r
4834       f = OpenFileDialog(hwnd, "rb", "",\r
4835                          appData.oldSaveStyle ? "pos" : "fen",\r
4836                          POSITION_FILT,\r
4837                          _("Load Position from File"), &number, fileTitle, NULL);\r
4838       if (f != NULL) {\r
4839         LoadPosition(f, number, fileTitle);\r
4840       }\r
4841       break;\r
4842 \r
4843     case IDM_LoadNextPosition:\r
4844       ReloadPosition(1);\r
4845       break;\r
4846 \r
4847     case IDM_LoadPrevPosition:\r
4848       ReloadPosition(-1);\r
4849       break;\r
4850 \r
4851     case IDM_ReloadPosition:\r
4852       ReloadPosition(0);\r
4853       break;\r
4854 \r
4855     case IDM_SaveGame:\r
4856       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4857       f = OpenFileDialog(hwnd, "a", defName,\r
4858                          appData.oldSaveStyle ? "gam" : "pgn",\r
4859                          GAME_FILT,\r
4860                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4861       if (f != NULL) {\r
4862         SaveGame(f, 0, "");\r
4863       }\r
4864       break;\r
4865 \r
4866     case IDM_SavePosition:\r
4867       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4868       f = OpenFileDialog(hwnd, "a", defName,\r
4869                          appData.oldSaveStyle ? "pos" : "fen",\r
4870                          POSITION_FILT,\r
4871                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4872       if (f != NULL) {\r
4873         SavePosition(f, 0, "");\r
4874       }\r
4875       break;\r
4876 \r
4877     case IDM_SaveDiagram:\r
4878       defName = "diagram";\r
4879       f = OpenFileDialog(hwnd, "wb", defName,\r
4880                          "bmp",\r
4881                          DIAGRAM_FILT,\r
4882                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4883       if (f != NULL) {\r
4884         SaveDiagram(f);\r
4885       }\r
4886       break;\r
4887 \r
4888     case IDM_CreateBook:\r
4889       CreateBookEvent();\r
4890       break;\r
4891 \r
4892     case IDM_CopyGame:\r
4893       CopyGameToClipboard();\r
4894       break;\r
4895 \r
4896     case IDM_PasteGame:\r
4897       PasteGameFromClipboard();\r
4898       break;\r
4899 \r
4900     case IDM_CopyGameListToClipboard:\r
4901       CopyGameListToClipboard();\r
4902       break;\r
4903 \r
4904     /* [AS] Autodetect FEN or PGN data */\r
4905     case IDM_PasteAny:\r
4906       PasteGameOrFENFromClipboard();\r
4907       break;\r
4908 \r
4909     /* [AS] Move history */\r
4910     case IDM_ShowMoveHistory:\r
4911         if( MoveHistoryIsUp() ) {\r
4912             MoveHistoryPopDown();\r
4913         }\r
4914         else {\r
4915             MoveHistoryPopUp();\r
4916         }\r
4917         break;\r
4918 \r
4919     /* [AS] Eval graph */\r
4920     case IDM_ShowEvalGraph:\r
4921         if( EvalGraphIsUp() ) {\r
4922             EvalGraphPopDown();\r
4923         }\r
4924         else {\r
4925             EvalGraphPopUp();\r
4926             SetFocus(hwndMain);\r
4927         }\r
4928         break;\r
4929 \r
4930     /* [AS] Engine output */\r
4931     case IDM_ShowEngineOutput:\r
4932         if( EngineOutputIsUp() ) {\r
4933             EngineOutputPopDown();\r
4934         }\r
4935         else {\r
4936             EngineOutputPopUp();\r
4937         }\r
4938         break;\r
4939 \r
4940     /* [AS] User adjudication */\r
4941     case IDM_UserAdjudication_White:\r
4942         UserAdjudicationEvent( +1 );\r
4943         break;\r
4944 \r
4945     case IDM_UserAdjudication_Black:\r
4946         UserAdjudicationEvent( -1 );\r
4947         break;\r
4948 \r
4949     case IDM_UserAdjudication_Draw:\r
4950         UserAdjudicationEvent( 0 );\r
4951         break;\r
4952 \r
4953     /* [AS] Game list options dialog */\r
4954     case IDM_GameListOptions:\r
4955       GameListOptions();\r
4956       break;\r
4957 \r
4958     case IDM_NewChat:\r
4959       ChatPopUp(NULL);\r
4960       break;\r
4961 \r
4962     case IDM_CopyPosition:\r
4963       CopyFENToClipboard();\r
4964       break;\r
4965 \r
4966     case IDM_PastePosition:\r
4967       PasteFENFromClipboard();\r
4968       break;\r
4969 \r
4970     case IDM_MailMove:\r
4971       MailMoveEvent();\r
4972       break;\r
4973 \r
4974     case IDM_ReloadCMailMsg:\r
4975       Reset(TRUE, TRUE);\r
4976       ReloadCmailMsgEvent(FALSE);\r
4977       break;\r
4978 \r
4979     case IDM_Minimize:\r
4980       ShowWindow(hwnd, SW_MINIMIZE);\r
4981       break;\r
4982 \r
4983     case IDM_Exit:\r
4984       ExitEvent(0);\r
4985       break;\r
4986 \r
4987     case IDM_MachineWhite:\r
4988       MachineWhiteEvent();\r
4989       /*\r
4990        * refresh the tags dialog only if it's visible\r
4991        */\r
4992       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4993           char *tags;\r
4994           tags = PGNTags(&gameInfo);\r
4995           TagsPopUp(tags, CmailMsg());\r
4996           free(tags);\r
4997       }\r
4998       SAY("computer starts playing white");\r
4999       break;\r
5000 \r
5001     case IDM_MachineBlack:\r
5002       MachineBlackEvent();\r
5003       /*\r
5004        * refresh the tags dialog only if it's visible\r
5005        */\r
5006       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5007           char *tags;\r
5008           tags = PGNTags(&gameInfo);\r
5009           TagsPopUp(tags, CmailMsg());\r
5010           free(tags);\r
5011       }\r
5012       SAY("computer starts playing black");\r
5013       break;\r
5014 \r
5015     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5016       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5017       break;\r
5018 \r
5019     case IDM_TwoMachines:\r
5020       TwoMachinesEvent();\r
5021       /*\r
5022        * refresh the tags dialog only if it's visible\r
5023        */\r
5024       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5025           char *tags;\r
5026           tags = PGNTags(&gameInfo);\r
5027           TagsPopUp(tags, CmailMsg());\r
5028           free(tags);\r
5029       }\r
5030       SAY("computer starts playing both sides");\r
5031       break;\r
5032 \r
5033     case IDM_AnalysisMode:\r
5034       if(AnalyzeModeEvent()) {\r
5035         SAY("analyzing current position");\r
5036       }\r
5037       break;\r
5038 \r
5039     case IDM_AnalyzeFile:\r
5040       AnalyzeFileEvent();\r
5041       break;\r
5042 \r
5043     case IDM_IcsClient:\r
5044       IcsClientEvent();\r
5045       break;\r
5046 \r
5047     case IDM_EditGame:\r
5048     case IDM_EditGame2:\r
5049       EditGameEvent();\r
5050       SAY("edit game");\r
5051       break;\r
5052 \r
5053     case IDM_EditPosition:\r
5054     case IDM_EditPosition2:\r
5055       EditPositionEvent();\r
5056       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5057       break;\r
5058 \r
5059     case IDM_Training:\r
5060       TrainingEvent();\r
5061       break;\r
5062 \r
5063     case IDM_ShowGameList:\r
5064       ShowGameListProc();\r
5065       break;\r
5066 \r
5067     case IDM_EditProgs1:\r
5068       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5069       break;\r
5070 \r
5071     case IDM_LoadProg1:\r
5072      LoadEnginePopUp(hwndMain, 0);\r
5073       break;\r
5074 \r
5075     case IDM_LoadProg2:\r
5076      LoadEnginePopUp(hwndMain, 1);\r
5077       break;\r
5078 \r
5079     case IDM_EditServers:\r
5080       EditTagsPopUp(icsNames, &icsNames);\r
5081       break;\r
5082 \r
5083     case IDM_EditTags:\r
5084     case IDM_Tags:\r
5085       EditTagsProc();\r
5086       break;\r
5087 \r
5088     case IDM_EditBook:\r
5089       EditBookEvent();\r
5090       break;\r
5091 \r
5092     case IDM_EditComment:\r
5093     case IDM_Comment:\r
5094       if (commentUp && editComment) {\r
5095         CommentPopDown();\r
5096       } else {\r
5097         EditCommentEvent();\r
5098       }\r
5099       break;\r
5100 \r
5101     case IDM_Pause:\r
5102       PauseEvent();\r
5103       break;\r
5104 \r
5105     case IDM_Accept:\r
5106       AcceptEvent();\r
5107       break;\r
5108 \r
5109     case IDM_Decline:\r
5110       DeclineEvent();\r
5111       break;\r
5112 \r
5113     case IDM_Rematch:\r
5114 \r
5115       RematchEvent();\r
5116       break;\r
5117 \r
5118     case IDM_CallFlag:\r
5119       CallFlagEvent();\r
5120       break;\r
5121 \r
5122     case IDM_Draw:\r
5123       DrawEvent();\r
5124       break;\r
5125 \r
5126     case IDM_Adjourn:\r
5127       AdjournEvent();\r
5128       break;\r
5129 \r
5130     case IDM_Abort:\r
5131       AbortEvent();\r
5132       break;\r
5133 \r
5134     case IDM_Resign:\r
5135       ResignEvent();\r
5136       break;\r
5137 \r
5138     case IDM_StopObserving:\r
5139       StopObservingEvent();\r
5140       break;\r
5141 \r
5142     case IDM_StopExamining:\r
5143       StopExaminingEvent();\r
5144       break;\r
5145 \r
5146     case IDM_Upload:\r
5147       UploadGameEvent();\r
5148       break;\r
5149 \r
5150     case IDM_TypeInMove:\r
5151       TypeInEvent('\000');\r
5152       break;\r
5153 \r
5154     case IDM_TypeInName:\r
5155       PopUpNameDialog('\000');\r
5156       break;\r
5157 \r
5158     case IDM_Backward:\r
5159       BackwardEvent();\r
5160       SetFocus(hwndMain);\r
5161       break;\r
5162 \r
5163     JAWS_MENU_ITEMS\r
5164 \r
5165     case IDM_Forward:\r
5166       ForwardEvent();\r
5167       SetFocus(hwndMain);\r
5168       break;\r
5169 \r
5170     case IDM_ToStart:\r
5171       ToStartEvent();\r
5172       SetFocus(hwndMain);\r
5173       break;\r
5174 \r
5175     case IDM_ToEnd:\r
5176       ToEndEvent();\r
5177       SetFocus(hwndMain);\r
5178       break;\r
5179 \r
5180     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5181     case OPT_GameListPrev:\r
5182       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5183       break;\r
5184 \r
5185     case IDM_Revert:\r
5186       RevertEvent(FALSE);\r
5187       break;\r
5188 \r
5189     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5190       RevertEvent(TRUE);\r
5191       break;\r
5192 \r
5193     case IDM_TruncateGame:\r
5194       TruncateGameEvent();\r
5195       break;\r
5196 \r
5197     case IDM_MoveNow:\r
5198       MoveNowEvent();\r
5199       break;\r
5200 \r
5201     case IDM_RetractMove:\r
5202       RetractMoveEvent();\r
5203       break;\r
5204 \r
5205     case IDM_FlipView:\r
5206       flipView = !flipView;\r
5207       DrawPosition(FALSE, NULL);\r
5208       break;\r
5209 \r
5210     case IDM_FlipClock:\r
5211       flipClock = !flipClock;\r
5212       DisplayBothClocks();\r
5213       DisplayLogos();\r
5214       break;\r
5215 \r
5216     case IDM_MuteSounds:\r
5217       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5218       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5219                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5220       break;\r
5221 \r
5222     case IDM_GeneralOptions:\r
5223       GeneralOptionsPopup(hwnd);\r
5224       DrawPosition(TRUE, NULL);\r
5225       break;\r
5226 \r
5227     case IDM_BoardOptions:\r
5228       BoardOptionsPopup(hwnd);\r
5229       break;\r
5230 \r
5231     case IDM_ThemeOptions:\r
5232       ThemeOptionsPopup(hwnd);\r
5233       break;\r
5234 \r
5235     case IDM_EnginePlayOptions:\r
5236       EnginePlayOptionsPopup(hwnd);\r
5237       break;\r
5238 \r
5239     case IDM_Engine1Options:\r
5240       EngineOptionsPopup(hwnd, &first);\r
5241       break;\r
5242 \r
5243     case IDM_Engine2Options:\r
5244       savedHwnd = hwnd;\r
5245       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5246       EngineOptionsPopup(hwnd, &second);\r
5247       break;\r
5248 \r
5249     case IDM_OptionsUCI:\r
5250       UciOptionsPopup(hwnd);\r
5251       break;\r
5252 \r
5253     case IDM_Tourney:\r
5254       TourneyPopup(hwnd);\r
5255       break;\r
5256 \r
5257     case IDM_IcsOptions:\r
5258       IcsOptionsPopup(hwnd);\r
5259       break;\r
5260 \r
5261     case IDM_Fonts:\r
5262       FontsOptionsPopup(hwnd);\r
5263       break;\r
5264 \r
5265     case IDM_Sounds:\r
5266       SoundOptionsPopup(hwnd);\r
5267       break;\r
5268 \r
5269     case IDM_CommPort:\r
5270       CommPortOptionsPopup(hwnd);\r
5271       break;\r
5272 \r
5273     case IDM_LoadOptions:\r
5274       LoadOptionsPopup(hwnd);\r
5275       break;\r
5276 \r
5277     case IDM_SaveOptions:\r
5278       SaveOptionsPopup(hwnd);\r
5279       break;\r
5280 \r
5281     case IDM_TimeControl:\r
5282       TimeControlOptionsPopup(hwnd);\r
5283       break;\r
5284 \r
5285     case IDM_SaveSettings:\r
5286       SaveSettings(settingsFileName);\r
5287       break;\r
5288 \r
5289     case IDM_SaveSettingsOnExit:\r
5290       saveSettingsOnExit = !saveSettingsOnExit;\r
5291       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5292                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5293                                          MF_CHECKED : MF_UNCHECKED));\r
5294       break;\r
5295 \r
5296     case IDM_Hint:\r
5297       HintEvent();\r
5298       break;\r
5299 \r
5300     case IDM_Book:\r
5301       BookEvent();\r
5302       break;\r
5303 \r
5304     case IDM_AboutGame:\r
5305       AboutGameEvent();\r
5306       break;\r
5307 \r
5308     case IDM_Debug:\r
5309       appData.debugMode = !appData.debugMode;\r
5310       if (appData.debugMode) {\r
5311         char dir[MSG_SIZ];\r
5312         GetCurrentDirectory(MSG_SIZ, dir);\r
5313         SetCurrentDirectory(installDir);\r
5314         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5315         SetCurrentDirectory(dir);\r
5316         setbuf(debugFP, NULL);\r
5317       } else {\r
5318         fclose(debugFP);\r
5319         debugFP = NULL;\r
5320       }\r
5321       break;\r
5322 \r
5323     case IDM_HELPCONTENTS:\r
5324       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5325           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5326           MessageBox (GetFocus(),\r
5327                     _("Unable to activate help"),\r
5328                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5329       }\r
5330       break;\r
5331 \r
5332     case IDM_HELPSEARCH:\r
5333         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5334             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5335         MessageBox (GetFocus(),\r
5336                     _("Unable to activate help"),\r
5337                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5338       }\r
5339       break;\r
5340 \r
5341     case IDM_HELPHELP:\r
5342       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5343         MessageBox (GetFocus(),\r
5344                     _("Unable to activate help"),\r
5345                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5346       }\r
5347       break;\r
5348 \r
5349     case IDM_ABOUT:\r
5350       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5351       DialogBox(hInst, \r
5352         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5353         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5354       FreeProcInstance(lpProc);\r
5355       break;\r
5356 \r
5357     case IDM_DirectCommand1:\r
5358       AskQuestionEvent(_("Direct Command"),\r
5359                        _("Send to chess program:"), "", "1");\r
5360       break;\r
5361     case IDM_DirectCommand2:\r
5362       AskQuestionEvent(_("Direct Command"),\r
5363                        _("Send to second chess program:"), "", "2");\r
5364       break;\r
5365 \r
5366     case EP_WhitePawn:\r
5367       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5368       fromX = fromY = -1;\r
5369       break;\r
5370 \r
5371     case EP_WhiteKnight:\r
5372       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5373       fromX = fromY = -1;\r
5374       break;\r
5375 \r
5376     case EP_WhiteBishop:\r
5377       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5378       fromX = fromY = -1;\r
5379       break;\r
5380 \r
5381     case EP_WhiteRook:\r
5382       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5383       fromX = fromY = -1;\r
5384       break;\r
5385 \r
5386     case EP_WhiteQueen:\r
5387       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5388       fromX = fromY = -1;\r
5389       break;\r
5390 \r
5391     case EP_WhiteFerz:\r
5392       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5393       fromX = fromY = -1;\r
5394       break;\r
5395 \r
5396     case EP_WhiteWazir:\r
5397       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5398       fromX = fromY = -1;\r
5399       break;\r
5400 \r
5401     case EP_WhiteAlfil:\r
5402       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5403       fromX = fromY = -1;\r
5404       break;\r
5405 \r
5406     case EP_WhiteCannon:\r
5407       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5408       fromX = fromY = -1;\r
5409       break;\r
5410 \r
5411     case EP_WhiteCardinal:\r
5412       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5413       fromX = fromY = -1;\r
5414       break;\r
5415 \r
5416     case EP_WhiteMarshall:\r
5417       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5418       fromX = fromY = -1;\r
5419       break;\r
5420 \r
5421     case EP_WhiteKing:\r
5422       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5423       fromX = fromY = -1;\r
5424       break;\r
5425 \r
5426     case EP_BlackPawn:\r
5427       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5428       fromX = fromY = -1;\r
5429       break;\r
5430 \r
5431     case EP_BlackKnight:\r
5432       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5433       fromX = fromY = -1;\r
5434       break;\r
5435 \r
5436     case EP_BlackBishop:\r
5437       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5438       fromX = fromY = -1;\r
5439       break;\r
5440 \r
5441     case EP_BlackRook:\r
5442       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5443       fromX = fromY = -1;\r
5444       break;\r
5445 \r
5446     case EP_BlackQueen:\r
5447       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5448       fromX = fromY = -1;\r
5449       break;\r
5450 \r
5451     case EP_BlackFerz:\r
5452       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5453       fromX = fromY = -1;\r
5454       break;\r
5455 \r
5456     case EP_BlackWazir:\r
5457       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5458       fromX = fromY = -1;\r
5459       break;\r
5460 \r
5461     case EP_BlackAlfil:\r
5462       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5463       fromX = fromY = -1;\r
5464       break;\r
5465 \r
5466     case EP_BlackCannon:\r
5467       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5468       fromX = fromY = -1;\r
5469       break;\r
5470 \r
5471     case EP_BlackCardinal:\r
5472       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5473       fromX = fromY = -1;\r
5474       break;\r
5475 \r
5476     case EP_BlackMarshall:\r
5477       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5478       fromX = fromY = -1;\r
5479       break;\r
5480 \r
5481     case EP_BlackKing:\r
5482       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5483       fromX = fromY = -1;\r
5484       break;\r
5485 \r
5486     case EP_EmptySquare:\r
5487       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5488       fromX = fromY = -1;\r
5489       break;\r
5490 \r
5491     case EP_ClearBoard:\r
5492       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5493       fromX = fromY = -1;\r
5494       break;\r
5495 \r
5496     case EP_White:\r
5497       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5498       fromX = fromY = -1;\r
5499       break;\r
5500 \r
5501     case EP_Black:\r
5502       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5503       fromX = fromY = -1;\r
5504       break;\r
5505 \r
5506     case EP_Promote:\r
5507       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5508       fromX = fromY = -1;\r
5509       break;\r
5510 \r
5511     case EP_Demote:\r
5512       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5513       fromX = fromY = -1;\r
5514       break;\r
5515 \r
5516     case DP_Pawn:\r
5517       DropMenuEvent(WhitePawn, fromX, fromY);\r
5518       fromX = fromY = -1;\r
5519       break;\r
5520 \r
5521     case DP_Knight:\r
5522       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5523       fromX = fromY = -1;\r
5524       break;\r
5525 \r
5526     case DP_Bishop:\r
5527       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5528       fromX = fromY = -1;\r
5529       break;\r
5530 \r
5531     case DP_Rook:\r
5532       DropMenuEvent(WhiteRook, fromX, fromY);\r
5533       fromX = fromY = -1;\r
5534       break;\r
5535 \r
5536     case DP_Queen:\r
5537       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5538       fromX = fromY = -1;\r
5539       break;\r
5540 \r
5541     case IDM_English:\r
5542       barbaric = 0; appData.language = "";\r
5543       TranslateMenus(0);\r
5544       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5545       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5546       lastChecked = wmId;\r
5547       break;\r
5548 \r
5549     default:\r
5550       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5551           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5552       else\r
5553       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5554           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5555           TranslateMenus(0);\r
5556           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5557           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5558           lastChecked = wmId;\r
5559           break;\r
5560       }\r
5561       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5562     }\r
5563     break;\r
5564 \r
5565   case WM_TIMER:\r
5566     switch (wParam) {\r
5567     case CLOCK_TIMER_ID:\r
5568       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5569       clockTimerEvent = 0;\r
5570       DecrementClocks(); /* call into back end */\r
5571       break;\r
5572     case LOAD_GAME_TIMER_ID:\r
5573       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5574       loadGameTimerEvent = 0;\r
5575       AutoPlayGameLoop(); /* call into back end */\r
5576       break;\r
5577     case ANALYSIS_TIMER_ID:\r
5578       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5579                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5580         AnalysisPeriodicEvent(0);\r
5581       } else {\r
5582         KillTimer(hwnd, analysisTimerEvent);\r
5583         analysisTimerEvent = 0;\r
5584       }\r
5585       break;\r
5586     case DELAYED_TIMER_ID:\r
5587       KillTimer(hwnd, delayedTimerEvent);\r
5588       delayedTimerEvent = 0;\r
5589       delayedTimerCallback();\r
5590       break;\r
5591     }\r
5592     break;\r
5593 \r
5594   case WM_USER_Input:\r
5595     InputEvent(hwnd, message, wParam, lParam);\r
5596     break;\r
5597 \r
5598   /* [AS] Also move "attached" child windows */\r
5599   case WM_WINDOWPOSCHANGING:\r
5600 \r
5601     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5602         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5603 \r
5604         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5605             /* Window is moving */\r
5606             RECT rcMain;\r
5607 \r
5608 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5609             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5610             rcMain.right  = wpMain.x + wpMain.width;\r
5611             rcMain.top    = wpMain.y;\r
5612             rcMain.bottom = wpMain.y + wpMain.height;\r
5613             \r
5614             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5615             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5616             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5617             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5618             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5619             wpMain.x = lpwp->x;\r
5620             wpMain.y = lpwp->y;\r
5621         }\r
5622     }\r
5623     break;\r
5624 \r
5625   /* [AS] Snapping */\r
5626   case WM_ENTERSIZEMOVE:\r
5627     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5628     if (hwnd == hwndMain) {\r
5629       doingSizing = TRUE;\r
5630       lastSizing = 0;\r
5631     }\r
5632     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5633     break;\r
5634 \r
5635   case WM_SIZING:\r
5636     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5637     if (hwnd == hwndMain) {\r
5638       lastSizing = wParam;\r
5639     }\r
5640     break;\r
5641 \r
5642   case WM_MOVING:\r
5643     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5644       return OnMoving( &sd, hwnd, wParam, lParam );\r
5645 \r
5646   case WM_EXITSIZEMOVE:\r
5647     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5648     if (hwnd == hwndMain) {\r
5649       RECT client;\r
5650       doingSizing = FALSE;\r
5651       InvalidateRect(hwnd, &boardRect, FALSE);\r
5652       GetClientRect(hwnd, &client);\r
5653       ResizeBoard(client.right, client.bottom, lastSizing);\r
5654       lastSizing = 0;\r
5655       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5656     }\r
5657     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5658     break;\r
5659 \r
5660   case WM_DESTROY: /* message: window being destroyed */\r
5661     PostQuitMessage(0);\r
5662     break;\r
5663 \r
5664   case WM_CLOSE:\r
5665     if (hwnd == hwndMain) {\r
5666       ExitEvent(0);\r
5667     }\r
5668     break;\r
5669 \r
5670   default:      /* Passes it on if unprocessed */\r
5671     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5672   }\r
5673   return 0;\r
5674 }\r
5675 \r
5676 /*---------------------------------------------------------------------------*\\r
5677  *\r
5678  * Misc utility routines\r
5679  *\r
5680 \*---------------------------------------------------------------------------*/\r
5681 \r
5682 /*\r
5683  * Decent random number generator, at least not as bad as Windows\r
5684  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5685  */\r
5686 unsigned int randstate;\r
5687 \r
5688 int\r
5689 myrandom(void)\r
5690 {\r
5691   randstate = randstate * 1664525 + 1013904223;\r
5692   return (int) randstate & 0x7fffffff;\r
5693 }\r
5694 \r
5695 void\r
5696 mysrandom(unsigned int seed)\r
5697 {\r
5698   randstate = seed;\r
5699 }\r
5700 \r
5701 \r
5702 /* \r
5703  * returns TRUE if user selects a different color, FALSE otherwise \r
5704  */\r
5705 \r
5706 BOOL\r
5707 ChangeColor(HWND hwnd, COLORREF *which)\r
5708 {\r
5709   static BOOL firstTime = TRUE;\r
5710   static DWORD customColors[16];\r
5711   CHOOSECOLOR cc;\r
5712   COLORREF newcolor;\r
5713   int i;\r
5714   ColorClass ccl;\r
5715 \r
5716   if (firstTime) {\r
5717     /* Make initial colors in use available as custom colors */\r
5718     /* Should we put the compiled-in defaults here instead? */\r
5719     i = 0;\r
5720     customColors[i++] = lightSquareColor & 0xffffff;\r
5721     customColors[i++] = darkSquareColor & 0xffffff;\r
5722     customColors[i++] = whitePieceColor & 0xffffff;\r
5723     customColors[i++] = blackPieceColor & 0xffffff;\r
5724     customColors[i++] = highlightSquareColor & 0xffffff;\r
5725     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5726 \r
5727     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5728       customColors[i++] = textAttribs[ccl].color;\r
5729     }\r
5730     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5731     firstTime = FALSE;\r
5732   }\r
5733 \r
5734   cc.lStructSize = sizeof(cc);\r
5735   cc.hwndOwner = hwnd;\r
5736   cc.hInstance = NULL;\r
5737   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5738   cc.lpCustColors = (LPDWORD) customColors;\r
5739   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5740 \r
5741   if (!ChooseColor(&cc)) return FALSE;\r
5742 \r
5743   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5744   if (newcolor == *which) return FALSE;\r
5745   *which = newcolor;\r
5746   return TRUE;\r
5747 \r
5748   /*\r
5749   InitDrawingColors();\r
5750   InvalidateRect(hwnd, &boardRect, FALSE);\r
5751   */\r
5752 }\r
5753 \r
5754 BOOLEAN\r
5755 MyLoadSound(MySound *ms)\r
5756 {\r
5757   BOOL ok = FALSE;\r
5758   struct stat st;\r
5759   FILE *f;\r
5760 \r
5761   if (ms->data && ms->flag) free(ms->data);\r
5762   ms->data = NULL;\r
5763 \r
5764   switch (ms->name[0]) {\r
5765   case NULLCHAR:\r
5766     /* Silence */\r
5767     ok = TRUE;\r
5768     break;\r
5769   case '$':\r
5770     /* System sound from Control Panel.  Don't preload here. */\r
5771     ok = TRUE;\r
5772     break;\r
5773   case '!':\r
5774     if (ms->name[1] == NULLCHAR) {\r
5775       /* "!" alone = silence */\r
5776       ok = TRUE;\r
5777     } else {\r
5778       /* Builtin wave resource.  Error if not found. */\r
5779       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5780       if (h == NULL) break;\r
5781       ms->data = (void *)LoadResource(hInst, h);\r
5782       ms->flag = 0; // not maloced, so cannot be freed!\r
5783       if (h == NULL) break;\r
5784       ok = TRUE;\r
5785     }\r
5786     break;\r
5787   default:\r
5788     /* .wav file.  Error if not found. */\r
5789     f = fopen(ms->name, "rb");\r
5790     if (f == NULL) break;\r
5791     if (fstat(fileno(f), &st) < 0) break;\r
5792     ms->data = malloc(st.st_size);\r
5793     ms->flag = 1;\r
5794     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5795     fclose(f);\r
5796     ok = TRUE;\r
5797     break;\r
5798   }\r
5799   if (!ok) {\r
5800     char buf[MSG_SIZ];\r
5801       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5802     DisplayError(buf, GetLastError());\r
5803   }\r
5804   return ok;\r
5805 }\r
5806 \r
5807 BOOLEAN\r
5808 MyPlaySound(MySound *ms)\r
5809 {\r
5810   BOOLEAN ok = FALSE;\r
5811 \r
5812   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5813   switch (ms->name[0]) {\r
5814   case NULLCHAR:\r
5815         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5816     /* Silence */\r
5817     ok = TRUE;\r
5818     break;\r
5819   case '$':\r
5820     /* System sound from Control Panel (deprecated feature).\r
5821        "$" alone or an unset sound name gets default beep (still in use). */\r
5822     if (ms->name[1]) {\r
5823       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5824     }\r
5825     if (!ok) ok = MessageBeep(MB_OK);\r
5826     break; \r
5827   case '!':\r
5828     /* Builtin wave resource, or "!" alone for silence */\r
5829     if (ms->name[1]) {\r
5830       if (ms->data == NULL) return FALSE;\r
5831       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5832     } else {\r
5833       ok = TRUE;\r
5834     }\r
5835     break;\r
5836   default:\r
5837     /* .wav file.  Error if not found. */\r
5838     if (ms->data == NULL) return FALSE;\r
5839     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5840     break;\r
5841   }\r
5842   /* Don't print an error: this can happen innocently if the sound driver\r
5843      is busy; for instance, if another instance of WinBoard is playing\r
5844      a sound at about the same time. */\r
5845   return ok;\r
5846 }\r
5847 \r
5848 \r
5849 LRESULT CALLBACK\r
5850 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5851 {\r
5852   BOOL ok;\r
5853   OPENFILENAME *ofn;\r
5854   static UINT *number; /* gross that this is static */\r
5855 \r
5856   switch (message) {\r
5857   case WM_INITDIALOG: /* message: initialize dialog box */\r
5858     /* Center the dialog over the application window */\r
5859     ofn = (OPENFILENAME *) lParam;\r
5860     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5861       number = (UINT *) ofn->lCustData;\r
5862       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5863     } else {\r
5864       number = NULL;\r
5865     }\r
5866     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5867     Translate(hDlg, 1536);\r
5868     return FALSE;  /* Allow for further processing */\r
5869 \r
5870   case WM_COMMAND:\r
5871     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5872       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5873     }\r
5874     return FALSE;  /* Allow for further processing */\r
5875   }\r
5876   return FALSE;\r
5877 }\r
5878 \r
5879 UINT APIENTRY\r
5880 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5881 {\r
5882   static UINT *number;\r
5883   OPENFILENAME *ofname;\r
5884   OFNOTIFY *ofnot;\r
5885   switch (uiMsg) {\r
5886   case WM_INITDIALOG:\r
5887     Translate(hdlg, DLG_IndexNumber);\r
5888     ofname = (OPENFILENAME *)lParam;\r
5889     number = (UINT *)(ofname->lCustData);\r
5890     break;\r
5891   case WM_NOTIFY:\r
5892     ofnot = (OFNOTIFY *)lParam;\r
5893     if (ofnot->hdr.code == CDN_FILEOK) {\r
5894       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5895     }\r
5896     break;\r
5897   }\r
5898   return 0;\r
5899 }\r
5900 \r
5901 \r
5902 FILE *\r
5903 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5904                char *nameFilt, char *dlgTitle, UINT *number,\r
5905                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5906 {\r
5907   OPENFILENAME openFileName;\r
5908   char buf1[MSG_SIZ];\r
5909   FILE *f;\r
5910 \r
5911   if (fileName == NULL) fileName = buf1;\r
5912   if (defName == NULL) {\r
5913     safeStrCpy(fileName, "*.", 3 );\r
5914     strcat(fileName, defExt);\r
5915   } else {\r
5916     safeStrCpy(fileName, defName, MSG_SIZ );\r
5917   }\r
5918     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5919   if (number) *number = 0;\r
5920 \r
5921   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5922   openFileName.hwndOwner         = hwnd;\r
5923   openFileName.hInstance         = (HANDLE) hInst;\r
5924   openFileName.lpstrFilter       = nameFilt;\r
5925   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5926   openFileName.nMaxCustFilter    = 0L;\r
5927   openFileName.nFilterIndex      = 1L;\r
5928   openFileName.lpstrFile         = fileName;\r
5929   openFileName.nMaxFile          = MSG_SIZ;\r
5930   openFileName.lpstrFileTitle    = fileTitle;\r
5931   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5932   openFileName.lpstrInitialDir   = NULL;\r
5933   openFileName.lpstrTitle        = dlgTitle;\r
5934   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5935     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5936     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5937     | (oldDialog ? 0 : OFN_EXPLORER);\r
5938   openFileName.nFileOffset       = 0;\r
5939   openFileName.nFileExtension    = 0;\r
5940   openFileName.lpstrDefExt       = defExt;\r
5941   openFileName.lCustData         = (LONG) number;\r
5942   openFileName.lpfnHook          = oldDialog ?\r
5943     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5944   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5945 \r
5946   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5947                         GetOpenFileName(&openFileName)) {\r
5948     /* open the file */\r
5949     f = fopen(openFileName.lpstrFile, write);\r
5950     if (f == NULL) {\r
5951       MessageBox(hwnd, _("File open failed"), NULL,\r
5952                  MB_OK|MB_ICONEXCLAMATION);\r
5953       return NULL;\r
5954     }\r
5955   } else {\r
5956     int err = CommDlgExtendedError();\r
5957     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5958     return FALSE;\r
5959   }\r
5960   return f;\r
5961 }\r
5962 \r
5963 \r
5964 \r
5965 VOID APIENTRY\r
5966 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5967 {\r
5968   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5969 \r
5970   /*\r
5971    * Get the first pop-up menu in the menu template. This is the\r
5972    * menu that TrackPopupMenu displays.\r
5973    */\r
5974   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5975   TranslateOneMenu(10, hmenuTrackPopup);\r
5976 \r
5977   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5978 \r
5979   /*\r
5980    * TrackPopup uses screen coordinates, so convert the\r
5981    * coordinates of the mouse click to screen coordinates.\r
5982    */\r
5983   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5984 \r
5985   /* Draw and track the floating pop-up menu. */\r
5986   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5987                  pt.x, pt.y, 0, hwnd, NULL);\r
5988 \r
5989   /* Destroy the menu.*/\r
5990   DestroyMenu(hmenu);\r
5991 }\r
5992    \r
5993 typedef struct {\r
5994   HWND hDlg, hText;\r
5995   int sizeX, sizeY, newSizeX, newSizeY;\r
5996   HDWP hdwp;\r
5997 } ResizeEditPlusButtonsClosure;\r
5998 \r
5999 BOOL CALLBACK\r
6000 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6001 {\r
6002   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6003   RECT rect;\r
6004   POINT pt;\r
6005 \r
6006   if (hChild == cl->hText) return TRUE;\r
6007   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6008   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6009   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6010   ScreenToClient(cl->hDlg, &pt);\r
6011   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6012     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6013   return TRUE;\r
6014 }\r
6015 \r
6016 /* Resize a dialog that has a (rich) edit field filling most of\r
6017    the top, with a row of buttons below */\r
6018 VOID\r
6019 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6020 {\r
6021   RECT rectText;\r
6022   int newTextHeight, newTextWidth;\r
6023   ResizeEditPlusButtonsClosure cl;\r
6024   \r
6025   /*if (IsIconic(hDlg)) return;*/\r
6026   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6027   \r
6028   cl.hdwp = BeginDeferWindowPos(8);\r
6029 \r
6030   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6031   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6032   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6033   if (newTextHeight < 0) {\r
6034     newSizeY += -newTextHeight;\r
6035     newTextHeight = 0;\r
6036   }\r
6037   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6038     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6039 \r
6040   cl.hDlg = hDlg;\r
6041   cl.hText = hText;\r
6042   cl.sizeX = sizeX;\r
6043   cl.sizeY = sizeY;\r
6044   cl.newSizeX = newSizeX;\r
6045   cl.newSizeY = newSizeY;\r
6046   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6047 \r
6048   EndDeferWindowPos(cl.hdwp);\r
6049 }\r
6050 \r
6051 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6052 {\r
6053     RECT    rChild, rParent;\r
6054     int     wChild, hChild, wParent, hParent;\r
6055     int     wScreen, hScreen, xNew, yNew;\r
6056     HDC     hdc;\r
6057 \r
6058     /* Get the Height and Width of the child window */\r
6059     GetWindowRect (hwndChild, &rChild);\r
6060     wChild = rChild.right - rChild.left;\r
6061     hChild = rChild.bottom - rChild.top;\r
6062 \r
6063     /* Get the Height and Width of the parent window */\r
6064     GetWindowRect (hwndParent, &rParent);\r
6065     wParent = rParent.right - rParent.left;\r
6066     hParent = rParent.bottom - rParent.top;\r
6067 \r
6068     /* Get the display limits */\r
6069     hdc = GetDC (hwndChild);\r
6070     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6071     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6072     ReleaseDC(hwndChild, hdc);\r
6073 \r
6074     /* Calculate new X position, then adjust for screen */\r
6075     xNew = rParent.left + ((wParent - wChild) /2);\r
6076     if (xNew < 0) {\r
6077         xNew = 0;\r
6078     } else if ((xNew+wChild) > wScreen) {\r
6079         xNew = wScreen - wChild;\r
6080     }\r
6081 \r
6082     /* Calculate new Y position, then adjust for screen */\r
6083     if( mode == 0 ) {\r
6084         yNew = rParent.top  + ((hParent - hChild) /2);\r
6085     }\r
6086     else {\r
6087         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6088     }\r
6089 \r
6090     if (yNew < 0) {\r
6091         yNew = 0;\r
6092     } else if ((yNew+hChild) > hScreen) {\r
6093         yNew = hScreen - hChild;\r
6094     }\r
6095 \r
6096     /* Set it, and return */\r
6097     return SetWindowPos (hwndChild, NULL,\r
6098                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6099 }\r
6100 \r
6101 /* Center one window over another */\r
6102 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6103 {\r
6104     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6105 }\r
6106 \r
6107 /*---------------------------------------------------------------------------*\\r
6108  *\r
6109  * Startup Dialog functions\r
6110  *\r
6111 \*---------------------------------------------------------------------------*/\r
6112 void\r
6113 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6114 {\r
6115   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6116 \r
6117   while (*cd != NULL) {\r
6118     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6119     cd++;\r
6120   }\r
6121 }\r
6122 \r
6123 void\r
6124 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6125 {\r
6126   char buf1[MAX_ARG_LEN];\r
6127   int len;\r
6128 \r
6129   if (str[0] == '@') {\r
6130     FILE* f = fopen(str + 1, "r");\r
6131     if (f == NULL) {\r
6132       DisplayFatalError(str + 1, errno, 2);\r
6133       return;\r
6134     }\r
6135     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6136     fclose(f);\r
6137     buf1[len] = NULLCHAR;\r
6138     str = buf1;\r
6139   }\r
6140 \r
6141   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6142 \r
6143   for (;;) {\r
6144     char buf[MSG_SIZ];\r
6145     char *end = strchr(str, '\n');\r
6146     if (end == NULL) return;\r
6147     memcpy(buf, str, end - str);\r
6148     buf[end - str] = NULLCHAR;\r
6149     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6150     str = end + 1;\r
6151   }\r
6152 }\r
6153 \r
6154 void\r
6155 SetStartupDialogEnables(HWND hDlg)\r
6156 {\r
6157   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6158     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6159     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6160   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6161     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6162   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6163     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6164   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6165     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6166   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6167     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6168     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6169     IsDlgButtonChecked(hDlg, OPT_View));\r
6170 }\r
6171 \r
6172 char *\r
6173 QuoteForFilename(char *filename)\r
6174 {\r
6175   int dquote, space;\r
6176   dquote = strchr(filename, '"') != NULL;\r
6177   space = strchr(filename, ' ') != NULL;\r
6178   if (dquote || space) {\r
6179     if (dquote) {\r
6180       return "'";\r
6181     } else {\r
6182       return "\"";\r
6183     }\r
6184   } else {\r
6185     return "";\r
6186   }\r
6187 }\r
6188 \r
6189 VOID\r
6190 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6191 {\r
6192   char buf[MSG_SIZ];\r
6193   char *q;\r
6194 \r
6195   InitComboStringsFromOption(hwndCombo, nthnames);\r
6196   q = QuoteForFilename(nthcp);\r
6197     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6198   if (*nthdir != NULLCHAR) {\r
6199     q = QuoteForFilename(nthdir);\r
6200       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6201   }\r
6202   if (*nthcp == NULLCHAR) {\r
6203     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6204   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6205     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6206     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6207   }\r
6208 }\r
6209 \r
6210 LRESULT CALLBACK\r
6211 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6212 {\r
6213   char buf[MSG_SIZ];\r
6214   HANDLE hwndCombo;\r
6215   char *p;\r
6216 \r
6217   switch (message) {\r
6218   case WM_INITDIALOG:\r
6219     /* Center the dialog */\r
6220     CenterWindow (hDlg, GetDesktopWindow());\r
6221     Translate(hDlg, DLG_Startup);\r
6222     /* Initialize the dialog items */\r
6223     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6224                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6225                   firstChessProgramNames);\r
6226     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6227                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6228                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6229     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6230     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6231       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6232     if (*appData.icsHelper != NULLCHAR) {\r
6233       char *q = QuoteForFilename(appData.icsHelper);\r
6234       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6235     }\r
6236     if (*appData.icsHost == NULLCHAR) {\r
6237       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6238       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6239     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6240       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6241       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6242     }\r
6243 \r
6244     if (appData.icsActive) {\r
6245       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6246     }\r
6247     else if (appData.noChessProgram) {\r
6248       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6249     }\r
6250     else {\r
6251       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6252     }\r
6253 \r
6254     SetStartupDialogEnables(hDlg);\r
6255     return TRUE;\r
6256 \r
6257   case WM_COMMAND:\r
6258     switch (LOWORD(wParam)) {\r
6259     case IDOK:\r
6260       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6261         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6262         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6263         p = buf;\r
6264         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6265         ParseArgs(StringGet, &p);\r
6266         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6267         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6268         p = buf;\r
6269         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6270         ParseArgs(StringGet, &p);\r
6271         SwapEngines(singleList); // ... and then make it 'second'\r
6272 \r
6273         appData.noChessProgram = FALSE;\r
6274         appData.icsActive = FALSE;\r
6275       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6276         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6277         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6278         p = buf;\r
6279         ParseArgs(StringGet, &p);\r
6280         if (appData.zippyPlay) {\r
6281           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6282           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6283           p = buf;\r
6284           ParseArgs(StringGet, &p);\r
6285         }\r
6286       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6287         appData.noChessProgram = TRUE;\r
6288         appData.icsActive = FALSE;\r
6289       } else {\r
6290         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6291                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6292         return TRUE;\r
6293       }\r
6294       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6295         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6296         p = buf;\r
6297         ParseArgs(StringGet, &p);\r
6298       }\r
6299       EndDialog(hDlg, TRUE);\r
6300       return TRUE;\r
6301 \r
6302     case IDCANCEL:\r
6303       ExitEvent(0);\r
6304       return TRUE;\r
6305 \r
6306     case IDM_HELPCONTENTS:\r
6307       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6308         MessageBox (GetFocus(),\r
6309                     _("Unable to activate help"),\r
6310                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6311       }\r
6312       break;\r
6313 \r
6314     default:\r
6315       SetStartupDialogEnables(hDlg);\r
6316       break;\r
6317     }\r
6318     break;\r
6319   }\r
6320   return FALSE;\r
6321 }\r
6322 \r
6323 /*---------------------------------------------------------------------------*\\r
6324  *\r
6325  * About box dialog functions\r
6326  *\r
6327 \*---------------------------------------------------------------------------*/\r
6328 \r
6329 /* Process messages for "About" dialog box */\r
6330 LRESULT CALLBACK\r
6331 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6332 {\r
6333   switch (message) {\r
6334   case WM_INITDIALOG: /* message: initialize dialog box */\r
6335     /* Center the dialog over the application window */\r
6336     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6337     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6338     Translate(hDlg, ABOUTBOX);\r
6339     JAWS_COPYRIGHT\r
6340     return (TRUE);\r
6341 \r
6342   case WM_COMMAND: /* message: received a command */\r
6343     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6344         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6345       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6346       return (TRUE);\r
6347     }\r
6348     break;\r
6349   }\r
6350   return (FALSE);\r
6351 }\r
6352 \r
6353 /*---------------------------------------------------------------------------*\\r
6354  *\r
6355  * Comment Dialog functions\r
6356  *\r
6357 \*---------------------------------------------------------------------------*/\r
6358 \r
6359 LRESULT CALLBACK\r
6360 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6361 {\r
6362   static HANDLE hwndText = NULL;\r
6363   int len, newSizeX, newSizeY, flags;\r
6364   static int sizeX, sizeY;\r
6365   char *str;\r
6366   RECT rect;\r
6367   MINMAXINFO *mmi;\r
6368 \r
6369   switch (message) {\r
6370   case WM_INITDIALOG: /* message: initialize dialog box */\r
6371     /* Initialize the dialog items */\r
6372     Translate(hDlg, DLG_EditComment);\r
6373     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6374     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6375     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6376     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6377     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6378     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6379     SetWindowText(hDlg, commentTitle);\r
6380     if (editComment) {\r
6381       SetFocus(hwndText);\r
6382     } else {\r
6383       SetFocus(GetDlgItem(hDlg, IDOK));\r
6384     }\r
6385     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6386                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6387                 MAKELPARAM(FALSE, 0));\r
6388     /* Size and position the dialog */\r
6389     if (!commentDialog) {\r
6390       commentDialog = hDlg;\r
6391       flags = SWP_NOZORDER;\r
6392       GetClientRect(hDlg, &rect);\r
6393       sizeX = rect.right;\r
6394       sizeY = rect.bottom;\r
6395       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6396           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6397         WINDOWPLACEMENT wp;\r
6398         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6399         wp.length = sizeof(WINDOWPLACEMENT);\r
6400         wp.flags = 0;\r
6401         wp.showCmd = SW_SHOW;\r
6402         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6403         wp.rcNormalPosition.left = wpComment.x;\r
6404         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6405         wp.rcNormalPosition.top = wpComment.y;\r
6406         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6407         SetWindowPlacement(hDlg, &wp);\r
6408 \r
6409         GetClientRect(hDlg, &rect);\r
6410         newSizeX = rect.right;\r
6411         newSizeY = rect.bottom;\r
6412         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6413                               newSizeX, newSizeY);\r
6414         sizeX = newSizeX;\r
6415         sizeY = newSizeY;\r
6416       }\r
6417     }\r
6418     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6419     return FALSE;\r
6420 \r
6421   case WM_COMMAND: /* message: received a command */\r
6422     switch (LOWORD(wParam)) {\r
6423     case IDOK:\r
6424       if (editComment) {\r
6425         char *p, *q;\r
6426         /* Read changed options from the dialog box */\r
6427         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6428         len = GetWindowTextLength(hwndText);\r
6429         str = (char *) malloc(len + 1);\r
6430         GetWindowText(hwndText, str, len + 1);\r
6431         p = q = str;\r
6432         while (*q) {\r
6433           if (*q == '\r')\r
6434             q++;\r
6435           else\r
6436             *p++ = *q++;\r
6437         }\r
6438         *p = NULLCHAR;\r
6439         ReplaceComment(commentIndex, str);\r
6440         free(str);\r
6441       }\r
6442       CommentPopDown();\r
6443       return TRUE;\r
6444 \r
6445     case IDCANCEL:\r
6446     case OPT_CancelComment:\r
6447       CommentPopDown();\r
6448       return TRUE;\r
6449 \r
6450     case OPT_ClearComment:\r
6451       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6452       break;\r
6453 \r
6454     case OPT_EditComment:\r
6455       EditCommentEvent();\r
6456       return TRUE;\r
6457 \r
6458     default:\r
6459       break;\r
6460     }\r
6461     break;\r
6462 \r
6463   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6464         if( wParam == OPT_CommentText ) {\r
6465             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6466 \r
6467             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6468                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6469                 POINTL pt;\r
6470                 LRESULT index;\r
6471 \r
6472                 pt.x = LOWORD( lpMF->lParam );\r
6473                 pt.y = HIWORD( lpMF->lParam );\r
6474 \r
6475                 if(lpMF->msg == WM_CHAR) {\r
6476                         CHARRANGE sel;\r
6477                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6478                         index = sel.cpMin;\r
6479                 } else\r
6480                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6481 \r
6482                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6483                 len = GetWindowTextLength(hwndText);\r
6484                 str = (char *) malloc(len + 1);\r
6485                 GetWindowText(hwndText, str, len + 1);\r
6486                 ReplaceComment(commentIndex, str);\r
6487                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6488                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6489                 free(str);\r
6490 \r
6491                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6492                 lpMF->msg = WM_USER;\r
6493 \r
6494                 return TRUE;\r
6495             }\r
6496         }\r
6497         break;\r
6498 \r
6499   case WM_SIZE:\r
6500     newSizeX = LOWORD(lParam);\r
6501     newSizeY = HIWORD(lParam);\r
6502     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6503     sizeX = newSizeX;\r
6504     sizeY = newSizeY;\r
6505     break;\r
6506 \r
6507   case WM_GETMINMAXINFO:\r
6508     /* Prevent resizing window too small */\r
6509     mmi = (MINMAXINFO *) lParam;\r
6510     mmi->ptMinTrackSize.x = 100;\r
6511     mmi->ptMinTrackSize.y = 100;\r
6512     break;\r
6513   }\r
6514   return FALSE;\r
6515 }\r
6516 \r
6517 VOID\r
6518 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6519 {\r
6520   FARPROC lpProc;\r
6521   char *p, *q;\r
6522 \r
6523   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6524 \r
6525   if (str == NULL) str = "";\r
6526   p = (char *) malloc(2 * strlen(str) + 2);\r
6527   q = p;\r
6528   while (*str) {\r
6529     if (*str == '\n') *q++ = '\r';\r
6530     *q++ = *str++;\r
6531   }\r
6532   *q = NULLCHAR;\r
6533   if (commentText != NULL) free(commentText);\r
6534 \r
6535   commentIndex = index;\r
6536   commentTitle = title;\r
6537   commentText = p;\r
6538   editComment = edit;\r
6539 \r
6540   if (commentDialog) {\r
6541     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6542     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6543   } else {\r
6544     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6545     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6546                  hwndMain, (DLGPROC)lpProc);\r
6547     FreeProcInstance(lpProc);\r
6548   }\r
6549   commentUp = TRUE;\r
6550 }\r
6551 \r
6552 \r
6553 /*---------------------------------------------------------------------------*\\r
6554  *\r
6555  * Type-in move dialog functions\r
6556  * \r
6557 \*---------------------------------------------------------------------------*/\r
6558 \r
6559 LRESULT CALLBACK\r
6560 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6561 {\r
6562   char move[MSG_SIZ];\r
6563   HWND hInput;\r
6564 \r
6565   switch (message) {\r
6566   case WM_INITDIALOG:\r
6567     move[0] = (char) lParam;\r
6568     move[1] = NULLCHAR;\r
6569     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6570     Translate(hDlg, DLG_TypeInMove);\r
6571     hInput = GetDlgItem(hDlg, OPT_Move);\r
6572     SetWindowText(hInput, move);\r
6573     SetFocus(hInput);\r
6574     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6575     return FALSE;\r
6576 \r
6577   case WM_COMMAND:\r
6578     switch (LOWORD(wParam)) {\r
6579     case IDOK:\r
6580 \r
6581       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6582       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6583       TypeInDoneEvent(move);\r
6584       EndDialog(hDlg, TRUE);\r
6585       return TRUE;\r
6586     case IDCANCEL:\r
6587       EndDialog(hDlg, FALSE);\r
6588       return TRUE;\r
6589     default:\r
6590       break;\r
6591     }\r
6592     break;\r
6593   }\r
6594   return FALSE;\r
6595 }\r
6596 \r
6597 VOID\r
6598 PopUpMoveDialog(char firstchar)\r
6599 {\r
6600     FARPROC lpProc;\r
6601 \r
6602       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6603       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6604         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6605       FreeProcInstance(lpProc);\r
6606 }\r
6607 \r
6608 /*---------------------------------------------------------------------------*\\r
6609  *\r
6610  * Type-in name dialog functions\r
6611  * \r
6612 \*---------------------------------------------------------------------------*/\r
6613 \r
6614 LRESULT CALLBACK\r
6615 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6616 {\r
6617   char move[MSG_SIZ];\r
6618   HWND hInput;\r
6619 \r
6620   switch (message) {\r
6621   case WM_INITDIALOG:\r
6622     move[0] = (char) lParam;\r
6623     move[1] = NULLCHAR;\r
6624     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6625     Translate(hDlg, DLG_TypeInName);\r
6626     hInput = GetDlgItem(hDlg, OPT_Name);\r
6627     SetWindowText(hInput, move);\r
6628     SetFocus(hInput);\r
6629     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6630     return FALSE;\r
6631 \r
6632   case WM_COMMAND:\r
6633     switch (LOWORD(wParam)) {\r
6634     case IDOK:\r
6635       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6636       appData.userName = strdup(move);\r
6637       SetUserLogo();\r
6638       SetGameInfo();\r
6639       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6640         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6641         DisplayTitle(move);\r
6642       }\r
6643 \r
6644 \r
6645       EndDialog(hDlg, TRUE);\r
6646       return TRUE;\r
6647     case IDCANCEL:\r
6648       EndDialog(hDlg, FALSE);\r
6649       return TRUE;\r
6650     default:\r
6651       break;\r
6652     }\r
6653     break;\r
6654   }\r
6655   return FALSE;\r
6656 }\r
6657 \r
6658 VOID\r
6659 PopUpNameDialog(char firstchar)\r
6660 {\r
6661     FARPROC lpProc;\r
6662     \r
6663       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6664       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6665         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6666       FreeProcInstance(lpProc);\r
6667 }\r
6668 \r
6669 /*---------------------------------------------------------------------------*\\r
6670  *\r
6671  *  Error dialogs\r
6672  * \r
6673 \*---------------------------------------------------------------------------*/\r
6674 \r
6675 /* Nonmodal error box */\r
6676 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6677                              WPARAM wParam, LPARAM lParam);\r
6678 \r
6679 VOID\r
6680 ErrorPopUp(char *title, char *content)\r
6681 {\r
6682   FARPROC lpProc;\r
6683   char *p, *q;\r
6684   BOOLEAN modal = hwndMain == NULL;\r
6685 \r
6686   p = content;\r
6687   q = errorMessage;\r
6688   while (*p) {\r
6689     if (*p == '\n') {\r
6690       if (modal) {\r
6691         *q++ = ' ';\r
6692         p++;\r
6693       } else {\r
6694         *q++ = '\r';\r
6695         *q++ = *p++;\r
6696       }\r
6697     } else {\r
6698       *q++ = *p++;\r
6699     }\r
6700   }\r
6701   *q = NULLCHAR;\r
6702   strncpy(errorTitle, title, sizeof(errorTitle));\r
6703   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6704   \r
6705   if (modal) {\r
6706     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6707   } else {\r
6708     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6709     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6710                  hwndMain, (DLGPROC)lpProc);\r
6711     FreeProcInstance(lpProc);\r
6712   }\r
6713 }\r
6714 \r
6715 VOID\r
6716 ErrorPopDown()\r
6717 {\r
6718   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6719   if (errorDialog == NULL) return;\r
6720   DestroyWindow(errorDialog);\r
6721   errorDialog = NULL;\r
6722   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6723 }\r
6724 \r
6725 LRESULT CALLBACK\r
6726 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6727 {\r
6728   HANDLE hwndText;\r
6729   RECT rChild;\r
6730 \r
6731   switch (message) {\r
6732   case WM_INITDIALOG:\r
6733     GetWindowRect(hDlg, &rChild);\r
6734 \r
6735     /*\r
6736     SetWindowPos(hDlg, NULL, rChild.left,\r
6737       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6738       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6739     */\r
6740 \r
6741     /* \r
6742         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6743         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6744         and it doesn't work when you resize the dialog.\r
6745         For now, just give it a default position.\r
6746     */\r
6747     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6748     Translate(hDlg, DLG_Error);\r
6749 \r
6750     errorDialog = hDlg;\r
6751     SetWindowText(hDlg, errorTitle);\r
6752     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6753     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6754     return FALSE;\r
6755 \r
6756   case WM_COMMAND:\r
6757     switch (LOWORD(wParam)) {\r
6758     case IDOK:\r
6759     case IDCANCEL:\r
6760       if (errorDialog == hDlg) errorDialog = NULL;\r
6761       DestroyWindow(hDlg);\r
6762       return TRUE;\r
6763 \r
6764     default:\r
6765       break;\r
6766     }\r
6767     break;\r
6768   }\r
6769   return FALSE;\r
6770 }\r
6771 \r
6772 #ifdef GOTHIC\r
6773 HWND gothicDialog = NULL;\r
6774 \r
6775 LRESULT CALLBACK\r
6776 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6777 {\r
6778   HANDLE hwndText;\r
6779   RECT rChild;\r
6780   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6781 \r
6782   switch (message) {\r
6783   case WM_INITDIALOG:\r
6784     GetWindowRect(hDlg, &rChild);\r
6785 \r
6786     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6787                                                              SWP_NOZORDER);\r
6788 \r
6789     /* \r
6790         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6791         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6792         and it doesn't work when you resize the dialog.\r
6793         For now, just give it a default position.\r
6794     */\r
6795     gothicDialog = hDlg;\r
6796     SetWindowText(hDlg, errorTitle);\r
6797     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6798     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6799     return FALSE;\r
6800 \r
6801   case WM_COMMAND:\r
6802     switch (LOWORD(wParam)) {\r
6803     case IDOK:\r
6804     case IDCANCEL:\r
6805       if (errorDialog == hDlg) errorDialog = NULL;\r
6806       DestroyWindow(hDlg);\r
6807       return TRUE;\r
6808 \r
6809     default:\r
6810       break;\r
6811     }\r
6812     break;\r
6813   }\r
6814   return FALSE;\r
6815 }\r
6816 \r
6817 VOID\r
6818 GothicPopUp(char *title, VariantClass variant)\r
6819 {\r
6820   FARPROC lpProc;\r
6821   static char *lastTitle;\r
6822 \r
6823   strncpy(errorTitle, title, sizeof(errorTitle));\r
6824   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6825 \r
6826   if(lastTitle != title && gothicDialog != NULL) {\r
6827     DestroyWindow(gothicDialog);\r
6828     gothicDialog = NULL;\r
6829   }\r
6830   if(variant != VariantNormal && gothicDialog == NULL) {\r
6831     title = lastTitle;\r
6832     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6833     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6834                  hwndMain, (DLGPROC)lpProc);\r
6835     FreeProcInstance(lpProc);\r
6836   }\r
6837 }\r
6838 #endif\r
6839 \r
6840 /*---------------------------------------------------------------------------*\\r
6841  *\r
6842  *  Ics Interaction console functions\r
6843  *\r
6844 \*---------------------------------------------------------------------------*/\r
6845 \r
6846 #define HISTORY_SIZE 64\r
6847 static char *history[HISTORY_SIZE];\r
6848 int histIn = 0, histP = 0;\r
6849 \r
6850 \r
6851 VOID\r
6852 SaveInHistory(char *cmd)\r
6853 {\r
6854   if (history[histIn] != NULL) {\r
6855     free(history[histIn]);\r
6856     history[histIn] = NULL;\r
6857   }\r
6858   if (*cmd == NULLCHAR) return;\r
6859   history[histIn] = StrSave(cmd);\r
6860   histIn = (histIn + 1) % HISTORY_SIZE;\r
6861   if (history[histIn] != NULL) {\r
6862     free(history[histIn]);\r
6863 \r
6864     history[histIn] = NULL;\r
6865   }\r
6866   histP = histIn;\r
6867 }\r
6868 \r
6869 char *\r
6870 PrevInHistory(char *cmd)\r
6871 {\r
6872   int newhp;\r
6873   if (histP == histIn) {\r
6874     if (history[histIn] != NULL) free(history[histIn]);\r
6875     history[histIn] = StrSave(cmd);\r
6876   }\r
6877   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6878   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6879   histP = newhp;\r
6880   return history[histP];\r
6881 }\r
6882 \r
6883 char *\r
6884 NextInHistory()\r
6885 {\r
6886   if (histP == histIn) return NULL;\r
6887   histP = (histP + 1) % HISTORY_SIZE;\r
6888   return history[histP];   \r
6889 }\r
6890 \r
6891 HMENU\r
6892 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6893 {\r
6894   HMENU hmenu, h;\r
6895   int i = 0;\r
6896   hmenu = LoadMenu(hInst, "TextMenu");\r
6897   h = GetSubMenu(hmenu, 0);\r
6898   while (e->item) {\r
6899     if (strcmp(e->item, "-") == 0) {\r
6900       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6901     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6902       int flags = MF_STRING, j = 0;\r
6903       if (e->item[0] == '|') {\r
6904         flags |= MF_MENUBARBREAK;\r
6905         j++;\r
6906       }\r
6907       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6908       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6909     }\r
6910     e++;\r
6911     i++;\r
6912   } \r
6913   return hmenu;\r
6914 }\r
6915 \r
6916 WNDPROC consoleTextWindowProc;\r
6917 \r
6918 void\r
6919 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6920 {\r
6921   char buf[MSG_SIZ], name[MSG_SIZ];\r
6922   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6923   CHARRANGE sel;\r
6924 \r
6925   if (!getname) {\r
6926     SetWindowText(hInput, command);\r
6927     if (immediate) {\r
6928       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6929     } else {\r
6930       sel.cpMin = 999999;\r
6931       sel.cpMax = 999999;\r
6932       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6933       SetFocus(hInput);\r
6934     }\r
6935     return;\r
6936   }    \r
6937   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6938   if (sel.cpMin == sel.cpMax) {\r
6939     /* Expand to surrounding word */\r
6940     TEXTRANGE tr;\r
6941     do {\r
6942       tr.chrg.cpMax = sel.cpMin;\r
6943       tr.chrg.cpMin = --sel.cpMin;\r
6944       if (sel.cpMin < 0) break;\r
6945       tr.lpstrText = name;\r
6946       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6947     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6948     sel.cpMin++;\r
6949 \r
6950     do {\r
6951       tr.chrg.cpMin = sel.cpMax;\r
6952       tr.chrg.cpMax = ++sel.cpMax;\r
6953       tr.lpstrText = name;\r
6954       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6955     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6956     sel.cpMax--;\r
6957 \r
6958     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6959       MessageBeep(MB_ICONEXCLAMATION);\r
6960       return;\r
6961     }\r
6962     tr.chrg = sel;\r
6963     tr.lpstrText = name;\r
6964     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6965   } else {\r
6966     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6967       MessageBeep(MB_ICONEXCLAMATION);\r
6968       return;\r
6969     }\r
6970     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6971   }\r
6972   if (immediate) {\r
6973     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6974     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6975     SetWindowText(hInput, buf);\r
6976     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6977   } else {\r
6978     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6979       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6980     SetWindowText(hInput, buf);\r
6981     sel.cpMin = 999999;\r
6982     sel.cpMax = 999999;\r
6983     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6984     SetFocus(hInput);\r
6985   }\r
6986 }\r
6987 \r
6988 LRESULT CALLBACK \r
6989 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6990 {\r
6991   HWND hInput;\r
6992   CHARRANGE sel;\r
6993 \r
6994   switch (message) {\r
6995   case WM_KEYDOWN:\r
6996     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6997     if(wParam=='R') return 0;\r
6998     switch (wParam) {\r
6999     case VK_PRIOR:\r
7000       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7001       return 0;\r
7002     case VK_NEXT:\r
7003       sel.cpMin = 999999;\r
7004       sel.cpMax = 999999;\r
7005       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7006       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7007       return 0;\r
7008     }\r
7009     break;\r
7010   case WM_CHAR:\r
7011    if(wParam != '\022') {\r
7012     if (wParam == '\t') {\r
7013       if (GetKeyState(VK_SHIFT) < 0) {\r
7014         /* shifted */\r
7015         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7016         if (buttonDesc[0].hwnd) {\r
7017           SetFocus(buttonDesc[0].hwnd);\r
7018         } else {\r
7019           SetFocus(hwndMain);\r
7020         }\r
7021       } else {\r
7022         /* unshifted */\r
7023         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7024       }\r
7025     } else {\r
7026       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7027       JAWS_DELETE( SetFocus(hInput); )\r
7028       SendMessage(hInput, message, wParam, lParam);\r
7029     }\r
7030     return 0;\r
7031    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7032    lParam = -1;\r
7033   case WM_RBUTTONDOWN:\r
7034     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7035       /* Move selection here if it was empty */\r
7036       POINT pt;\r
7037       pt.x = LOWORD(lParam);\r
7038       pt.y = HIWORD(lParam);\r
7039       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7040       if (sel.cpMin == sel.cpMax) {\r
7041         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7042         sel.cpMax = sel.cpMin;\r
7043         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7044       }\r
7045       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7046 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7047       POINT pt;\r
7048       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7049       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7050       if (sel.cpMin == sel.cpMax) {\r
7051         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7052         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7053       }\r
7054       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7055         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7056       }\r
7057       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7058       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7059       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7060       MenuPopup(hwnd, pt, hmenu, -1);\r
7061 }\r
7062     }\r
7063     return 0;\r
7064   case WM_RBUTTONUP:\r
7065     if (GetKeyState(VK_SHIFT) & ~1) {\r
7066       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7067         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7068     }\r
7069     return 0;\r
7070   case WM_PASTE:\r
7071     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7072     SetFocus(hInput);\r
7073     return SendMessage(hInput, message, wParam, lParam);\r
7074   case WM_MBUTTONDOWN:\r
7075     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7076   case WM_COMMAND:\r
7077     switch (LOWORD(wParam)) {\r
7078     case IDM_QuickPaste:\r
7079       {\r
7080         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7081         if (sel.cpMin == sel.cpMax) {\r
7082           MessageBeep(MB_ICONEXCLAMATION);\r
7083           return 0;\r
7084         }\r
7085         SendMessage(hwnd, WM_COPY, 0, 0);\r
7086         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7087         SendMessage(hInput, WM_PASTE, 0, 0);\r
7088         SetFocus(hInput);\r
7089         return 0;\r
7090       }\r
7091     case IDM_Cut:\r
7092       SendMessage(hwnd, WM_CUT, 0, 0);\r
7093       return 0;\r
7094     case IDM_Paste:\r
7095       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7096       return 0;\r
7097     case IDM_Copy:\r
7098       SendMessage(hwnd, WM_COPY, 0, 0);\r
7099       return 0;\r
7100     default:\r
7101       {\r
7102         int i = LOWORD(wParam) - IDM_CommandX;\r
7103         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7104             icsTextMenuEntry[i].command != NULL) {\r
7105           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7106                    icsTextMenuEntry[i].getname,\r
7107                    icsTextMenuEntry[i].immediate);\r
7108           return 0;\r
7109         }\r
7110       }\r
7111       break;\r
7112     }\r
7113     break;\r
7114   }\r
7115   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7116 }\r
7117 \r
7118 WNDPROC consoleInputWindowProc;\r
7119 \r
7120 LRESULT CALLBACK\r
7121 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7122 {\r
7123   char buf[MSG_SIZ];\r
7124   char *p;\r
7125   static BOOL sendNextChar = FALSE;\r
7126   static BOOL quoteNextChar = FALSE;\r
7127   InputSource *is = consoleInputSource;\r
7128   CHARFORMAT cf;\r
7129   CHARRANGE sel;\r
7130 \r
7131   switch (message) {\r
7132   case WM_CHAR:\r
7133     if (!appData.localLineEditing || sendNextChar) {\r
7134       is->buf[0] = (CHAR) wParam;\r
7135       is->count = 1;\r
7136       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7137       sendNextChar = FALSE;\r
7138       return 0;\r
7139     }\r
7140     if (quoteNextChar) {\r
7141       buf[0] = (char) wParam;\r
7142       buf[1] = NULLCHAR;\r
7143       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7144       quoteNextChar = FALSE;\r
7145       return 0;\r
7146     }\r
7147     switch (wParam) {\r
7148     case '\r':   /* Enter key */\r
7149       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7150       if (consoleEcho) SaveInHistory(is->buf);\r
7151       is->buf[is->count++] = '\n';\r
7152       is->buf[is->count] = NULLCHAR;\r
7153       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7154       if (consoleEcho) {\r
7155         ConsoleOutput(is->buf, is->count, TRUE);\r
7156       } else if (appData.localLineEditing) {\r
7157         ConsoleOutput("\n", 1, TRUE);\r
7158       }\r
7159       /* fall thru */\r
7160     case '\033': /* Escape key */\r
7161       SetWindowText(hwnd, "");\r
7162       cf.cbSize = sizeof(CHARFORMAT);\r
7163       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7164       if (consoleEcho) {\r
7165         cf.crTextColor = textAttribs[ColorNormal].color;\r
7166       } else {\r
7167         cf.crTextColor = COLOR_ECHOOFF;\r
7168       }\r
7169       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7170       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7171       return 0;\r
7172     case '\t':   /* Tab key */\r
7173       if (GetKeyState(VK_SHIFT) < 0) {\r
7174         /* shifted */\r
7175         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7176       } else {\r
7177         /* unshifted */\r
7178         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7179         if (buttonDesc[0].hwnd) {\r
7180           SetFocus(buttonDesc[0].hwnd);\r
7181         } else {\r
7182           SetFocus(hwndMain);\r
7183         }\r
7184       }\r
7185       return 0;\r
7186     case '\023': /* Ctrl+S */\r
7187       sendNextChar = TRUE;\r
7188       return 0;\r
7189     case '\021': /* Ctrl+Q */\r
7190       quoteNextChar = TRUE;\r
7191       return 0;\r
7192     JAWS_REPLAY\r
7193     default:\r
7194       break;\r
7195     }\r
7196     break;\r
7197   case WM_KEYDOWN:\r
7198     switch (wParam) {\r
7199     case VK_UP:\r
7200       GetWindowText(hwnd, buf, MSG_SIZ);\r
7201       p = PrevInHistory(buf);\r
7202       if (p != NULL) {\r
7203         SetWindowText(hwnd, p);\r
7204         sel.cpMin = 999999;\r
7205         sel.cpMax = 999999;\r
7206         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7207         return 0;\r
7208       }\r
7209       break;\r
7210     case VK_DOWN:\r
7211       p = NextInHistory();\r
7212       if (p != NULL) {\r
7213         SetWindowText(hwnd, p);\r
7214         sel.cpMin = 999999;\r
7215         sel.cpMax = 999999;\r
7216         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7217         return 0;\r
7218       }\r
7219       break;\r
7220     case VK_HOME:\r
7221     case VK_END:\r
7222       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7223       /* fall thru */\r
7224     case VK_PRIOR:\r
7225     case VK_NEXT:\r
7226       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7227       return 0;\r
7228     }\r
7229     break;\r
7230   case WM_MBUTTONDOWN:\r
7231     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7232       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7233     break;\r
7234   case WM_RBUTTONUP:\r
7235     if (GetKeyState(VK_SHIFT) & ~1) {\r
7236       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7237         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7238     } else {\r
7239       POINT pt;\r
7240       HMENU hmenu;\r
7241       hmenu = LoadMenu(hInst, "InputMenu");\r
7242       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7243       if (sel.cpMin == sel.cpMax) {\r
7244         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7245         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7246       }\r
7247       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7248         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7249       }\r
7250       pt.x = LOWORD(lParam);\r
7251       pt.y = HIWORD(lParam);\r
7252       MenuPopup(hwnd, pt, hmenu, -1);\r
7253     }\r
7254     return 0;\r
7255   case WM_COMMAND:\r
7256     switch (LOWORD(wParam)) { \r
7257     case IDM_Undo:\r
7258       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7259       return 0;\r
7260     case IDM_SelectAll:\r
7261       sel.cpMin = 0;\r
7262       sel.cpMax = -1; /*999999?*/\r
7263       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7264       return 0;\r
7265     case IDM_Cut:\r
7266       SendMessage(hwnd, WM_CUT, 0, 0);\r
7267       return 0;\r
7268     case IDM_Paste:\r
7269       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7270       return 0;\r
7271     case IDM_Copy:\r
7272       SendMessage(hwnd, WM_COPY, 0, 0);\r
7273       return 0;\r
7274     }\r
7275     break;\r
7276   }\r
7277   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7278 }\r
7279 \r
7280 #define CO_MAX  100000\r
7281 #define CO_TRIM   1000\r
7282 \r
7283 LRESULT CALLBACK\r
7284 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7285 {\r
7286   static SnapData sd;\r
7287   HWND hText, hInput;\r
7288   RECT rect;\r
7289   static int sizeX, sizeY;\r
7290   int newSizeX, newSizeY;\r
7291   MINMAXINFO *mmi;\r
7292   WORD wMask;\r
7293 \r
7294   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7295   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7296 \r
7297   switch (message) {\r
7298   case WM_NOTIFY:\r
7299     if (((NMHDR*)lParam)->code == EN_LINK)\r
7300     {\r
7301       ENLINK *pLink = (ENLINK*)lParam;\r
7302       if (pLink->msg == WM_LBUTTONUP)\r
7303       {\r
7304         TEXTRANGE tr;\r
7305 \r
7306         tr.chrg = pLink->chrg;\r
7307         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7308         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7309         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7310         free(tr.lpstrText);\r
7311       }\r
7312     }\r
7313     break;\r
7314   case WM_INITDIALOG: /* message: initialize dialog box */\r
7315     hwndConsole = hDlg;\r
7316     SetFocus(hInput);\r
7317     consoleTextWindowProc = (WNDPROC)\r
7318       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7319     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7320     consoleInputWindowProc = (WNDPROC)\r
7321       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7322     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7323     Colorize(ColorNormal, TRUE);\r
7324     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7325     ChangedConsoleFont();\r
7326     GetClientRect(hDlg, &rect);\r
7327     sizeX = rect.right;\r
7328     sizeY = rect.bottom;\r
7329     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7330         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7331       WINDOWPLACEMENT wp;\r
7332       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7333       wp.length = sizeof(WINDOWPLACEMENT);\r
7334       wp.flags = 0;\r
7335       wp.showCmd = SW_SHOW;\r
7336       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7337       wp.rcNormalPosition.left = wpConsole.x;\r
7338       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7339       wp.rcNormalPosition.top = wpConsole.y;\r
7340       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7341       SetWindowPlacement(hDlg, &wp);\r
7342     }\r
7343 \r
7344    // [HGM] Chessknight's change 2004-07-13\r
7345    else { /* Determine Defaults */\r
7346        WINDOWPLACEMENT wp;\r
7347        wpConsole.x = wpMain.width + 1;\r
7348        wpConsole.y = wpMain.y;\r
7349        wpConsole.width = screenWidth -  wpMain.width;\r
7350        wpConsole.height = wpMain.height;\r
7351        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7352        wp.length = sizeof(WINDOWPLACEMENT);\r
7353        wp.flags = 0;\r
7354        wp.showCmd = SW_SHOW;\r
7355        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7356        wp.rcNormalPosition.left = wpConsole.x;\r
7357        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7358        wp.rcNormalPosition.top = wpConsole.y;\r
7359        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7360        SetWindowPlacement(hDlg, &wp);\r
7361     }\r
7362 \r
7363    // Allow hText to highlight URLs and send notifications on them\r
7364    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7365    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7366    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7367    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7368 \r
7369     return FALSE;\r
7370 \r
7371   case WM_SETFOCUS:\r
7372     SetFocus(hInput);\r
7373     return 0;\r
7374 \r
7375   case WM_CLOSE:\r
7376     ExitEvent(0);\r
7377     /* not reached */\r
7378     break;\r
7379 \r
7380   case WM_SIZE:\r
7381     if (IsIconic(hDlg)) break;\r
7382     newSizeX = LOWORD(lParam);\r
7383     newSizeY = HIWORD(lParam);\r
7384     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7385       RECT rectText, rectInput;\r
7386       POINT pt;\r
7387       int newTextHeight, newTextWidth;\r
7388       GetWindowRect(hText, &rectText);\r
7389       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7390       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7391       if (newTextHeight < 0) {\r
7392         newSizeY += -newTextHeight;\r
7393         newTextHeight = 0;\r
7394       }\r
7395       SetWindowPos(hText, NULL, 0, 0,\r
7396         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7397       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7398       pt.x = rectInput.left;\r
7399       pt.y = rectInput.top + newSizeY - sizeY;\r
7400       ScreenToClient(hDlg, &pt);\r
7401       SetWindowPos(hInput, NULL, \r
7402         pt.x, pt.y, /* needs client coords */   \r
7403         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7404         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7405     }\r
7406     sizeX = newSizeX;\r
7407     sizeY = newSizeY;\r
7408     break;\r
7409 \r
7410   case WM_GETMINMAXINFO:\r
7411     /* Prevent resizing window too small */\r
7412     mmi = (MINMAXINFO *) lParam;\r
7413     mmi->ptMinTrackSize.x = 100;\r
7414     mmi->ptMinTrackSize.y = 100;\r
7415     break;\r
7416 \r
7417   /* [AS] Snapping */\r
7418   case WM_ENTERSIZEMOVE:\r
7419     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7420 \r
7421   case WM_SIZING:\r
7422     return OnSizing( &sd, hDlg, wParam, lParam );\r
7423 \r
7424   case WM_MOVING:\r
7425     return OnMoving( &sd, hDlg, wParam, lParam );\r
7426 \r
7427   case WM_EXITSIZEMOVE:\r
7428         UpdateICSWidth(hText);\r
7429     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7430   }\r
7431 \r
7432   return DefWindowProc(hDlg, message, wParam, lParam);\r
7433 }\r
7434 \r
7435 \r
7436 VOID\r
7437 ConsoleCreate()\r
7438 {\r
7439   HWND hCons;\r
7440   if (hwndConsole) return;\r
7441   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7442   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7443 }\r
7444 \r
7445 \r
7446 VOID\r
7447 ConsoleOutput(char* data, int length, int forceVisible)\r
7448 {\r
7449   HWND hText;\r
7450   int trim, exlen;\r
7451   char *p, *q;\r
7452   char buf[CO_MAX+1];\r
7453   POINT pEnd;\r
7454   RECT rect;\r
7455   static int delayLF = 0;\r
7456   CHARRANGE savesel, sel;\r
7457 \r
7458   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7459   p = data;\r
7460   q = buf;\r
7461   if (delayLF) {\r
7462     *q++ = '\r';\r
7463     *q++ = '\n';\r
7464     delayLF = 0;\r
7465   }\r
7466   while (length--) {\r
7467     if (*p == '\n') {\r
7468       if (*++p) {\r
7469         *q++ = '\r';\r
7470         *q++ = '\n';\r
7471       } else {\r
7472         delayLF = 1;\r
7473       }\r
7474     } else if (*p == '\007') {\r
7475        MyPlaySound(&sounds[(int)SoundBell]);\r
7476        p++;\r
7477     } else {\r
7478       *q++ = *p++;\r
7479     }\r
7480   }\r
7481   *q = NULLCHAR;\r
7482   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7483   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7484   /* Save current selection */\r
7485   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7486   exlen = GetWindowTextLength(hText);\r
7487   /* Find out whether current end of text is visible */\r
7488   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7489   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7490   /* Trim existing text if it's too long */\r
7491   if (exlen + (q - buf) > CO_MAX) {\r
7492     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7493     sel.cpMin = 0;\r
7494     sel.cpMax = trim;\r
7495     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7496     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7497     exlen -= trim;\r
7498     savesel.cpMin -= trim;\r
7499     savesel.cpMax -= trim;\r
7500     if (exlen < 0) exlen = 0;\r
7501     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7502     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7503   }\r
7504   /* Append the new text */\r
7505   sel.cpMin = exlen;\r
7506   sel.cpMax = exlen;\r
7507   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7508   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7509   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7510   if (forceVisible || exlen == 0 ||\r
7511       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7512        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7513     /* Scroll to make new end of text visible if old end of text\r
7514        was visible or new text is an echo of user typein */\r
7515     sel.cpMin = 9999999;\r
7516     sel.cpMax = 9999999;\r
7517     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7518     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7519     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7520     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7521   }\r
7522   if (savesel.cpMax == exlen || forceVisible) {\r
7523     /* Move insert point to new end of text if it was at the old\r
7524        end of text or if the new text is an echo of user typein */\r
7525     sel.cpMin = 9999999;\r
7526     sel.cpMax = 9999999;\r
7527     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7528   } else {\r
7529     /* Restore previous selection */\r
7530     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7531   }\r
7532   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7533 }\r
7534 \r
7535 /*---------*/\r
7536 \r
7537 \r
7538 void\r
7539 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7540 {\r
7541   char buf[100];\r
7542   char *str;\r
7543   COLORREF oldFg, oldBg;\r
7544   HFONT oldFont;\r
7545   RECT rect;\r
7546 \r
7547   if(copyNumber > 1)\r
7548     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7549 \r
7550   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7551   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7552   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7553 \r
7554   rect.left = x;\r
7555   rect.right = x + squareSize;\r
7556   rect.top  = y;\r
7557   rect.bottom = y + squareSize;\r
7558   str = buf;\r
7559 \r
7560   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7561                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7562              y, ETO_CLIPPED|ETO_OPAQUE,\r
7563              &rect, str, strlen(str), NULL);\r
7564 \r
7565   (void) SetTextColor(hdc, oldFg);\r
7566   (void) SetBkColor(hdc, oldBg);\r
7567   (void) SelectObject(hdc, oldFont);\r
7568 }\r
7569 \r
7570 void\r
7571 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7572               RECT *rect, char *color, char *flagFell)\r
7573 {\r
7574   char buf[100];\r
7575   char *str;\r
7576   COLORREF oldFg, oldBg;\r
7577   HFONT oldFont;\r
7578 \r
7579   if (twoBoards && partnerUp) return;\r
7580   if (appData.clockMode) {\r
7581     if (tinyLayout)\r
7582       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7583     else\r
7584       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7585     str = buf;\r
7586   } else {\r
7587     str = color;\r
7588   }\r
7589 \r
7590   if (highlight) {\r
7591     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7592     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7593   } else {\r
7594     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7595     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7596   }\r
7597   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7598 \r
7599   JAWS_SILENCE\r
7600 \r
7601   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7602              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7603              rect, str, strlen(str), NULL);\r
7604   if(logoHeight > 0 && appData.clockMode) {\r
7605       RECT r;\r
7606       str += strlen(color)+2;\r
7607       r.top = rect->top + logoHeight/2;\r
7608       r.left = rect->left;\r
7609       r.right = rect->right;\r
7610       r.bottom = rect->bottom;\r
7611       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7612                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7613                  &r, str, strlen(str), NULL);\r
7614   }\r
7615   (void) SetTextColor(hdc, oldFg);\r
7616   (void) SetBkColor(hdc, oldBg);\r
7617   (void) SelectObject(hdc, oldFont);\r
7618 }\r
7619 \r
7620 \r
7621 int\r
7622 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7623            OVERLAPPED *ovl)\r
7624 {\r
7625   int ok, err;\r
7626 \r
7627   /* [AS]  */\r
7628   if( count <= 0 ) {\r
7629     if (appData.debugMode) {\r
7630       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7631     }\r
7632 \r
7633     return ERROR_INVALID_USER_BUFFER;\r
7634   }\r
7635 \r
7636   ResetEvent(ovl->hEvent);\r
7637   ovl->Offset = ovl->OffsetHigh = 0;\r
7638   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7639   if (ok) {\r
7640     err = NO_ERROR;\r
7641   } else {\r
7642     err = GetLastError();\r
7643     if (err == ERROR_IO_PENDING) {\r
7644       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7645       if (ok)\r
7646         err = NO_ERROR;\r
7647       else\r
7648         err = GetLastError();\r
7649     }\r
7650   }\r
7651   return err;\r
7652 }\r
7653 \r
7654 int\r
7655 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7656             OVERLAPPED *ovl)\r
7657 {\r
7658   int ok, err;\r
7659 \r
7660   ResetEvent(ovl->hEvent);\r
7661   ovl->Offset = ovl->OffsetHigh = 0;\r
7662   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7663   if (ok) {\r
7664     err = NO_ERROR;\r
7665   } else {\r
7666     err = GetLastError();\r
7667     if (err == ERROR_IO_PENDING) {\r
7668       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7669       if (ok)\r
7670         err = NO_ERROR;\r
7671       else\r
7672         err = GetLastError();\r
7673     }\r
7674   }\r
7675   return err;\r
7676 }\r
7677 \r
7678 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7679 void CheckForInputBufferFull( InputSource * is )\r
7680 {\r
7681     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7682         /* Look for end of line */\r
7683         char * p = is->buf;\r
7684         \r
7685         while( p < is->next && *p != '\n' ) {\r
7686             p++;\r
7687         }\r
7688 \r
7689         if( p >= is->next ) {\r
7690             if (appData.debugMode) {\r
7691                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7692             }\r
7693 \r
7694             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7695             is->count = (DWORD) -1;\r
7696             is->next = is->buf;\r
7697         }\r
7698     }\r
7699 }\r
7700 \r
7701 DWORD\r
7702 InputThread(LPVOID arg)\r
7703 {\r
7704   InputSource *is;\r
7705   OVERLAPPED ovl;\r
7706 \r
7707   is = (InputSource *) arg;\r
7708   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7709   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7710   while (is->hThread != NULL) {\r
7711     is->error = DoReadFile(is->hFile, is->next,\r
7712                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7713                            &is->count, &ovl);\r
7714     if (is->error == NO_ERROR) {\r
7715       is->next += is->count;\r
7716     } else {\r
7717       if (is->error == ERROR_BROKEN_PIPE) {\r
7718         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7719         is->count = 0;\r
7720       } else {\r
7721         is->count = (DWORD) -1;\r
7722         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7723         break; \r
7724       }\r
7725     }\r
7726 \r
7727     CheckForInputBufferFull( is );\r
7728 \r
7729     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7730 \r
7731     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7732 \r
7733     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7734   }\r
7735 \r
7736   CloseHandle(ovl.hEvent);\r
7737   CloseHandle(is->hFile);\r
7738 \r
7739   if (appData.debugMode) {\r
7740     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7741   }\r
7742 \r
7743   return 0;\r
7744 }\r
7745 \r
7746 \r
7747 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7748 DWORD\r
7749 NonOvlInputThread(LPVOID arg)\r
7750 {\r
7751   InputSource *is;\r
7752   char *p, *q;\r
7753   int i;\r
7754   char prev;\r
7755 \r
7756   is = (InputSource *) arg;\r
7757   while (is->hThread != NULL) {\r
7758     is->error = ReadFile(is->hFile, is->next,\r
7759                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7760                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7761     if (is->error == NO_ERROR) {\r
7762       /* Change CRLF to LF */\r
7763       if (is->next > is->buf) {\r
7764         p = is->next - 1;\r
7765         i = is->count + 1;\r
7766       } else {\r
7767         p = is->next;\r
7768         i = is->count;\r
7769       }\r
7770       q = p;\r
7771       prev = NULLCHAR;\r
7772       while (i > 0) {\r
7773         if (prev == '\r' && *p == '\n') {\r
7774           *(q-1) = '\n';\r
7775           is->count--;\r
7776         } else { \r
7777           *q++ = *p;\r
7778         }\r
7779         prev = *p++;\r
7780         i--;\r
7781       }\r
7782       *q = NULLCHAR;\r
7783       is->next = q;\r
7784     } else {\r
7785       if (is->error == ERROR_BROKEN_PIPE) {\r
7786         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7787         is->count = 0; \r
7788       } else {\r
7789         is->count = (DWORD) -1;\r
7790       }\r
7791     }\r
7792 \r
7793     CheckForInputBufferFull( is );\r
7794 \r
7795     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7796 \r
7797     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7798 \r
7799     if (is->count < 0) break;  /* Quit on error */\r
7800   }\r
7801   CloseHandle(is->hFile);\r
7802   return 0;\r
7803 }\r
7804 \r
7805 DWORD\r
7806 SocketInputThread(LPVOID arg)\r
7807 {\r
7808   InputSource *is;\r
7809 \r
7810   is = (InputSource *) arg;\r
7811   while (is->hThread != NULL) {\r
7812     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7813     if ((int)is->count == SOCKET_ERROR) {\r
7814       is->count = (DWORD) -1;\r
7815       is->error = WSAGetLastError();\r
7816     } else {\r
7817       is->error = NO_ERROR;\r
7818       is->next += is->count;\r
7819       if (is->count == 0 && is->second == is) {\r
7820         /* End of file on stderr; quit with no message */\r
7821         break;\r
7822       }\r
7823     }\r
7824     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7825 \r
7826     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7827 \r
7828     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7829   }\r
7830   return 0;\r
7831 }\r
7832 \r
7833 VOID\r
7834 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7835 {\r
7836   InputSource *is;\r
7837 \r
7838   is = (InputSource *) lParam;\r
7839   if (is->lineByLine) {\r
7840     /* Feed in lines one by one */\r
7841     char *p = is->buf;\r
7842     char *q = p;\r
7843     while (q < is->next) {\r
7844       if (*q++ == '\n') {\r
7845         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7846         p = q;\r
7847       }\r
7848     }\r
7849     \r
7850     /* Move any partial line to the start of the buffer */\r
7851     q = is->buf;\r
7852     while (p < is->next) {\r
7853       *q++ = *p++;\r
7854     }\r
7855     is->next = q;\r
7856 \r
7857     if (is->error != NO_ERROR || is->count == 0) {\r
7858       /* Notify backend of the error.  Note: If there was a partial\r
7859          line at the end, it is not flushed through. */\r
7860       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7861     }\r
7862   } else {\r
7863     /* Feed in the whole chunk of input at once */\r
7864     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7865     is->next = is->buf;\r
7866   }\r
7867 }\r
7868 \r
7869 /*---------------------------------------------------------------------------*\\r
7870  *\r
7871  *  Menu enables. Used when setting various modes.\r
7872  *\r
7873 \*---------------------------------------------------------------------------*/\r
7874 \r
7875 typedef struct {\r
7876   int item;\r
7877   int flags;\r
7878 } Enables;\r
7879 \r
7880 VOID\r
7881 GreyRevert(Boolean grey)\r
7882 { // [HGM] vari: for retracting variations in local mode\r
7883   HMENU hmenu = GetMenu(hwndMain);\r
7884   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7885   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7886 }\r
7887 \r
7888 VOID\r
7889 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7890 {\r
7891   while (enab->item > 0) {\r
7892     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7893     enab++;\r
7894   }\r
7895 }\r
7896 \r
7897 Enables gnuEnables[] = {\r
7898   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7899   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7900   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7901   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7902   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7903   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7904   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7905   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7906   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7907   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7908   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7909   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7910   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7911 \r
7912   // Needed to switch from ncp to GNU mode on Engine Load\r
7913   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7914   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7915   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7916   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7917   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7918   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7919   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7920   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7921   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7922   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7923   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7924   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7925   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7926   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7927   { -1, -1 }\r
7928 };\r
7929 \r
7930 Enables icsEnables[] = {\r
7931   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7932   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7933   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7934   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7935   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7936   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7937   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7938   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7939   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7940   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7941   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7942   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7943   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7944   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
7945   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
7946   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7947   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7948   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7949   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7950   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7951   { -1, -1 }\r
7952 };\r
7953 \r
7954 #if ZIPPY\r
7955 Enables zippyEnables[] = {\r
7956   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7957   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7958   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7959   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7960   { -1, -1 }\r
7961 };\r
7962 #endif\r
7963 \r
7964 Enables ncpEnables[] = {\r
7965   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7966   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7967   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7968   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7969   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7970   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7971   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7972   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7973   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7974   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7975   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7976   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7977   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7978   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7979   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7980   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7981   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7982   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7983   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7984   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7985   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7986   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7987   { -1, -1 }\r
7988 };\r
7989 \r
7990 Enables trainingOnEnables[] = {\r
7991   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7992   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7993   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7994   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7995   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7996   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7997   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7998   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7999   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8000   { -1, -1 }\r
8001 };\r
8002 \r
8003 Enables trainingOffEnables[] = {\r
8004   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8005   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8006   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8007   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8008   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8009   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8010   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8011   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8012   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8013   { -1, -1 }\r
8014 };\r
8015 \r
8016 /* These modify either ncpEnables or gnuEnables */\r
8017 Enables cmailEnables[] = {\r
8018   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8019   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8020   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8021   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8022   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8023   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8024   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8025   { -1, -1 }\r
8026 };\r
8027 \r
8028 Enables machineThinkingEnables[] = {\r
8029   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8030   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8031   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8032   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8033   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8034   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8035   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8036   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8037   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8038   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8039   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8040   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8041   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8042 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8043   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8044   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8045   { -1, -1 }\r
8046 };\r
8047 \r
8048 Enables userThinkingEnables[] = {\r
8049   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8050   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8051   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8052   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8053   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8054   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8055   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8056   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8057   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8058   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8059   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8060   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8061   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8062 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8063   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8064   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8065   { -1, -1 }\r
8066 };\r
8067 \r
8068 /*---------------------------------------------------------------------------*\\r
8069  *\r
8070  *  Front-end interface functions exported by XBoard.\r
8071  *  Functions appear in same order as prototypes in frontend.h.\r
8072  * \r
8073 \*---------------------------------------------------------------------------*/\r
8074 VOID\r
8075 CheckMark(UINT item, int state)\r
8076 {\r
8077     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8078 }\r
8079 \r
8080 VOID\r
8081 ModeHighlight()\r
8082 {\r
8083   static UINT prevChecked = 0;\r
8084   static int prevPausing = 0;\r
8085   UINT nowChecked;\r
8086 \r
8087   if (pausing != prevPausing) {\r
8088     prevPausing = pausing;\r
8089     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8090                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8091     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8092   }\r
8093 \r
8094   switch (gameMode) {\r
8095   case BeginningOfGame:\r
8096     if (appData.icsActive)\r
8097       nowChecked = IDM_IcsClient;\r
8098     else if (appData.noChessProgram)\r
8099       nowChecked = IDM_EditGame;\r
8100     else\r
8101       nowChecked = IDM_MachineBlack;\r
8102     break;\r
8103   case MachinePlaysBlack:\r
8104     nowChecked = IDM_MachineBlack;\r
8105     break;\r
8106   case MachinePlaysWhite:\r
8107     nowChecked = IDM_MachineWhite;\r
8108     break;\r
8109   case TwoMachinesPlay:\r
8110     nowChecked = IDM_TwoMachines;\r
8111     break;\r
8112   case AnalyzeMode:\r
8113     nowChecked = IDM_AnalysisMode;\r
8114     break;\r
8115   case AnalyzeFile:\r
8116     nowChecked = IDM_AnalyzeFile;\r
8117     break;\r
8118   case EditGame:\r
8119     nowChecked = IDM_EditGame;\r
8120     break;\r
8121   case PlayFromGameFile:\r
8122     nowChecked = IDM_LoadGame;\r
8123     break;\r
8124   case EditPosition:\r
8125     nowChecked = IDM_EditPosition;\r
8126     break;\r
8127   case Training:\r
8128     nowChecked = IDM_Training;\r
8129     break;\r
8130   case IcsPlayingWhite:\r
8131   case IcsPlayingBlack:\r
8132   case IcsObserving:\r
8133   case IcsIdle:\r
8134     nowChecked = IDM_IcsClient;\r
8135     break;\r
8136   default:\r
8137   case EndOfGame:\r
8138     nowChecked = 0;\r
8139     break;\r
8140   }\r
8141   CheckMark(prevChecked, MF_UNCHECKED);\r
8142   CheckMark(nowChecked, MF_CHECKED);\r
8143   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8144 \r
8145   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8146     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8147                           MF_BYCOMMAND|MF_ENABLED);\r
8148   } else {\r
8149     (void) EnableMenuItem(GetMenu(hwndMain), \r
8150                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8151   }\r
8152 \r
8153   prevChecked = nowChecked;\r
8154 \r
8155   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8156   if (appData.icsActive) {\r
8157        if (appData.icsEngineAnalyze) {\r
8158                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8159        } else {\r
8160                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8161        }\r
8162   }\r
8163   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8164 }\r
8165 \r
8166 VOID\r
8167 SetICSMode()\r
8168 {\r
8169   HMENU hmenu = GetMenu(hwndMain);\r
8170   SetMenuEnables(hmenu, icsEnables);\r
8171   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8172     MF_BYCOMMAND|MF_ENABLED);\r
8173 #if ZIPPY\r
8174   if (appData.zippyPlay) {\r
8175     SetMenuEnables(hmenu, zippyEnables);\r
8176     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8177          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8178           MF_BYCOMMAND|MF_ENABLED);\r
8179   }\r
8180 #endif\r
8181 }\r
8182 \r
8183 VOID\r
8184 SetGNUMode()\r
8185 {\r
8186   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8187 }\r
8188 \r
8189 VOID\r
8190 SetNCPMode()\r
8191 {\r
8192   HMENU hmenu = GetMenu(hwndMain);\r
8193   SetMenuEnables(hmenu, ncpEnables);\r
8194     DrawMenuBar(hwndMain);\r
8195 }\r
8196 \r
8197 VOID\r
8198 SetCmailMode()\r
8199 {\r
8200   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8201 }\r
8202 \r
8203 VOID \r
8204 SetTrainingModeOn()\r
8205 {\r
8206   int i;\r
8207   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8208   for (i = 0; i < N_BUTTONS; i++) {\r
8209     if (buttonDesc[i].hwnd != NULL)\r
8210       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8211   }\r
8212   CommentPopDown();\r
8213 }\r
8214 \r
8215 VOID SetTrainingModeOff()\r
8216 {\r
8217   int i;\r
8218   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8219   for (i = 0; i < N_BUTTONS; i++) {\r
8220     if (buttonDesc[i].hwnd != NULL)\r
8221       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8222   }\r
8223 }\r
8224 \r
8225 \r
8226 VOID\r
8227 SetUserThinkingEnables()\r
8228 {\r
8229   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8230 }\r
8231 \r
8232 VOID\r
8233 SetMachineThinkingEnables()\r
8234 {\r
8235   HMENU hMenu = GetMenu(hwndMain);\r
8236   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8237 \r
8238   SetMenuEnables(hMenu, machineThinkingEnables);\r
8239 \r
8240   if (gameMode == MachinePlaysBlack) {\r
8241     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8242   } else if (gameMode == MachinePlaysWhite) {\r
8243     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8244   } else if (gameMode == TwoMachinesPlay) {\r
8245     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8246   }\r
8247 }\r
8248 \r
8249 \r
8250 VOID\r
8251 DisplayTitle(char *str)\r
8252 {\r
8253   char title[MSG_SIZ], *host;\r
8254   if (str[0] != NULLCHAR) {\r
8255     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8256   } else if (appData.icsActive) {\r
8257     if (appData.icsCommPort[0] != NULLCHAR)\r
8258       host = "ICS";\r
8259     else \r
8260       host = appData.icsHost;\r
8261       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8262   } else if (appData.noChessProgram) {\r
8263     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8264   } else {\r
8265     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8266     strcat(title, ": ");\r
8267     strcat(title, first.tidy);\r
8268   }\r
8269   SetWindowText(hwndMain, title);\r
8270 }\r
8271 \r
8272 \r
8273 VOID\r
8274 DisplayMessage(char *str1, char *str2)\r
8275 {\r
8276   HDC hdc;\r
8277   HFONT oldFont;\r
8278   int remain = MESSAGE_TEXT_MAX - 1;\r
8279   int len;\r
8280 \r
8281   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8282   messageText[0] = NULLCHAR;\r
8283   if (*str1) {\r
8284     len = strlen(str1);\r
8285     if (len > remain) len = remain;\r
8286     strncpy(messageText, str1, len);\r
8287     messageText[len] = NULLCHAR;\r
8288     remain -= len;\r
8289   }\r
8290   if (*str2 && remain >= 2) {\r
8291     if (*str1) {\r
8292       strcat(messageText, "  ");\r
8293       remain -= 2;\r
8294     }\r
8295     len = strlen(str2);\r
8296     if (len > remain) len = remain;\r
8297     strncat(messageText, str2, len);\r
8298   }\r
8299   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8300   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8301 \r
8302   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8303 \r
8304   SAYMACHINEMOVE();\r
8305 \r
8306   hdc = GetDC(hwndMain);\r
8307   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8308   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8309              &messageRect, messageText, strlen(messageText), NULL);\r
8310   (void) SelectObject(hdc, oldFont);\r
8311   (void) ReleaseDC(hwndMain, hdc);\r
8312 }\r
8313 \r
8314 VOID\r
8315 DisplayError(char *str, int error)\r
8316 {\r
8317   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8318   int len;\r
8319 \r
8320   if (error == 0) {\r
8321     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8322   } else {\r
8323     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8324                         NULL, error, LANG_NEUTRAL,\r
8325                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8326     if (len > 0) {\r
8327       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8328     } else {\r
8329       ErrorMap *em = errmap;\r
8330       while (em->err != 0 && em->err != error) em++;\r
8331       if (em->err != 0) {\r
8332         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8333       } else {\r
8334         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8335       }\r
8336     }\r
8337   }\r
8338   \r
8339   ErrorPopUp(_("Error"), buf);\r
8340 }\r
8341 \r
8342 \r
8343 VOID\r
8344 DisplayMoveError(char *str)\r
8345 {\r
8346   fromX = fromY = -1;\r
8347   ClearHighlights();\r
8348   DrawPosition(FALSE, NULL);\r
8349   if (appData.popupMoveErrors) {\r
8350     ErrorPopUp(_("Error"), str);\r
8351   } else {\r
8352     DisplayMessage(str, "");\r
8353     moveErrorMessageUp = TRUE;\r
8354   }\r
8355 }\r
8356 \r
8357 VOID\r
8358 DisplayFatalError(char *str, int error, int exitStatus)\r
8359 {\r
8360   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8361   int len;\r
8362   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8363 \r
8364   if (error != 0) {\r
8365     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8366                         NULL, error, LANG_NEUTRAL,\r
8367                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8368     if (len > 0) {\r
8369       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8370     } else {\r
8371       ErrorMap *em = errmap;\r
8372       while (em->err != 0 && em->err != error) em++;\r
8373       if (em->err != 0) {\r
8374         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8375       } else {\r
8376         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8377       }\r
8378     }\r
8379     str = buf;\r
8380   }\r
8381   if (appData.debugMode) {\r
8382     fprintf(debugFP, "%s: %s\n", label, str);\r
8383   }\r
8384   if (appData.popupExitMessage) {\r
8385     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8386                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8387   }\r
8388   ExitEvent(exitStatus);\r
8389 }\r
8390 \r
8391 \r
8392 VOID\r
8393 DisplayInformation(char *str)\r
8394 {\r
8395   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8396 }\r
8397 \r
8398 \r
8399 VOID\r
8400 DisplayNote(char *str)\r
8401 {\r
8402   ErrorPopUp(_("Note"), str);\r
8403 }\r
8404 \r
8405 \r
8406 typedef struct {\r
8407   char *title, *question, *replyPrefix;\r
8408   ProcRef pr;\r
8409 } QuestionParams;\r
8410 \r
8411 LRESULT CALLBACK\r
8412 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8413 {\r
8414   static QuestionParams *qp;\r
8415   char reply[MSG_SIZ];\r
8416   int len, err;\r
8417 \r
8418   switch (message) {\r
8419   case WM_INITDIALOG:\r
8420     qp = (QuestionParams *) lParam;\r
8421     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8422     Translate(hDlg, DLG_Question);\r
8423     SetWindowText(hDlg, qp->title);\r
8424     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8425     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8426     return FALSE;\r
8427 \r
8428   case WM_COMMAND:\r
8429     switch (LOWORD(wParam)) {\r
8430     case IDOK:\r
8431       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8432       if (*reply) strcat(reply, " ");\r
8433       len = strlen(reply);\r
8434       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8435       strcat(reply, "\n");\r
8436       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8437       EndDialog(hDlg, TRUE);\r
8438       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8439       return TRUE;\r
8440     case IDCANCEL:\r
8441       EndDialog(hDlg, FALSE);\r
8442       return TRUE;\r
8443     default:\r
8444       break;\r
8445     }\r
8446     break;\r
8447   }\r
8448   return FALSE;\r
8449 }\r
8450 \r
8451 VOID\r
8452 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8453 {\r
8454     QuestionParams qp;\r
8455     FARPROC lpProc;\r
8456     \r
8457     qp.title = title;\r
8458     qp.question = question;\r
8459     qp.replyPrefix = replyPrefix;\r
8460     qp.pr = pr;\r
8461     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8462     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8463       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8464     FreeProcInstance(lpProc);\r
8465 }\r
8466 \r
8467 /* [AS] Pick FRC position */\r
8468 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8469 {\r
8470     static int * lpIndexFRC;\r
8471     BOOL index_is_ok;\r
8472     char buf[16];\r
8473 \r
8474     switch( message )\r
8475     {\r
8476     case WM_INITDIALOG:\r
8477         lpIndexFRC = (int *) lParam;\r
8478 \r
8479         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8480         Translate(hDlg, DLG_NewGameFRC);\r
8481 \r
8482         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8483         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8484         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8485         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8486 \r
8487         break;\r
8488 \r
8489     case WM_COMMAND:\r
8490         switch( LOWORD(wParam) ) {\r
8491         case IDOK:\r
8492             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8493             EndDialog( hDlg, 0 );\r
8494             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8495             return TRUE;\r
8496         case IDCANCEL:\r
8497             EndDialog( hDlg, 1 );   \r
8498             return TRUE;\r
8499         case IDC_NFG_Edit:\r
8500             if( HIWORD(wParam) == EN_CHANGE ) {\r
8501                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8502 \r
8503                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8504             }\r
8505             return TRUE;\r
8506         case IDC_NFG_Random:\r
8507           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8508             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8509             return TRUE;\r
8510         }\r
8511 \r
8512         break;\r
8513     }\r
8514 \r
8515     return FALSE;\r
8516 }\r
8517 \r
8518 int NewGameFRC()\r
8519 {\r
8520     int result;\r
8521     int index = appData.defaultFrcPosition;\r
8522     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8523 \r
8524     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8525 \r
8526     if( result == 0 ) {\r
8527         appData.defaultFrcPosition = index;\r
8528     }\r
8529 \r
8530     return result;\r
8531 }\r
8532 \r
8533 /* [AS] Game list options. Refactored by HGM */\r
8534 \r
8535 HWND gameListOptionsDialog;\r
8536 \r
8537 // low-level front-end: clear text edit / list widget\r
8538 void\r
8539 \r
8540 GLT_ClearList()\r
8541 {\r
8542     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8543 }\r
8544 \r
8545 // low-level front-end: clear text edit / list widget\r
8546 void\r
8547 GLT_DeSelectList()\r
8548 {\r
8549     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8550 }\r
8551 \r
8552 // low-level front-end: append line to text edit / list widget\r
8553 void\r
8554 GLT_AddToList( char *name )\r
8555 {\r
8556     if( name != 0 ) {\r
8557             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8558     }\r
8559 }\r
8560 \r
8561 // low-level front-end: get line from text edit / list widget\r
8562 Boolean\r
8563 GLT_GetFromList( int index, char *name )\r
8564 {\r
8565     if( name != 0 ) {\r
8566             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8567                 return TRUE;\r
8568     }\r
8569     return FALSE;\r
8570 }\r
8571 \r
8572 void GLT_MoveSelection( HWND hDlg, int delta )\r
8573 {\r
8574     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8575     int idx2 = idx1 + delta;\r
8576     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8577 \r
8578     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8579         char buf[128];\r
8580 \r
8581         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8582         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8583         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8584         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8585     }\r
8586 }\r
8587 \r
8588 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8589 {\r
8590     switch( message )\r
8591     {\r
8592     case WM_INITDIALOG:\r
8593         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8594         \r
8595         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8596         Translate(hDlg, DLG_GameListOptions);\r
8597 \r
8598         /* Initialize list */\r
8599         GLT_TagsToList( lpUserGLT );\r
8600 \r
8601         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8602 \r
8603         break;\r
8604 \r
8605     case WM_COMMAND:\r
8606         switch( LOWORD(wParam) ) {\r
8607         case IDOK:\r
8608             GLT_ParseList();\r
8609             EndDialog( hDlg, 0 );\r
8610             return TRUE;\r
8611         case IDCANCEL:\r
8612             EndDialog( hDlg, 1 );\r
8613             return TRUE;\r
8614 \r
8615         case IDC_GLT_Default:\r
8616             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8617             return TRUE;\r
8618 \r
8619         case IDC_GLT_Restore:\r
8620             GLT_TagsToList( appData.gameListTags );\r
8621             return TRUE;\r
8622 \r
8623         case IDC_GLT_Up:\r
8624             GLT_MoveSelection( hDlg, -1 );\r
8625             return TRUE;\r
8626 \r
8627         case IDC_GLT_Down:\r
8628             GLT_MoveSelection( hDlg, +1 );\r
8629             return TRUE;\r
8630         }\r
8631 \r
8632         break;\r
8633     }\r
8634 \r
8635     return FALSE;\r
8636 }\r
8637 \r
8638 int GameListOptions()\r
8639 {\r
8640     int result;\r
8641     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8642 \r
8643       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8644 \r
8645     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8646 \r
8647     if( result == 0 ) {\r
8648         /* [AS] Memory leak here! */\r
8649         appData.gameListTags = strdup( lpUserGLT ); \r
8650     }\r
8651 \r
8652     return result;\r
8653 }\r
8654 \r
8655 VOID\r
8656 DisplayIcsInteractionTitle(char *str)\r
8657 {\r
8658   char consoleTitle[MSG_SIZ];\r
8659 \r
8660     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8661     SetWindowText(hwndConsole, consoleTitle);\r
8662 \r
8663     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8664       char buf[MSG_SIZ], *p = buf, *q;\r
8665         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8666       do {\r
8667         q = strchr(p, ';');\r
8668         if(q) *q++ = 0;\r
8669         if(*p) ChatPopUp(p);\r
8670       } while(p=q);\r
8671     }\r
8672 \r
8673     SetActiveWindow(hwndMain);\r
8674 }\r
8675 \r
8676 void\r
8677 DrawPosition(int fullRedraw, Board board)\r
8678 {\r
8679   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8680 }\r
8681 \r
8682 void NotifyFrontendLogin()\r
8683 {\r
8684         if (hwndConsole)\r
8685                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8686 }\r
8687 \r
8688 VOID\r
8689 ResetFrontEnd()\r
8690 {\r
8691   fromX = fromY = -1;\r
8692   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8693     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8694     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8695     dragInfo.lastpos = dragInfo.pos;\r
8696     dragInfo.start.x = dragInfo.start.y = -1;\r
8697     dragInfo.from = dragInfo.start;\r
8698     ReleaseCapture();\r
8699     DrawPosition(TRUE, NULL);\r
8700   }\r
8701   TagsPopDown();\r
8702 }\r
8703 \r
8704 \r
8705 VOID\r
8706 CommentPopUp(char *title, char *str)\r
8707 {\r
8708   HWND hwnd = GetActiveWindow();\r
8709   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8710   SAY(str);\r
8711   SetActiveWindow(hwnd);\r
8712 }\r
8713 \r
8714 VOID\r
8715 CommentPopDown(void)\r
8716 {\r
8717   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8718   if (commentDialog) {\r
8719     ShowWindow(commentDialog, SW_HIDE);\r
8720   }\r
8721   commentUp = FALSE;\r
8722 }\r
8723 \r
8724 VOID\r
8725 EditCommentPopUp(int index, char *title, char *str)\r
8726 {\r
8727   EitherCommentPopUp(index, title, str, TRUE);\r
8728 }\r
8729 \r
8730 \r
8731 int\r
8732 Roar()\r
8733 {\r
8734   MyPlaySound(&sounds[(int)SoundRoar]);\r
8735   return 1;\r
8736 }\r
8737 \r
8738 VOID\r
8739 RingBell()\r
8740 {\r
8741   MyPlaySound(&sounds[(int)SoundMove]);\r
8742 }\r
8743 \r
8744 VOID PlayIcsWinSound()\r
8745 {\r
8746   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8747 }\r
8748 \r
8749 VOID PlayIcsLossSound()\r
8750 {\r
8751   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8752 }\r
8753 \r
8754 VOID PlayIcsDrawSound()\r
8755 {\r
8756   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8757 }\r
8758 \r
8759 VOID PlayIcsUnfinishedSound()\r
8760 {\r
8761   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8762 }\r
8763 \r
8764 VOID\r
8765 PlayAlarmSound()\r
8766 {\r
8767   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8768 }\r
8769 \r
8770 VOID\r
8771 PlayTellSound()\r
8772 {\r
8773   MyPlaySound(&textAttribs[ColorTell].sound);\r
8774 }\r
8775 \r
8776 \r
8777 VOID\r
8778 EchoOn()\r
8779 {\r
8780   HWND hInput;\r
8781   consoleEcho = TRUE;\r
8782   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8783   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8784   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8785 }\r
8786 \r
8787 \r
8788 VOID\r
8789 EchoOff()\r
8790 {\r
8791   CHARFORMAT cf;\r
8792   HWND hInput;\r
8793   consoleEcho = FALSE;\r
8794   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8795   /* This works OK: set text and background both to the same color */\r
8796   cf = consoleCF;\r
8797   cf.crTextColor = COLOR_ECHOOFF;\r
8798   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8799   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8800 }\r
8801 \r
8802 /* No Raw()...? */\r
8803 \r
8804 void Colorize(ColorClass cc, int continuation)\r
8805 {\r
8806   currentColorClass = cc;\r
8807   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8808   consoleCF.crTextColor = textAttribs[cc].color;\r
8809   consoleCF.dwEffects = textAttribs[cc].effects;\r
8810   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8811 }\r
8812 \r
8813 char *\r
8814 UserName()\r
8815 {\r
8816   static char buf[MSG_SIZ];\r
8817   DWORD bufsiz = MSG_SIZ;\r
8818 \r
8819   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8820         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8821   }\r
8822   if (!GetUserName(buf, &bufsiz)) {\r
8823     /*DisplayError("Error getting user name", GetLastError());*/\r
8824     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8825   }\r
8826   return buf;\r
8827 }\r
8828 \r
8829 char *\r
8830 HostName()\r
8831 {\r
8832   static char buf[MSG_SIZ];\r
8833   DWORD bufsiz = MSG_SIZ;\r
8834 \r
8835   if (!GetComputerName(buf, &bufsiz)) {\r
8836     /*DisplayError("Error getting host name", GetLastError());*/\r
8837     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8838   }\r
8839   return buf;\r
8840 }\r
8841 \r
8842 \r
8843 int\r
8844 ClockTimerRunning()\r
8845 {\r
8846   return clockTimerEvent != 0;\r
8847 }\r
8848 \r
8849 int\r
8850 StopClockTimer()\r
8851 {\r
8852   if (clockTimerEvent == 0) return FALSE;\r
8853   KillTimer(hwndMain, clockTimerEvent);\r
8854   clockTimerEvent = 0;\r
8855   return TRUE;\r
8856 }\r
8857 \r
8858 void\r
8859 StartClockTimer(long millisec)\r
8860 {\r
8861   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8862                              (UINT) millisec, NULL);\r
8863 }\r
8864 \r
8865 void\r
8866 DisplayWhiteClock(long timeRemaining, int highlight)\r
8867 {\r
8868   HDC hdc;\r
8869   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8870 \r
8871   if(appData.noGUI) return;\r
8872   hdc = GetDC(hwndMain);\r
8873   if (!IsIconic(hwndMain)) {\r
8874     DisplayAClock(hdc, timeRemaining, highlight, \r
8875                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8876   }\r
8877   if (highlight && iconCurrent == iconBlack) {\r
8878     iconCurrent = iconWhite;\r
8879     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8880     if (IsIconic(hwndMain)) {\r
8881       DrawIcon(hdc, 2, 2, iconCurrent);\r
8882     }\r
8883   }\r
8884   (void) ReleaseDC(hwndMain, hdc);\r
8885   if (hwndConsole)\r
8886     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8887 }\r
8888 \r
8889 void\r
8890 DisplayBlackClock(long timeRemaining, int highlight)\r
8891 {\r
8892   HDC hdc;\r
8893   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8894 \r
8895 \r
8896   if(appData.noGUI) return;\r
8897   hdc = GetDC(hwndMain);\r
8898   if (!IsIconic(hwndMain)) {\r
8899     DisplayAClock(hdc, timeRemaining, highlight, \r
8900                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8901   }\r
8902   if (highlight && iconCurrent == iconWhite) {\r
8903     iconCurrent = iconBlack;\r
8904     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8905     if (IsIconic(hwndMain)) {\r
8906       DrawIcon(hdc, 2, 2, iconCurrent);\r
8907     }\r
8908   }\r
8909   (void) ReleaseDC(hwndMain, hdc);\r
8910   if (hwndConsole)\r
8911     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8912 }\r
8913 \r
8914 \r
8915 int\r
8916 LoadGameTimerRunning()\r
8917 {\r
8918   return loadGameTimerEvent != 0;\r
8919 }\r
8920 \r
8921 int\r
8922 StopLoadGameTimer()\r
8923 {\r
8924   if (loadGameTimerEvent == 0) return FALSE;\r
8925   KillTimer(hwndMain, loadGameTimerEvent);\r
8926   loadGameTimerEvent = 0;\r
8927   return TRUE;\r
8928 }\r
8929 \r
8930 void\r
8931 StartLoadGameTimer(long millisec)\r
8932 {\r
8933   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8934                                 (UINT) millisec, NULL);\r
8935 }\r
8936 \r
8937 void\r
8938 AutoSaveGame()\r
8939 {\r
8940   char *defName;\r
8941   FILE *f;\r
8942   char fileTitle[MSG_SIZ];\r
8943 \r
8944   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8945   f = OpenFileDialog(hwndMain, "a", defName,\r
8946                      appData.oldSaveStyle ? "gam" : "pgn",\r
8947                      GAME_FILT, \r
8948                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8949   if (f != NULL) {\r
8950     SaveGame(f, 0, "");\r
8951     fclose(f);\r
8952   }\r
8953 }\r
8954 \r
8955 \r
8956 void\r
8957 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8958 {\r
8959   if (delayedTimerEvent != 0) {\r
8960     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8961       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8962     }\r
8963     KillTimer(hwndMain, delayedTimerEvent);\r
8964     delayedTimerEvent = 0;\r
8965     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8966     delayedTimerCallback();\r
8967   }\r
8968   delayedTimerCallback = cb;\r
8969   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8970                                 (UINT) millisec, NULL);\r
8971 }\r
8972 \r
8973 DelayedEventCallback\r
8974 GetDelayedEvent()\r
8975 {\r
8976   if (delayedTimerEvent) {\r
8977     return delayedTimerCallback;\r
8978   } else {\r
8979     return NULL;\r
8980   }\r
8981 }\r
8982 \r
8983 void\r
8984 CancelDelayedEvent()\r
8985 {\r
8986   if (delayedTimerEvent) {\r
8987     KillTimer(hwndMain, delayedTimerEvent);\r
8988     delayedTimerEvent = 0;\r
8989   }\r
8990 }\r
8991 \r
8992 DWORD GetWin32Priority(int nice)\r
8993 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8994 /*\r
8995 REALTIME_PRIORITY_CLASS     0x00000100\r
8996 HIGH_PRIORITY_CLASS         0x00000080\r
8997 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8998 NORMAL_PRIORITY_CLASS       0x00000020\r
8999 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9000 IDLE_PRIORITY_CLASS         0x00000040\r
9001 */\r
9002         if (nice < -15) return 0x00000080;\r
9003         if (nice < 0)   return 0x00008000;\r
9004         if (nice == 0)  return 0x00000020;\r
9005         if (nice < 15)  return 0x00004000;\r
9006         return 0x00000040;\r
9007 }\r
9008 \r
9009 void RunCommand(char *cmdLine)\r
9010 {\r
9011   /* Now create the child process. */\r
9012   STARTUPINFO siStartInfo;\r
9013   PROCESS_INFORMATION piProcInfo;\r
9014 \r
9015   siStartInfo.cb = sizeof(STARTUPINFO);\r
9016   siStartInfo.lpReserved = NULL;\r
9017   siStartInfo.lpDesktop = NULL;\r
9018   siStartInfo.lpTitle = NULL;\r
9019   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9020   siStartInfo.cbReserved2 = 0;\r
9021   siStartInfo.lpReserved2 = NULL;\r
9022   siStartInfo.hStdInput = NULL;\r
9023   siStartInfo.hStdOutput = NULL;\r
9024   siStartInfo.hStdError = NULL;\r
9025 \r
9026   CreateProcess(NULL,\r
9027                 cmdLine,           /* command line */\r
9028                 NULL,      /* process security attributes */\r
9029                 NULL,      /* primary thread security attrs */\r
9030                 TRUE,      /* handles are inherited */\r
9031                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9032                 NULL,      /* use parent's environment */\r
9033                 NULL,\r
9034                 &siStartInfo, /* STARTUPINFO pointer */\r
9035                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9036 \r
9037   CloseHandle(piProcInfo.hThread);\r
9038 }\r
9039 \r
9040 /* Start a child process running the given program.\r
9041    The process's standard output can be read from "from", and its\r
9042    standard input can be written to "to".\r
9043    Exit with fatal error if anything goes wrong.\r
9044    Returns an opaque pointer that can be used to destroy the process\r
9045    later.\r
9046 */\r
9047 int\r
9048 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9049 {\r
9050 #define BUFSIZE 4096\r
9051 \r
9052   HANDLE hChildStdinRd, hChildStdinWr,\r
9053     hChildStdoutRd, hChildStdoutWr;\r
9054   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9055   SECURITY_ATTRIBUTES saAttr;\r
9056   BOOL fSuccess;\r
9057   PROCESS_INFORMATION piProcInfo;\r
9058   STARTUPINFO siStartInfo;\r
9059   ChildProc *cp;\r
9060   char buf[MSG_SIZ];\r
9061   DWORD err;\r
9062 \r
9063   if (appData.debugMode) {\r
9064     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9065   }\r
9066 \r
9067   *pr = NoProc;\r
9068 \r
9069   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9070   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9071   saAttr.bInheritHandle = TRUE;\r
9072   saAttr.lpSecurityDescriptor = NULL;\r
9073 \r
9074   /*\r
9075    * The steps for redirecting child's STDOUT:\r
9076    *     1. Create anonymous pipe to be STDOUT for child.\r
9077    *     2. Create a noninheritable duplicate of read handle,\r
9078    *         and close the inheritable read handle.\r
9079    */\r
9080 \r
9081   /* Create a pipe for the child's STDOUT. */\r
9082   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9083     return GetLastError();\r
9084   }\r
9085 \r
9086   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9087   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9088                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9089                              FALSE,     /* not inherited */\r
9090                              DUPLICATE_SAME_ACCESS);\r
9091   if (! fSuccess) {\r
9092     return GetLastError();\r
9093   }\r
9094   CloseHandle(hChildStdoutRd);\r
9095 \r
9096   /*\r
9097    * The steps for redirecting child's STDIN:\r
9098    *     1. Create anonymous pipe to be STDIN for child.\r
9099    *     2. Create a noninheritable duplicate of write handle,\r
9100    *         and close the inheritable write handle.\r
9101    */\r
9102 \r
9103   /* Create a pipe for the child's STDIN. */\r
9104   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9105     return GetLastError();\r
9106   }\r
9107 \r
9108   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9109   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9110                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9111                              FALSE,     /* not inherited */\r
9112                              DUPLICATE_SAME_ACCESS);\r
9113   if (! fSuccess) {\r
9114     return GetLastError();\r
9115   }\r
9116   CloseHandle(hChildStdinWr);\r
9117 \r
9118   /* Arrange to (1) look in dir for the child .exe file, and\r
9119    * (2) have dir be the child's working directory.  Interpret\r
9120    * dir relative to the directory WinBoard loaded from. */\r
9121   GetCurrentDirectory(MSG_SIZ, buf);\r
9122   SetCurrentDirectory(installDir);\r
9123   SetCurrentDirectory(dir);\r
9124 \r
9125   /* Now create the child process. */\r
9126 \r
9127   siStartInfo.cb = sizeof(STARTUPINFO);\r
9128   siStartInfo.lpReserved = NULL;\r
9129   siStartInfo.lpDesktop = NULL;\r
9130   siStartInfo.lpTitle = NULL;\r
9131   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9132   siStartInfo.cbReserved2 = 0;\r
9133   siStartInfo.lpReserved2 = NULL;\r
9134   siStartInfo.hStdInput = hChildStdinRd;\r
9135   siStartInfo.hStdOutput = hChildStdoutWr;\r
9136   siStartInfo.hStdError = hChildStdoutWr;\r
9137 \r
9138   fSuccess = CreateProcess(NULL,\r
9139                            cmdLine,        /* command line */\r
9140                            NULL,           /* process security attributes */\r
9141                            NULL,           /* primary thread security attrs */\r
9142                            TRUE,           /* handles are inherited */\r
9143                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9144                            NULL,           /* use parent's environment */\r
9145                            NULL,\r
9146                            &siStartInfo, /* STARTUPINFO pointer */\r
9147                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9148 \r
9149   err = GetLastError();\r
9150   SetCurrentDirectory(buf); /* return to prev directory */\r
9151   if (! fSuccess) {\r
9152     return err;\r
9153   }\r
9154 \r
9155   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9156     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9157     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9158   }\r
9159 \r
9160   /* Close the handles we don't need in the parent */\r
9161   CloseHandle(piProcInfo.hThread);\r
9162   CloseHandle(hChildStdinRd);\r
9163   CloseHandle(hChildStdoutWr);\r
9164 \r
9165   /* Prepare return value */\r
9166   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9167   cp->kind = CPReal;\r
9168   cp->hProcess = piProcInfo.hProcess;\r
9169   cp->pid = piProcInfo.dwProcessId;\r
9170   cp->hFrom = hChildStdoutRdDup;\r
9171   cp->hTo = hChildStdinWrDup;\r
9172 \r
9173   *pr = (void *) cp;\r
9174 \r
9175   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9176      2000 where engines sometimes don't see the initial command(s)\r
9177      from WinBoard and hang.  I don't understand how that can happen,\r
9178      but the Sleep is harmless, so I've put it in.  Others have also\r
9179      reported what may be the same problem, so hopefully this will fix\r
9180      it for them too.  */\r
9181   Sleep(500);\r
9182 \r
9183   return NO_ERROR;\r
9184 }\r
9185 \r
9186 \r
9187 void\r
9188 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9189 {\r
9190   ChildProc *cp; int result;\r
9191 \r
9192   cp = (ChildProc *) pr;\r
9193   if (cp == NULL) return;\r
9194 \r
9195   switch (cp->kind) {\r
9196   case CPReal:\r
9197     /* TerminateProcess is considered harmful, so... */\r
9198     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9199     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9200     /* The following doesn't work because the chess program\r
9201        doesn't "have the same console" as WinBoard.  Maybe\r
9202        we could arrange for this even though neither WinBoard\r
9203        nor the chess program uses a console for stdio? */\r
9204     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9205 \r
9206     /* [AS] Special termination modes for misbehaving programs... */\r
9207     if( signal == 9 ) { \r
9208         result = TerminateProcess( cp->hProcess, 0 );\r
9209 \r
9210         if ( appData.debugMode) {\r
9211             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9212         }\r
9213     }\r
9214     else if( signal == 10 ) {\r
9215         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9216 \r
9217         if( dw != WAIT_OBJECT_0 ) {\r
9218             result = TerminateProcess( cp->hProcess, 0 );\r
9219 \r
9220             if ( appData.debugMode) {\r
9221                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9222             }\r
9223 \r
9224         }\r
9225     }\r
9226 \r
9227     CloseHandle(cp->hProcess);\r
9228     break;\r
9229 \r
9230   case CPComm:\r
9231     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9232     break;\r
9233 \r
9234   case CPSock:\r
9235     closesocket(cp->sock);\r
9236     WSACleanup();\r
9237     break;\r
9238 \r
9239   case CPRcmd:\r
9240     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9241     closesocket(cp->sock);\r
9242     closesocket(cp->sock2);\r
9243     WSACleanup();\r
9244     break;\r
9245   }\r
9246   free(cp);\r
9247 }\r
9248 \r
9249 void\r
9250 InterruptChildProcess(ProcRef pr)\r
9251 {\r
9252   ChildProc *cp;\r
9253 \r
9254   cp = (ChildProc *) pr;\r
9255   if (cp == NULL) return;\r
9256   switch (cp->kind) {\r
9257   case CPReal:\r
9258     /* The following doesn't work because the chess program\r
9259        doesn't "have the same console" as WinBoard.  Maybe\r
9260        we could arrange for this even though neither WinBoard\r
9261        nor the chess program uses a console for stdio */\r
9262     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9263     break;\r
9264 \r
9265   case CPComm:\r
9266   case CPSock:\r
9267     /* Can't interrupt */\r
9268     break;\r
9269 \r
9270   case CPRcmd:\r
9271     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9272     break;\r
9273   }\r
9274 }\r
9275 \r
9276 \r
9277 int\r
9278 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9279 {\r
9280   char cmdLine[MSG_SIZ];\r
9281 \r
9282   if (port[0] == NULLCHAR) {\r
9283     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9284   } else {\r
9285     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9286   }\r
9287   return StartChildProcess(cmdLine, "", pr);\r
9288 }\r
9289 \r
9290 \r
9291 /* Code to open TCP sockets */\r
9292 \r
9293 int\r
9294 OpenTCP(char *host, char *port, ProcRef *pr)\r
9295 {\r
9296   ChildProc *cp;\r
9297   int err;\r
9298   SOCKET s;\r
9299 \r
9300   struct sockaddr_in sa, mysa;\r
9301   struct hostent FAR *hp;\r
9302   unsigned short uport;\r
9303   WORD wVersionRequested;\r
9304   WSADATA wsaData;\r
9305 \r
9306   /* Initialize socket DLL */\r
9307   wVersionRequested = MAKEWORD(1, 1);\r
9308   err = WSAStartup(wVersionRequested, &wsaData);\r
9309   if (err != 0) return err;\r
9310 \r
9311   /* Make socket */\r
9312   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9313     err = WSAGetLastError();\r
9314     WSACleanup();\r
9315     return err;\r
9316   }\r
9317 \r
9318   /* Bind local address using (mostly) don't-care values.\r
9319    */\r
9320   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9321   mysa.sin_family = AF_INET;\r
9322   mysa.sin_addr.s_addr = INADDR_ANY;\r
9323   uport = (unsigned short) 0;\r
9324   mysa.sin_port = htons(uport);\r
9325   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9326       == SOCKET_ERROR) {\r
9327     err = WSAGetLastError();\r
9328     WSACleanup();\r
9329     return err;\r
9330   }\r
9331 \r
9332   /* Resolve remote host name */\r
9333   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9334   if (!(hp = gethostbyname(host))) {\r
9335     unsigned int b0, b1, b2, b3;\r
9336 \r
9337     err = WSAGetLastError();\r
9338 \r
9339     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9340       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9341       hp->h_addrtype = AF_INET;\r
9342       hp->h_length = 4;\r
9343       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9344       hp->h_addr_list[0] = (char *) malloc(4);\r
9345       hp->h_addr_list[0][0] = (char) b0;\r
9346       hp->h_addr_list[0][1] = (char) b1;\r
9347       hp->h_addr_list[0][2] = (char) b2;\r
9348       hp->h_addr_list[0][3] = (char) b3;\r
9349     } else {\r
9350       WSACleanup();\r
9351       return err;\r
9352     }\r
9353   }\r
9354   sa.sin_family = hp->h_addrtype;\r
9355   uport = (unsigned short) atoi(port);\r
9356   sa.sin_port = htons(uport);\r
9357   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9358 \r
9359   /* Make connection */\r
9360   if (connect(s, (struct sockaddr *) &sa,\r
9361               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9362     err = WSAGetLastError();\r
9363     WSACleanup();\r
9364     return err;\r
9365   }\r
9366 \r
9367   /* Prepare return value */\r
9368   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9369   cp->kind = CPSock;\r
9370   cp->sock = s;\r
9371   *pr = (ProcRef *) cp;\r
9372 \r
9373   return NO_ERROR;\r
9374 }\r
9375 \r
9376 int\r
9377 OpenCommPort(char *name, ProcRef *pr)\r
9378 {\r
9379   HANDLE h;\r
9380   COMMTIMEOUTS ct;\r
9381   ChildProc *cp;\r
9382   char fullname[MSG_SIZ];\r
9383 \r
9384   if (*name != '\\')\r
9385     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9386   else\r
9387     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9388 \r
9389   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9390                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9391   if (h == (HANDLE) -1) {\r
9392     return GetLastError();\r
9393   }\r
9394   hCommPort = h;\r
9395 \r
9396   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9397 \r
9398   /* Accumulate characters until a 100ms pause, then parse */\r
9399   ct.ReadIntervalTimeout = 100;\r
9400   ct.ReadTotalTimeoutMultiplier = 0;\r
9401   ct.ReadTotalTimeoutConstant = 0;\r
9402   ct.WriteTotalTimeoutMultiplier = 0;\r
9403   ct.WriteTotalTimeoutConstant = 0;\r
9404   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9405 \r
9406   /* Prepare return value */\r
9407   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9408   cp->kind = CPComm;\r
9409   cp->hFrom = h;\r
9410   cp->hTo = h;\r
9411   *pr = (ProcRef *) cp;\r
9412 \r
9413   return NO_ERROR;\r
9414 }\r
9415 \r
9416 int\r
9417 OpenLoopback(ProcRef *pr)\r
9418 {\r
9419   DisplayFatalError(_("Not implemented"), 0, 1);\r
9420   return NO_ERROR;\r
9421 }\r
9422 \r
9423 \r
9424 int\r
9425 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9426 {\r
9427   ChildProc *cp;\r
9428   int err;\r
9429   SOCKET s, s2, s3;\r
9430   struct sockaddr_in sa, mysa;\r
9431   struct hostent FAR *hp;\r
9432   unsigned short uport;\r
9433   WORD wVersionRequested;\r
9434   WSADATA wsaData;\r
9435   int fromPort;\r
9436   char stderrPortStr[MSG_SIZ];\r
9437 \r
9438   /* Initialize socket DLL */\r
9439   wVersionRequested = MAKEWORD(1, 1);\r
9440   err = WSAStartup(wVersionRequested, &wsaData);\r
9441   if (err != 0) return err;\r
9442 \r
9443   /* Resolve remote host name */\r
9444   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9445   if (!(hp = gethostbyname(host))) {\r
9446     unsigned int b0, b1, b2, b3;\r
9447 \r
9448     err = WSAGetLastError();\r
9449 \r
9450     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9451       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9452       hp->h_addrtype = AF_INET;\r
9453       hp->h_length = 4;\r
9454       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9455       hp->h_addr_list[0] = (char *) malloc(4);\r
9456       hp->h_addr_list[0][0] = (char) b0;\r
9457       hp->h_addr_list[0][1] = (char) b1;\r
9458       hp->h_addr_list[0][2] = (char) b2;\r
9459       hp->h_addr_list[0][3] = (char) b3;\r
9460     } else {\r
9461       WSACleanup();\r
9462       return err;\r
9463     }\r
9464   }\r
9465   sa.sin_family = hp->h_addrtype;\r
9466   uport = (unsigned short) 514;\r
9467   sa.sin_port = htons(uport);\r
9468   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9469 \r
9470   /* Bind local socket to unused "privileged" port address\r
9471    */\r
9472   s = INVALID_SOCKET;\r
9473   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9474   mysa.sin_family = AF_INET;\r
9475   mysa.sin_addr.s_addr = INADDR_ANY;\r
9476   for (fromPort = 1023;; fromPort--) {\r
9477     if (fromPort < 0) {\r
9478       WSACleanup();\r
9479       return WSAEADDRINUSE;\r
9480     }\r
9481     if (s == INVALID_SOCKET) {\r
9482       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9483         err = WSAGetLastError();\r
9484         WSACleanup();\r
9485         return err;\r
9486       }\r
9487     }\r
9488     uport = (unsigned short) fromPort;\r
9489     mysa.sin_port = htons(uport);\r
9490     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9491         == SOCKET_ERROR) {\r
9492       err = WSAGetLastError();\r
9493       if (err == WSAEADDRINUSE) continue;\r
9494       WSACleanup();\r
9495       return err;\r
9496     }\r
9497     if (connect(s, (struct sockaddr *) &sa,\r
9498       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9499       err = WSAGetLastError();\r
9500       if (err == WSAEADDRINUSE) {\r
9501         closesocket(s);\r
9502         s = -1;\r
9503         continue;\r
9504       }\r
9505       WSACleanup();\r
9506       return err;\r
9507     }\r
9508     break;\r
9509   }\r
9510 \r
9511   /* Bind stderr local socket to unused "privileged" port address\r
9512    */\r
9513   s2 = INVALID_SOCKET;\r
9514   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9515   mysa.sin_family = AF_INET;\r
9516   mysa.sin_addr.s_addr = INADDR_ANY;\r
9517   for (fromPort = 1023;; fromPort--) {\r
9518     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9519     if (fromPort < 0) {\r
9520       (void) closesocket(s);\r
9521       WSACleanup();\r
9522       return WSAEADDRINUSE;\r
9523     }\r
9524     if (s2 == INVALID_SOCKET) {\r
9525       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9526         err = WSAGetLastError();\r
9527         closesocket(s);\r
9528         WSACleanup();\r
9529         return err;\r
9530       }\r
9531     }\r
9532     uport = (unsigned short) fromPort;\r
9533     mysa.sin_port = htons(uport);\r
9534     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9535         == SOCKET_ERROR) {\r
9536       err = WSAGetLastError();\r
9537       if (err == WSAEADDRINUSE) continue;\r
9538       (void) closesocket(s);\r
9539       WSACleanup();\r
9540       return err;\r
9541     }\r
9542     if (listen(s2, 1) == SOCKET_ERROR) {\r
9543       err = WSAGetLastError();\r
9544       if (err == WSAEADDRINUSE) {\r
9545         closesocket(s2);\r
9546         s2 = INVALID_SOCKET;\r
9547         continue;\r
9548       }\r
9549       (void) closesocket(s);\r
9550       (void) closesocket(s2);\r
9551       WSACleanup();\r
9552       return err;\r
9553     }\r
9554     break;\r
9555   }\r
9556   prevStderrPort = fromPort; // remember port used\r
9557   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9558 \r
9559   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9560     err = WSAGetLastError();\r
9561     (void) closesocket(s);\r
9562     (void) closesocket(s2);\r
9563     WSACleanup();\r
9564     return err;\r
9565   }\r
9566 \r
9567   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9568     err = WSAGetLastError();\r
9569     (void) closesocket(s);\r
9570     (void) closesocket(s2);\r
9571     WSACleanup();\r
9572     return err;\r
9573   }\r
9574   if (*user == NULLCHAR) user = UserName();\r
9575   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9576     err = WSAGetLastError();\r
9577     (void) closesocket(s);\r
9578     (void) closesocket(s2);\r
9579     WSACleanup();\r
9580     return err;\r
9581   }\r
9582   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9583     err = WSAGetLastError();\r
9584     (void) closesocket(s);\r
9585     (void) closesocket(s2);\r
9586     WSACleanup();\r
9587     return err;\r
9588   }\r
9589 \r
9590   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9591     err = WSAGetLastError();\r
9592     (void) closesocket(s);\r
9593     (void) closesocket(s2);\r
9594     WSACleanup();\r
9595     return err;\r
9596   }\r
9597   (void) closesocket(s2);  /* Stop listening */\r
9598 \r
9599   /* Prepare return value */\r
9600   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9601   cp->kind = CPRcmd;\r
9602   cp->sock = s;\r
9603   cp->sock2 = s3;\r
9604   *pr = (ProcRef *) cp;\r
9605 \r
9606   return NO_ERROR;\r
9607 }\r
9608 \r
9609 \r
9610 InputSourceRef\r
9611 AddInputSource(ProcRef pr, int lineByLine,\r
9612                InputCallback func, VOIDSTAR closure)\r
9613 {\r
9614   InputSource *is, *is2 = NULL;\r
9615   ChildProc *cp = (ChildProc *) pr;\r
9616 \r
9617   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9618   is->lineByLine = lineByLine;\r
9619   is->func = func;\r
9620   is->closure = closure;\r
9621   is->second = NULL;\r
9622   is->next = is->buf;\r
9623   if (pr == NoProc) {\r
9624     is->kind = CPReal;\r
9625     consoleInputSource = is;\r
9626   } else {\r
9627     is->kind = cp->kind;\r
9628     /* \r
9629         [AS] Try to avoid a race condition if the thread is given control too early:\r
9630         we create all threads suspended so that the is->hThread variable can be\r
9631         safely assigned, then let the threads start with ResumeThread.\r
9632     */\r
9633     switch (cp->kind) {\r
9634     case CPReal:\r
9635       is->hFile = cp->hFrom;\r
9636       cp->hFrom = NULL; /* now owned by InputThread */\r
9637       is->hThread =\r
9638         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9639                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9640       break;\r
9641 \r
9642     case CPComm:\r
9643       is->hFile = cp->hFrom;\r
9644       cp->hFrom = NULL; /* now owned by InputThread */\r
9645       is->hThread =\r
9646         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9647                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9648       break;\r
9649 \r
9650     case CPSock:\r
9651       is->sock = cp->sock;\r
9652       is->hThread =\r
9653         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9654                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9655       break;\r
9656 \r
9657     case CPRcmd:\r
9658       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9659       *is2 = *is;\r
9660       is->sock = cp->sock;\r
9661       is->second = is2;\r
9662       is2->sock = cp->sock2;\r
9663       is2->second = is2;\r
9664       is->hThread =\r
9665         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9666                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9667       is2->hThread =\r
9668         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9669                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9670       break;\r
9671     }\r
9672 \r
9673     if( is->hThread != NULL ) {\r
9674         ResumeThread( is->hThread );\r
9675     }\r
9676 \r
9677     if( is2 != NULL && is2->hThread != NULL ) {\r
9678         ResumeThread( is2->hThread );\r
9679     }\r
9680   }\r
9681 \r
9682   return (InputSourceRef) is;\r
9683 }\r
9684 \r
9685 void\r
9686 RemoveInputSource(InputSourceRef isr)\r
9687 {\r
9688   InputSource *is;\r
9689 \r
9690   is = (InputSource *) isr;\r
9691   is->hThread = NULL;  /* tell thread to stop */\r
9692   CloseHandle(is->hThread);\r
9693   if (is->second != NULL) {\r
9694     is->second->hThread = NULL;\r
9695     CloseHandle(is->second->hThread);\r
9696   }\r
9697 }\r
9698 \r
9699 int no_wrap(char *message, int count)\r
9700 {\r
9701     ConsoleOutput(message, count, FALSE);\r
9702     return count;\r
9703 }\r
9704 \r
9705 int\r
9706 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9707 {\r
9708   DWORD dOutCount;\r
9709   int outCount = SOCKET_ERROR;\r
9710   ChildProc *cp = (ChildProc *) pr;\r
9711   static OVERLAPPED ovl;\r
9712   static int line = 0;\r
9713 \r
9714   if (pr == NoProc)\r
9715   {\r
9716     if (appData.noJoin || !appData.useInternalWrap)\r
9717       return no_wrap(message, count);\r
9718     else\r
9719     {\r
9720       int width = get_term_width();\r
9721       int len = wrap(NULL, message, count, width, &line);\r
9722       char *msg = malloc(len);\r
9723       int dbgchk;\r
9724 \r
9725       if (!msg)\r
9726         return no_wrap(message, count);\r
9727       else\r
9728       {\r
9729         dbgchk = wrap(msg, message, count, width, &line);\r
9730         if (dbgchk != len && appData.debugMode)\r
9731             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9732         ConsoleOutput(msg, len, FALSE);\r
9733         free(msg);\r
9734         return len;\r
9735       }\r
9736     }\r
9737   }\r
9738 \r
9739   if (ovl.hEvent == NULL) {\r
9740     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9741   }\r
9742   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9743 \r
9744   switch (cp->kind) {\r
9745   case CPSock:\r
9746   case CPRcmd:\r
9747     outCount = send(cp->sock, message, count, 0);\r
9748     if (outCount == SOCKET_ERROR) {\r
9749       *outError = WSAGetLastError();\r
9750     } else {\r
9751       *outError = NO_ERROR;\r
9752     }\r
9753     break;\r
9754 \r
9755   case CPReal:\r
9756     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9757                   &dOutCount, NULL)) {\r
9758       *outError = NO_ERROR;\r
9759       outCount = (int) dOutCount;\r
9760     } else {\r
9761       *outError = GetLastError();\r
9762     }\r
9763     break;\r
9764 \r
9765   case CPComm:\r
9766     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9767                             &dOutCount, &ovl);\r
9768     if (*outError == NO_ERROR) {\r
9769       outCount = (int) dOutCount;\r
9770     }\r
9771     break;\r
9772   }\r
9773   return outCount;\r
9774 }\r
9775 \r
9776 void\r
9777 DoSleep(int n)\r
9778 {\r
9779     if(n != 0) Sleep(n);\r
9780 }\r
9781 \r
9782 int\r
9783 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9784                        long msdelay)\r
9785 {\r
9786   /* Ignore delay, not implemented for WinBoard */\r
9787   return OutputToProcess(pr, message, count, outError);\r
9788 }\r
9789 \r
9790 \r
9791 void\r
9792 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9793                         char *buf, int count, int error)\r
9794 {\r
9795   DisplayFatalError(_("Not implemented"), 0, 1);\r
9796 }\r
9797 \r
9798 /* see wgamelist.c for Game List functions */\r
9799 /* see wedittags.c for Edit Tags functions */\r
9800 \r
9801 \r
9802 int\r
9803 ICSInitScript()\r
9804 {\r
9805   FILE *f;\r
9806   char buf[MSG_SIZ];\r
9807   char *dummy;\r
9808 \r
9809   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9810     f = fopen(buf, "r");\r
9811     if (f != NULL) {\r
9812       ProcessICSInitScript(f);\r
9813       fclose(f);\r
9814       return TRUE;\r
9815     }\r
9816   }\r
9817   return FALSE;\r
9818 }\r
9819 \r
9820 \r
9821 VOID\r
9822 StartAnalysisClock()\r
9823 {\r
9824   if (analysisTimerEvent) return;\r
9825   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9826                                         (UINT) 2000, NULL);\r
9827 }\r
9828 \r
9829 VOID\r
9830 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9831 {\r
9832   highlightInfo.sq[0].x = fromX;\r
9833   highlightInfo.sq[0].y = fromY;\r
9834   highlightInfo.sq[1].x = toX;\r
9835   highlightInfo.sq[1].y = toY;\r
9836 }\r
9837 \r
9838 VOID\r
9839 ClearHighlights()\r
9840 {\r
9841   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9842     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9843 }\r
9844 \r
9845 VOID\r
9846 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9847 {\r
9848   premoveHighlightInfo.sq[0].x = fromX;\r
9849   premoveHighlightInfo.sq[0].y = fromY;\r
9850   premoveHighlightInfo.sq[1].x = toX;\r
9851   premoveHighlightInfo.sq[1].y = toY;\r
9852 }\r
9853 \r
9854 VOID\r
9855 ClearPremoveHighlights()\r
9856 {\r
9857   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9858     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9859 }\r
9860 \r
9861 VOID\r
9862 ShutDownFrontEnd()\r
9863 {\r
9864   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9865   DeleteClipboardTempFiles();\r
9866 }\r
9867 \r
9868 void\r
9869 BoardToTop()\r
9870 {\r
9871     if (IsIconic(hwndMain))\r
9872       ShowWindow(hwndMain, SW_RESTORE);\r
9873 \r
9874     SetActiveWindow(hwndMain);\r
9875 }\r
9876 \r
9877 /*\r
9878  * Prototypes for animation support routines\r
9879  */\r
9880 static void ScreenSquare(int column, int row, POINT * pt);\r
9881 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9882      POINT frames[], int * nFrames);\r
9883 \r
9884 \r
9885 #define kFactor 4\r
9886 \r
9887 void\r
9888 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9889 {       // [HGM] atomic: animate blast wave\r
9890         int i;\r
9891 \r
9892         explodeInfo.fromX = fromX;\r
9893         explodeInfo.fromY = fromY;\r
9894         explodeInfo.toX = toX;\r
9895         explodeInfo.toY = toY;\r
9896         for(i=1; i<4*kFactor; i++) {\r
9897             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9898             DrawPosition(FALSE, board);\r
9899             Sleep(appData.animSpeed);\r
9900         }\r
9901         explodeInfo.radius = 0;\r
9902         DrawPosition(TRUE, board);\r
9903 }\r
9904 \r
9905 void\r
9906 AnimateMove(board, fromX, fromY, toX, toY)\r
9907      Board board;\r
9908      int fromX;\r
9909      int fromY;\r
9910      int toX;\r
9911      int toY;\r
9912 {\r
9913   ChessSquare piece;\r
9914   int x = toX, y = toY;\r
9915   POINT start, finish, mid;\r
9916   POINT frames[kFactor * 2 + 1];\r
9917   int nFrames, n;\r
9918 \r
9919   if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();\r
9920 \r
9921   if (!appData.animate) return;\r
9922   if (doingSizing) return;\r
9923   if (fromY < 0 || fromX < 0) return;\r
9924   piece = board[fromY][fromX];\r
9925   if (piece >= EmptySquare) return;\r
9926 \r
9927   if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square\r
9928 \r
9929 again:\r
9930 \r
9931   ScreenSquare(fromX, fromY, &start);\r
9932   ScreenSquare(toX, toY, &finish);\r
9933 \r
9934   /* All moves except knight jumps move in straight line */\r
9935   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9936     mid.x = start.x + (finish.x - start.x) / 2;\r
9937     mid.y = start.y + (finish.y - start.y) / 2;\r
9938   } else {\r
9939     /* Knight: make straight movement then diagonal */\r
9940     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9941        mid.x = start.x + (finish.x - start.x) / 2;\r
9942        mid.y = start.y;\r
9943      } else {\r
9944        mid.x = start.x;\r
9945        mid.y = start.y + (finish.y - start.y) / 2;\r
9946      }\r
9947   }\r
9948   \r
9949   /* Don't use as many frames for very short moves */\r
9950   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9951     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9952   else\r
9953     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9954 \r
9955   animInfo.from.x = fromX;\r
9956   animInfo.from.y = fromY;\r
9957   animInfo.to.x = toX;\r
9958   animInfo.to.y = toY;\r
9959   animInfo.lastpos = start;\r
9960   animInfo.piece = piece;\r
9961   for (n = 0; n < nFrames; n++) {\r
9962     animInfo.pos = frames[n];\r
9963     DrawPosition(FALSE, NULL);\r
9964     animInfo.lastpos = animInfo.pos;\r
9965     Sleep(appData.animSpeed);\r
9966   }\r
9967   animInfo.pos = finish;\r
9968   DrawPosition(FALSE, NULL);\r
9969 \r
9970   if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg\r
9971 \r
9972   animInfo.piece = EmptySquare;\r
9973   Explode(board, fromX, fromY, toX, toY);\r
9974 }\r
9975 \r
9976 /*      Convert board position to corner of screen rect and color       */\r
9977 \r
9978 static void\r
9979 ScreenSquare(column, row, pt)\r
9980      int column; int row; POINT * pt;\r
9981 {\r
9982   if (flipView) {\r
9983     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
9984     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
9985   } else {\r
9986     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
9987     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
9988   }\r
9989 }\r
9990 \r
9991 /*      Generate a series of frame coords from start->mid->finish.\r
9992         The movement rate doubles until the half way point is\r
9993         reached, then halves back down to the final destination,\r
9994         which gives a nice slow in/out effect. The algorithmn\r
9995         may seem to generate too many intermediates for short\r
9996         moves, but remember that the purpose is to attract the\r
9997         viewers attention to the piece about to be moved and\r
9998         then to where it ends up. Too few frames would be less\r
9999         noticeable.                                             */\r
10000 \r
10001 static void\r
10002 Tween(start, mid, finish, factor, frames, nFrames)\r
10003      POINT * start; POINT * mid;\r
10004      POINT * finish; int factor;\r
10005      POINT frames[]; int * nFrames;\r
10006 {\r
10007   int n, fraction = 1, count = 0;\r
10008 \r
10009   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10010   for (n = 0; n < factor; n++)\r
10011     fraction *= 2;\r
10012   for (n = 0; n < factor; n++) {\r
10013     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10014     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10015     count ++;\r
10016     fraction = fraction / 2;\r
10017   }\r
10018   \r
10019   /* Midpoint */\r
10020   frames[count] = *mid;\r
10021   count ++;\r
10022   \r
10023   /* Slow out, stepping 1/2, then 1/4, ... */\r
10024   fraction = 2;\r
10025   for (n = 0; n < factor; n++) {\r
10026     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10027     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10028     count ++;\r
10029     fraction = fraction * 2;\r
10030   }\r
10031   *nFrames = count;\r
10032 }\r
10033 \r
10034 void\r
10035 SettingsPopUp(ChessProgramState *cps)\r
10036 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10037       EngineOptionsPopup(savedHwnd, cps);\r
10038 }\r
10039 \r
10040 int flock(int fid, int code)\r
10041 {\r
10042     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10043     OVERLAPPED ov;\r
10044     ov.hEvent = NULL;\r
10045     ov.Offset = 0;\r
10046     ov.OffsetHigh = 0;\r
10047     switch(code) {\r
10048       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10049       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10050       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10051       default: return -1;\r
10052     }\r
10053     return 0;\r
10054 }\r
10055 \r
10056 char *\r
10057 Col2Text (int n)\r
10058 {\r
10059     static int i=0;\r
10060     static char col[8][20];\r
10061     COLORREF color = *(COLORREF *) colorVariable[n];\r
10062     i = i+1 & 7;\r
10063     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10064     return col[i];\r
10065 }\r
10066 \r
10067 void\r
10068 ActivateTheme (int new)\r
10069 {   // Redo initialization of features depending on options that can occur in themes\r
10070    InitTextures();\r
10071    if(new) InitDrawingColors();\r
10072    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10073    InitDrawingSizes(boardSize, 0);\r
10074    InvalidateRect(hwndMain, NULL, TRUE);\r
10075 }\r