Provide DoEvents function in front-ends
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts.\r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 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 #define SLASH '/'\r
96 #define DATADIR "~~"\r
97 \r
98 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
99 \r
100   int myrandom(void);\r
101   void mysrandom(unsigned int seed);\r
102 \r
103 extern int whiteFlag, blackFlag;\r
104 Boolean flipClock = FALSE;\r
105 extern HANDLE chatHandle[];\r
106 extern enum ICS_TYPE ics_type;\r
107 \r
108 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
109 int  MyGetFullPathName P((char *name, char *fullname));\r
110 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
111 VOID NewVariantPopup(HWND hwnd);\r
112 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
113                    /*char*/int promoChar));\r
114 void DisplayMove P((int moveNumber));\r
115 void ChatPopUp P((char *s));\r
116 typedef struct {\r
117   ChessSquare piece;  \r
118   POINT pos;      /* window coordinates of current pos */\r
119   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
120   POINT from;     /* board coordinates of the piece's orig pos */\r
121   POINT to;       /* board coordinates of the piece's new pos */\r
122 } AnimInfo;\r
123 \r
124 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
125 \r
126 typedef struct {\r
127   POINT start;    /* window coordinates of start pos */\r
128   POINT pos;      /* window coordinates of current pos */\r
129   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
130   POINT from;     /* board coordinates of the piece's orig pos */\r
131   ChessSquare piece;\r
132 } DragInfo;\r
133 \r
134 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
135 \r
136 typedef struct {\r
137   POINT sq[2];    /* board coordinates of from, to squares */\r
138 } HighlightInfo;\r
139 \r
140 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
141 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
142 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
143 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
144 \r
145 typedef struct { // [HGM] atomic\r
146   int fromX, fromY, toX, toY, radius;\r
147 } ExplodeInfo;\r
148 \r
149 static ExplodeInfo explodeInfo;\r
150 \r
151 /* Window class names */\r
152 char szAppName[] = "WinBoard";\r
153 char szConsoleName[] = "WBConsole";\r
154 \r
155 /* Title bar text */\r
156 char szTitle[] = "WinBoard";\r
157 char szConsoleTitle[] = "I C S Interaction";\r
158 \r
159 char *programName;\r
160 char *settingsFileName;\r
161 Boolean saveSettingsOnExit;\r
162 char installDir[MSG_SIZ];\r
163 int errorExitStatus;\r
164 \r
165 BoardSize boardSize;\r
166 Boolean chessProgram;\r
167 //static int boardX, boardY;\r
168 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
169 int squareSize, lineGap, minorSize, border;\r
170 static int winW, winH;\r
171 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
172 static int logoHeight = 0;\r
173 static char messageText[MESSAGE_TEXT_MAX];\r
174 static int clockTimerEvent = 0;\r
175 static int loadGameTimerEvent = 0;\r
176 static int analysisTimerEvent = 0;\r
177 static DelayedEventCallback delayedTimerCallback;\r
178 static int delayedTimerEvent = 0;\r
179 static int buttonCount = 2;\r
180 char *icsTextMenuString;\r
181 char *icsNames;\r
182 char *firstChessProgramNames;\r
183 char *secondChessProgramNames;\r
184 \r
185 #define PALETTESIZE 256\r
186 \r
187 HINSTANCE hInst;          /* current instance */\r
188 Boolean alwaysOnTop = FALSE;\r
189 RECT boardRect;\r
190 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
191   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
192 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };\r
193 HPALETTE hPal;\r
194 ColorClass currentColorClass;\r
195 \r
196 static HWND savedHwnd;\r
197 HWND hCommPort = NULL;    /* currently open comm port */\r
198 static HWND hwndPause;    /* pause button */\r
199 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
200 static HBRUSH lightSquareBrush, darkSquareBrush,\r
201   blackSquareBrush, /* [HGM] for band between board and holdings */\r
202   explodeBrush,     /* [HGM] atomic */\r
203   markerBrush[8],   /* [HGM] markers */\r
204   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
205 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
206 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
207 static HPEN gridPen = NULL;\r
208 static HPEN highlightPen = NULL;\r
209 static HPEN premovePen = NULL;\r
210 static NPLOGPALETTE pLogPal;\r
211 static BOOL paletteChanged = FALSE;\r
212 static HICON iconWhite, iconBlack, iconCurrent;\r
213 static int doingSizing = FALSE;\r
214 static int lastSizing = 0;\r
215 static int prevStderrPort;\r
216 static HBITMAP userLogo;\r
217 \r
218 static HBITMAP liteBackTexture = NULL;\r
219 static HBITMAP darkBackTexture = NULL;\r
220 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
221 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
222 static int backTextureSquareSize = 0;\r
223 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
224 \r
225 #if __GNUC__ && !defined(_winmajor)\r
226 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
227 #else\r
228 \r
229 #if defined(_winmajor)\r
230 #define oldDialog (_winmajor < 4)\r
231 #else\r
232 #define oldDialog 0\r
233 #endif\r
234 #endif\r
235 \r
236 #define INTERNATIONAL\r
237 \r
238 #ifdef INTERNATIONAL\r
239 #  define _(s) T_(s)\r
240 #  define N_(s) s\r
241 #else\r
242 #  define _(s) s\r
243 #  define N_(s) s\r
244 #  define T_(s) s\r
245 #  define Translate(x, y)\r
246 #  define LoadLanguageFile(s)\r
247 #endif\r
248 \r
249 #ifdef INTERNATIONAL\r
250 \r
251 Boolean barbaric; // flag indicating if translation is needed\r
252 \r
253 // list of item numbers used in each dialog (used to alter language at run time)\r
254 \r
255 #define ABOUTBOX -1  /* not sure why these are needed */\r
256 #define ABOUTBOX2 -1\r
257 \r
258 int dialogItems[][42] = {\r
259 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
260 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
261   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
262 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
263   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds,\r
264   OPT_Ranget, IDOK, IDCANCEL }, \r
265 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
266   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
267 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
268 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
269   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
270 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
271 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
272   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
273 { ABOUTBOX2, IDC_ChessBoard }, \r
274 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
275   OPT_GameListClose, IDC_GameListDoFilter }, \r
276 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
277 { DLG_Error, IDOK }, \r
278 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
279   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
280 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
281 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
282   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
283   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
284 { DLG_IndexNumber, IDC_Index }, \r
285 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
286 { DLG_TypeInName, IDOK, IDCANCEL }, \r
287 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
288   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
289 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
290   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
291   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
292   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
293   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
294   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
295   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
296 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
297   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
298   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
299   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
300   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
301   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
302   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
303   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
304   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
305 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
306   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
307   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
308   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
309   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
310   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
311   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
312   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
313 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
314   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
315   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
316   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
317   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
318   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
319   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
320   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
321   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
322 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
323   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
324   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
325   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
326   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
327 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
328 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
329   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
330 { DLG_MoveHistory }, \r
331 { DLG_EvalGraph }, \r
332 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
333 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
334 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
335   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
336   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
337   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
338 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
339   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
340   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
341 { 0 }\r
342 };\r
343 \r
344 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
345 static int lastChecked;\r
346 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
347 extern int tinyLayout;\r
348 extern char * menuBarText[][10];\r
349 \r
350 void\r
351 LoadLanguageFile(char *name)\r
352 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
353     FILE *f;\r
354     int i=0, j=0, n=0, k;\r
355     char buf[MSG_SIZ];\r
356 \r
357     if(!name || name[0] == NULLCHAR) return;\r
358       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
359     appData.language = oldLanguage;\r
360     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
361     if((f = fopen(buf, "r")) == NULL) return;\r
362     while((k = fgetc(f)) != EOF) {\r
363         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
364         languageBuf[i] = k;\r
365         if(k == '\n') {\r
366             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
367                 char *p;\r
368                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
369                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
370                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
371                         english[j] = languageBuf + n + 1; *p = 0;\r
372                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
373 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
374                     }\r
375                 }\r
376             }\r
377             n = i + 1;\r
378         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
379             switch(k) {\r
380               case 'n': k = '\n'; break;\r
381               case 'r': k = '\r'; break;\r
382               case 't': k = '\t'; break;\r
383             }\r
384             languageBuf[--i] = k;\r
385         }\r
386         i++;\r
387     }\r
388     fclose(f);\r
389     barbaric = (j != 0);\r
390     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
391 }\r
392 \r
393 char *\r
394 T_(char *s)\r
395 {   // return the translation of the given string\r
396     // efficiency can be improved a lot...\r
397     int i=0;\r
398     static char buf[MSG_SIZ];\r
399 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
400     if(!barbaric) return s;\r
401     if(!s) return ""; // sanity\r
402     while(english[i]) {\r
403         if(!strcmp(s, english[i])) return foreign[i];\r
404         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
405             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
406             return buf;\r
407         }\r
408         i++;\r
409     }\r
410     return s;\r
411 }\r
412 \r
413 void\r
414 Translate(HWND hDlg, int dialogID)\r
415 {   // translate all text items in the given dialog\r
416     int i=0, j, k;\r
417     char buf[MSG_SIZ], *s;\r
418     if(!barbaric) return;\r
419     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
420     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
421     GetWindowText( hDlg, buf, MSG_SIZ );\r
422     s = T_(buf);\r
423     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
424     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
425         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
426         if(strlen(buf) == 0) continue;\r
427         s = T_(buf);\r
428         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
429     }\r
430 }\r
431 \r
432 HMENU\r
433 TranslateOneMenu(int i, HMENU subMenu)\r
434 {\r
435     int j;\r
436     static MENUITEMINFO info;\r
437 \r
438     info.cbSize = sizeof(MENUITEMINFO);\r
439     info.fMask = MIIM_STATE | MIIM_TYPE;\r
440           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
441             char buf[MSG_SIZ];\r
442             info.dwTypeData = buf;\r
443             info.cch = sizeof(buf);\r
444             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
445             if(i < 10) {\r
446                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
447                 else menuText[i][j] = strdup(buf); // remember original on first change\r
448             }\r
449             if(buf[0] == NULLCHAR) continue;\r
450             info.dwTypeData = T_(buf);\r
451             info.cch = strlen(buf)+1;\r
452             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
453           }\r
454     return subMenu;\r
455 }\r
456 \r
457 void\r
458 TranslateMenus(int addLanguage)\r
459 {\r
460     int i;\r
461     WIN32_FIND_DATA fileData;\r
462     HANDLE hFind;\r
463 #define IDM_English 1970\r
464     if(1) {\r
465         HMENU mainMenu = GetMenu(hwndMain);\r
466         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
467           HMENU subMenu = GetSubMenu(mainMenu, i);\r
468           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
469                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
470           TranslateOneMenu(i, subMenu);\r
471         }\r
472         DrawMenuBar(hwndMain);\r
473     }\r
474 \r
475     if(!addLanguage) return;\r
476     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
477         HMENU mainMenu = GetMenu(hwndMain);\r
478         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
479         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
480         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
481         i = 0; lastChecked = IDM_English;\r
482         do {\r
483             char *p, *q = fileData.cFileName;\r
484             int checkFlag = MF_UNCHECKED;\r
485             languageFile[i] = strdup(q);\r
486             if(barbaric && !strcmp(oldLanguage, q)) {\r
487                 checkFlag = MF_CHECKED;\r
488                 lastChecked = IDM_English + i + 1;\r
489                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
490             }\r
491             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
492             p = strstr(fileData.cFileName, ".lng");\r
493             if(p) *p = 0;\r
494             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
495         } while(FindNextFile(hFind, &fileData));\r
496         FindClose(hFind);\r
497     }\r
498 }\r
499 \r
500 #endif\r
501 \r
502 #define IDM_RecentEngines 3000\r
503 \r
504 void\r
505 RecentEngineMenu (char *s)\r
506 {\r
507     if(appData.icsActive) return;\r
508     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
509         HMENU mainMenu = GetMenu(hwndMain);\r
510         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
511         int i=IDM_RecentEngines;\r
512         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
513         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
514         while(*s) {\r
515           char *p = strchr(s, '\n');\r
516           if(p == NULL) return; // malformed!\r
517           *p = NULLCHAR;\r
518           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
519           *p = '\n';\r
520           s = p+1;\r
521         }\r
522     }\r
523 }\r
524 \r
525 \r
526 typedef struct {\r
527   char *name;\r
528   int squareSize;\r
529   int lineGap;\r
530   int smallLayout;\r
531   int tinyLayout;\r
532   int cliWidth, cliHeight;\r
533 } SizeInfo;\r
534 \r
535 SizeInfo sizeInfo[] = \r
536 {\r
537   { "tiny",     21, 0, 1, 1, 0, 0 },\r
538   { "teeny",    25, 1, 1, 1, 0, 0 },\r
539   { "dinky",    29, 1, 1, 1, 0, 0 },\r
540   { "petite",   33, 1, 1, 1, 0, 0 },\r
541   { "slim",     37, 2, 1, 0, 0, 0 },\r
542   { "small",    40, 2, 1, 0, 0, 0 },\r
543   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
544   { "middling", 49, 2, 0, 0, 0, 0 },\r
545   { "average",  54, 2, 0, 0, 0, 0 },\r
546   { "moderate", 58, 3, 0, 0, 0, 0 },\r
547   { "medium",   64, 3, 0, 0, 0, 0 },\r
548   { "bulky",    72, 3, 0, 0, 0, 0 },\r
549   { "large",    80, 3, 0, 0, 0, 0 },\r
550   { "big",      87, 3, 0, 0, 0, 0 },\r
551   { "huge",     95, 3, 0, 0, 0, 0 },\r
552   { "giant",    108, 3, 0, 0, 0, 0 },\r
553   { "colossal", 116, 4, 0, 0, 0, 0 },\r
554   { "titanic",  129, 4, 0, 0, 0, 0 },\r
555   { NULL, 0, 0, 0, 0, 0, 0 }\r
556 };\r
557 \r
558 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
559 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
560 {\r
561   { 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
562   { 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
563   { 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
564   { 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
565   { 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
566   { 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
567   { 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
568   { 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
569   { 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
570   { 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
571   { 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
572   { 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
573   { 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
574   { 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
575   { 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
576   { 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
577   { 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
578   { 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
579 };\r
580 \r
581 MyFont *font[NUM_SIZES][NUM_FONTS];\r
582 \r
583 typedef struct {\r
584   char *label;\r
585   int id;\r
586   HWND hwnd;\r
587   WNDPROC wndproc;\r
588 } MyButtonDesc;\r
589 \r
590 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
591 #define N_BUTTONS 5\r
592 \r
593 MyButtonDesc buttonDesc[N_BUTTONS] =\r
594 {\r
595   {"<<", IDM_ToStart, NULL, NULL},\r
596   {"<", IDM_Backward, NULL, NULL},\r
597   {"P", IDM_Pause, NULL, NULL},\r
598   {">", IDM_Forward, NULL, NULL},\r
599   {">>", IDM_ToEnd, NULL, NULL},\r
600 };\r
601 \r
602 int tinyLayout = 0, smallLayout = 0;\r
603 #define MENU_BAR_ITEMS 9\r
604 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
605   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
606   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
607 };\r
608 \r
609 \r
610 MySound sounds[(int)NSoundClasses];\r
611 MyTextAttribs textAttribs[(int)NColorClasses];\r
612 \r
613 MyColorizeAttribs colorizeAttribs[] = {\r
614   { (COLORREF)0, 0, N_("Shout Text") },\r
615   { (COLORREF)0, 0, N_("SShout/CShout") },\r
616   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
617   { (COLORREF)0, 0, N_("Channel Text") },\r
618   { (COLORREF)0, 0, N_("Kibitz Text") },\r
619   { (COLORREF)0, 0, N_("Tell Text") },\r
620   { (COLORREF)0, 0, N_("Challenge Text") },\r
621   { (COLORREF)0, 0, N_("Request Text") },\r
622   { (COLORREF)0, 0, N_("Seek Text") },\r
623   { (COLORREF)0, 0, N_("Normal Text") },\r
624   { (COLORREF)0, 0, N_("None") }\r
625 };\r
626 \r
627 \r
628 \r
629 static char *commentTitle;\r
630 static char *commentText;\r
631 static int commentIndex;\r
632 static Boolean editComment = FALSE;\r
633 \r
634 \r
635 char errorTitle[MSG_SIZ];\r
636 char errorMessage[2*MSG_SIZ];\r
637 HWND errorDialog = NULL;\r
638 BOOLEAN moveErrorMessageUp = FALSE;\r
639 BOOLEAN consoleEcho = TRUE;\r
640 CHARFORMAT consoleCF;\r
641 COLORREF consoleBackgroundColor;\r
642 \r
643 char *programVersion;\r
644 \r
645 #define CPReal 1\r
646 #define CPComm 2\r
647 #define CPSock 3\r
648 #define CPRcmd 4\r
649 typedef int CPKind;\r
650 \r
651 typedef struct {\r
652   CPKind kind;\r
653   HANDLE hProcess;\r
654   DWORD pid;\r
655   HANDLE hTo;\r
656   HANDLE hFrom;\r
657   SOCKET sock;\r
658   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
659 } ChildProc;\r
660 \r
661 #define INPUT_SOURCE_BUF_SIZE 4096\r
662 \r
663 typedef struct _InputSource {\r
664   CPKind kind;\r
665   HANDLE hFile;\r
666   SOCKET sock;\r
667   int lineByLine;\r
668   HANDLE hThread;\r
669   DWORD id;\r
670   char buf[INPUT_SOURCE_BUF_SIZE];\r
671   char *next;\r
672   DWORD count;\r
673   int error;\r
674   InputCallback func;\r
675   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
676   VOIDSTAR closure;\r
677 } InputSource;\r
678 \r
679 InputSource *consoleInputSource;\r
680 \r
681 DCB dcb;\r
682 \r
683 /* forward */\r
684 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
685 VOID ConsoleCreate();\r
686 LRESULT CALLBACK\r
687   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
688 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
689 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
690 VOID ParseCommSettings(char *arg, DCB *dcb);\r
691 LRESULT CALLBACK\r
692   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
693 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
694 void ParseIcsTextMenu(char *icsTextMenuString);\r
695 VOID PopUpNameDialog(char firstchar);\r
696 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
697 \r
698 /* [AS] */\r
699 int NewGameFRC();\r
700 int GameListOptions();\r
701 \r
702 int dummy; // [HGM] for obsolete args\r
703 \r
704 HWND hwndMain = NULL;        /* root window*/\r
705 HWND hwndConsole = NULL;\r
706 HWND commentDialog = NULL;\r
707 HWND moveHistoryDialog = NULL;\r
708 HWND evalGraphDialog = NULL;\r
709 HWND engineOutputDialog = NULL;\r
710 HWND gameListDialog = NULL;\r
711 HWND editTagsDialog = NULL;\r
712 \r
713 int commentUp = FALSE;\r
714 \r
715 WindowPlacement wpMain;\r
716 WindowPlacement wpConsole;\r
717 WindowPlacement wpComment;\r
718 WindowPlacement wpMoveHistory;\r
719 WindowPlacement wpEvalGraph;\r
720 WindowPlacement wpEngineOutput;\r
721 WindowPlacement wpGameList;\r
722 WindowPlacement wpTags;\r
723 \r
724 VOID EngineOptionsPopup(); // [HGM] settings\r
725 \r
726 VOID GothicPopUp(char *title, VariantClass variant);\r
727 /*\r
728  * Setting "frozen" should disable all user input other than deleting\r
729  * the window.  We do this while engines are initializing themselves.\r
730  */\r
731 static int frozen = 0;\r
732 static int oldMenuItemState[MENU_BAR_ITEMS];\r
733 void FreezeUI()\r
734 {\r
735   HMENU hmenu;\r
736   int i;\r
737 \r
738   if (frozen) return;\r
739   frozen = 1;\r
740   hmenu = GetMenu(hwndMain);\r
741   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
742     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
743   }\r
744   DrawMenuBar(hwndMain);\r
745 }\r
746 \r
747 /* Undo a FreezeUI */\r
748 void ThawUI()\r
749 {\r
750   HMENU hmenu;\r
751   int i;\r
752 \r
753   if (!frozen) return;\r
754   frozen = 0;\r
755   hmenu = GetMenu(hwndMain);\r
756   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
757     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
758   }\r
759   DrawMenuBar(hwndMain);\r
760 }\r
761 \r
762 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
763 \r
764 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
765 #ifdef JAWS\r
766 #include "jaws.c"\r
767 #else\r
768 #define JAWS_INIT\r
769 #define JAWS_ARGS\r
770 #define JAWS_ALT_INTERCEPT\r
771 #define JAWS_KBUP_NAVIGATION\r
772 #define JAWS_KBDOWN_NAVIGATION\r
773 #define JAWS_MENU_ITEMS\r
774 #define JAWS_SILENCE\r
775 #define JAWS_REPLAY\r
776 #define JAWS_ACCEL\r
777 #define JAWS_COPYRIGHT\r
778 #define JAWS_DELETE(X) X\r
779 #define SAYMACHINEMOVE()\r
780 #define SAY(X)\r
781 #endif\r
782 \r
783 /*---------------------------------------------------------------------------*\\r
784  *\r
785  * WinMain\r
786  *\r
787 \*---------------------------------------------------------------------------*/\r
788 \r
789 static void HandleMessage P((MSG *message));\r
790 static HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
791 \r
792 int APIENTRY\r
793 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
794         LPSTR lpCmdLine, int nCmdShow)\r
795 {\r
796   MSG msg;\r
797 //  INITCOMMONCONTROLSEX ex;\r
798 \r
799   debugFP = stderr;\r
800 \r
801   LoadLibrary("RICHED32.DLL");\r
802   consoleCF.cbSize = sizeof(CHARFORMAT);\r
803 \r
804   if (!InitApplication(hInstance)) {\r
805     return (FALSE);\r
806   }\r
807   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
808     return (FALSE);\r
809   }\r
810 \r
811   JAWS_INIT\r
812   TranslateMenus(1);\r
813 \r
814 //  InitCommonControlsEx(&ex);\r
815   InitCommonControls();\r
816 \r
817   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
818   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
819   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
820 \r
821   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
822 \r
823   while (GetMessage(&msg, /* message structure */\r
824                     NULL, /* handle of window receiving the message */\r
825                     0,    /* lowest message to examine */\r
826                     0))   /* highest message to examine */\r
827     {\r
828         HandleMessage(msg);\r
829     }\r
830 \r
831 \r
832   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
833 }\r
834 \r
835 static void\r
836 HandleMessage (MSG *message)\r
837 {\r
838     MSG msg = *message;\r
839 \r
840       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
841         // [HGM] navigate: switch between all windows with tab\r
842         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
843         int i, currentElement = 0;\r
844 \r
845         // first determine what element of the chain we come from (if any)\r
846         if(appData.icsActive) {\r
847             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
848             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
849         }\r
850         if(engineOutputDialog && EngineOutputIsUp()) {\r
851             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
852             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
853         }\r
854         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
855             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
856         }\r
857         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
858         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
859         if(msg.hwnd == e1)                 currentElement = 2; else\r
860         if(msg.hwnd == e2)                 currentElement = 3; else\r
861         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
862         if(msg.hwnd == mh)                currentElement = 4; else\r
863         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
864         if(msg.hwnd == hText)  currentElement = 5; else\r
865         if(msg.hwnd == hInput) currentElement = 6; else\r
866         for (i = 0; i < N_BUTTONS; i++) {\r
867             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
868         }\r
869 \r
870         // determine where to go to\r
871         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
872           do {\r
873             currentElement = (currentElement + direction) % 7;\r
874             switch(currentElement) {\r
875                 case 0:\r
876                   h = hwndMain; break; // passing this case always makes the loop exit\r
877                 case 1:\r
878                   h = buttonDesc[0].hwnd; break; // could be NULL\r
879                 case 2:\r
880                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
881                   h = e1; break;\r
882                 case 3:\r
883                   if(!EngineOutputIsUp()) continue;\r
884                   h = e2; break;\r
885                 case 4:\r
886                   if(!MoveHistoryIsUp()) continue;\r
887                   h = mh; break;\r
888 //              case 6: // input to eval graph does not seem to get here!\r
889 //                if(!EvalGraphIsUp()) continue;\r
890 //                h = evalGraphDialog; break;\r
891                 case 5:\r
892                   if(!appData.icsActive) continue;\r
893                   SAY("display");\r
894                   h = hText; break;\r
895                 case 6:\r
896                   if(!appData.icsActive) continue;\r
897                   SAY("input");\r
898                   h = hInput; break;\r
899             }\r
900           } while(h == 0);\r
901 \r
902           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
903           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
904           SetFocus(h);\r
905 \r
906           return; // this message now has been processed\r
907         }\r
908       }\r
909 \r
910       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
911           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
912           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
913           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
914           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
915           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
916           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
917           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
918           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
919           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
920         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
921         for(i=0; i<MAX_CHAT; i++) \r
922             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
923                 done = 1; break;\r
924         }\r
925         if(done) return; // [HGM] chat: end patch\r
926         TranslateMessage(&msg); /* Translates virtual key codes */\r
927         DispatchMessage(&msg);  /* Dispatches message to window */\r
928       }\r
929 }\r
930 \r
931 void\r
932 DoEvents ()\r
933 { /* Dispatch pending messages */\r
934   MSG msg;\r
935   while (PeekMessage(&msg, /* message structure */\r
936                      NULL, /* handle of window receiving the message */\r
937                      0,    /* lowest message to examine */\r
938                      0,    /* highest message to examine */\r
939                      PM_REMOVE))\r
940     {\r
941         HandleMessage(msg);\r
942     }\r
943 }\r
944 \r
945 /*---------------------------------------------------------------------------*\\r
946  *\r
947  * Initialization functions\r
948  *\r
949 \*---------------------------------------------------------------------------*/\r
950 \r
951 void\r
952 SetUserLogo()\r
953 {   // update user logo if necessary\r
954     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
955 \r
956     if(appData.autoLogo) {\r
957           curName = UserName();\r
958           if(strcmp(curName, oldUserName)) {\r
959                 GetCurrentDirectory(MSG_SIZ, dir);\r
960                 SetCurrentDirectory(installDir);\r
961                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
962                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
963                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
964                 if(userLogo == NULL)\r
965                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
966                 SetCurrentDirectory(dir); /* return to prev directory */\r
967           }\r
968     }\r
969 }\r
970 \r
971 BOOL\r
972 InitApplication(HINSTANCE hInstance)\r
973 {\r
974   WNDCLASS wc;\r
975 \r
976   /* Fill in window class structure with parameters that describe the */\r
977   /* main window. */\r
978 \r
979   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
980   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
981   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
982   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
983   wc.hInstance     = hInstance;         /* Owner of this class */\r
984   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
985   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
986   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
987   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
988   wc.lpszClassName = szAppName;                 /* Name to register as */\r
989 \r
990   /* Register the window class and return success/failure code. */\r
991   if (!RegisterClass(&wc)) return FALSE;\r
992 \r
993   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
994   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
995   wc.cbClsExtra    = 0;\r
996   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
997   wc.hInstance     = hInstance;\r
998   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
999   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
1000   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
1001   wc.lpszMenuName  = NULL;\r
1002   wc.lpszClassName = szConsoleName;\r
1003 \r
1004   if (!RegisterClass(&wc)) return FALSE;\r
1005   return TRUE;\r
1006 }\r
1007 \r
1008 \r
1009 /* Set by InitInstance, used by EnsureOnScreen */\r
1010 int screenHeight, screenWidth;\r
1011 RECT screenGeometry;\r
1012 \r
1013 void\r
1014 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
1015 {\r
1016 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
1017   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
1018   if (*x > screenGeometry.right - 32) *x = screenGeometry.left;\r
1019   if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;\r
1020   if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;\r
1021   if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;\r
1022 }\r
1023 \r
1024 VOID\r
1025 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
1026 {\r
1027   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1028   GetCurrentDirectory(MSG_SIZ, dir);\r
1029   SetCurrentDirectory(installDir);\r
1030   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1031       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1032 \r
1033       if (cps->programLogo == NULL && appData.debugMode) {\r
1034           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1035       }\r
1036   } else if(appData.autoLogo) {\r
1037       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1038         char *opponent = "";\r
1039         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1040         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1041         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1042         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1043             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1044             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1045         }\r
1046       } else\r
1047       if(appData.directory[n] && appData.directory[n][0]) {\r
1048         SetCurrentDirectory(appData.directory[n]);\r
1049         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1050       }\r
1051   }\r
1052   SetCurrentDirectory(dir); /* return to prev directory */\r
1053 }\r
1054 \r
1055 VOID\r
1056 InitTextures()\r
1057 {\r
1058   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1059   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1060   \r
1061   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1062       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1063       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1064       liteBackTextureMode = appData.liteBackTextureMode;\r
1065 \r
1066       if (liteBackTexture == NULL && appData.debugMode) {\r
1067           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1068       }\r
1069   }\r
1070   \r
1071   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1072       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1073       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1074       darkBackTextureMode = appData.darkBackTextureMode;\r
1075 \r
1076       if (darkBackTexture == NULL && appData.debugMode) {\r
1077           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1078       }\r
1079   }\r
1080 }\r
1081 \r
1082 #ifndef SM_CXVIRTUALSCREEN\r
1083 #define SM_CXVIRTUALSCREEN 78\r
1084 #endif\r
1085 #ifndef SM_CYVIRTUALSCREEN\r
1086 #define SM_CYVIRTUALSCREEN 79\r
1087 #endif\r
1088 #ifndef SM_XVIRTUALSCREEN \r
1089 #define SM_XVIRTUALSCREEN 76\r
1090 #endif\r
1091 #ifndef SM_YVIRTUALSCREEN \r
1092 #define SM_YVIRTUALSCREEN 77\r
1093 #endif\r
1094 \r
1095 VOID\r
1096 InitGeometry()\r
1097 {\r
1098   screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);\r
1099   if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1100   screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);\r
1101   if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1102   screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);\r
1103   screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);\r
1104   screenGeometry.right = screenGeometry.left + screenWidth;\r
1105   screenGeometry.bottom = screenGeometry.top + screenHeight;\r
1106 }\r
1107 \r
1108 BOOL\r
1109 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1110 {\r
1111   HWND hwnd; /* Main window handle. */\r
1112   int ibs;\r
1113   WINDOWPLACEMENT wp;\r
1114   char *filepart;\r
1115 \r
1116   hInst = hInstance;    /* Store instance handle in our global variable */\r
1117   programName = szAppName;\r
1118 \r
1119   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1120     *filepart = NULLCHAR;\r
1121     SetCurrentDirectory(installDir);\r
1122   } else {\r
1123     GetCurrentDirectory(MSG_SIZ, installDir);\r
1124   }\r
1125   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1126   InitGeometry();\r
1127   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1128   /* xboard, and older WinBoards, controlled the move sound with the\r
1129      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1130      always turn the option on (so that the backend will call us),\r
1131      then let the user turn the sound off by setting it to silence if\r
1132      desired.  To accommodate old winboard.ini files saved by old\r
1133      versions of WinBoard, we also turn off the sound if the option\r
1134      was initially set to false. [HGM] taken out of InitAppData */\r
1135   if (!appData.ringBellAfterMoves) {\r
1136     sounds[(int)SoundMove].name = strdup("");\r
1137     appData.ringBellAfterMoves = TRUE;\r
1138   }\r
1139   if (appData.debugMode) {\r
1140     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1141     setbuf(debugFP, NULL);\r
1142   }\r
1143 \r
1144   LoadLanguageFile(appData.language);\r
1145 \r
1146   InitBackEnd1();\r
1147 \r
1148 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1149 //  InitEngineUCI( installDir, &second );\r
1150 \r
1151   /* Create a main window for this application instance. */\r
1152   hwnd = CreateWindow(szAppName, szTitle,\r
1153                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1154                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1155                       NULL, NULL, hInstance, NULL);\r
1156   hwndMain = hwnd;\r
1157 \r
1158   /* If window could not be created, return "failure" */\r
1159   if (!hwnd) {\r
1160     return (FALSE);\r
1161   }\r
1162 \r
1163   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1164   LoadLogo(&first, 0, FALSE);\r
1165   LoadLogo(&second, 1, appData.icsActive);\r
1166 \r
1167   SetUserLogo();\r
1168 \r
1169   iconWhite = LoadIcon(hInstance, "icon_white");\r
1170   iconBlack = LoadIcon(hInstance, "icon_black");\r
1171   iconCurrent = iconWhite;\r
1172   InitDrawingColors();\r
1173 \r
1174   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1175   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1176     /* Compute window size for each board size, and use the largest\r
1177        size that fits on this screen as the default. */\r
1178     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1179     if (boardSize == (BoardSize)-1 &&\r
1180         winH <= screenHeight\r
1181            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1182         && winW <= screenWidth) {\r
1183       boardSize = (BoardSize)ibs;\r
1184     }\r
1185   }\r
1186 \r
1187   InitDrawingSizes(boardSize, 0);\r
1188   RecentEngineMenu(appData.recentEngineList);\r
1189   InitMenuChecks();\r
1190   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1191 \r
1192   /* [AS] Load textures if specified */\r
1193   InitTextures();\r
1194 \r
1195   mysrandom( (unsigned) time(NULL) );\r
1196 \r
1197   /* [AS] Restore layout */\r
1198   if( wpMoveHistory.visible ) {\r
1199       MoveHistoryPopUp();\r
1200   }\r
1201 \r
1202   if( wpEvalGraph.visible ) {\r
1203       EvalGraphPopUp();\r
1204   }\r
1205 \r
1206   if( wpEngineOutput.visible ) {\r
1207       EngineOutputPopUp();\r
1208   }\r
1209 \r
1210   /* Make the window visible; update its client area; and return "success" */\r
1211   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1212   wp.length = sizeof(WINDOWPLACEMENT);\r
1213   wp.flags = 0;\r
1214   wp.showCmd = nCmdShow;\r
1215   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1216   wp.rcNormalPosition.left = wpMain.x;\r
1217   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1218   wp.rcNormalPosition.top = wpMain.y;\r
1219   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1220   SetWindowPlacement(hwndMain, &wp);\r
1221 \r
1222   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1223 \r
1224   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1225                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1226 \r
1227   if (hwndConsole) {\r
1228 #if AOT_CONSOLE\r
1229     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1230                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1231 #endif\r
1232     ShowWindow(hwndConsole, nCmdShow);\r
1233     SetActiveWindow(hwndConsole);\r
1234   }\r
1235   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1236   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1237 \r
1238   return TRUE;\r
1239 \r
1240 }\r
1241 \r
1242 VOID\r
1243 InitMenuChecks()\r
1244 {\r
1245   HMENU hmenu = GetMenu(hwndMain);\r
1246 \r
1247   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1248                         MF_BYCOMMAND|((appData.icsActive &&\r
1249                                        *appData.icsCommPort != NULLCHAR) ?\r
1250                                       MF_ENABLED : MF_GRAYED));\r
1251   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1252                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1253                                      MF_CHECKED : MF_UNCHECKED));\r
1254   EnableMenuItem(hmenu, IDM_SaveSelected, MF_GRAYED);\r
1255 }\r
1256 \r
1257 //---------------------------------------------------------------------------------------------------------\r
1258 \r
1259 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1260 #define XBOARD FALSE\r
1261 \r
1262 #define OPTCHAR "/"\r
1263 #define SEPCHAR "="\r
1264 #define TOPLEVEL 0\r
1265 \r
1266 #include "args.h"\r
1267 \r
1268 // front-end part of option handling\r
1269 \r
1270 VOID\r
1271 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1272 {\r
1273   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1274   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1275   DeleteDC(hdc);\r
1276   lf->lfWidth = 0;\r
1277   lf->lfEscapement = 0;\r
1278   lf->lfOrientation = 0;\r
1279   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1280   lf->lfItalic = mfp->italic;\r
1281   lf->lfUnderline = mfp->underline;\r
1282   lf->lfStrikeOut = mfp->strikeout;\r
1283   lf->lfCharSet = mfp->charset;\r
1284   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1285   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1286   lf->lfQuality = DEFAULT_QUALITY;\r
1287   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1288     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1289 }\r
1290 \r
1291 void\r
1292 CreateFontInMF(MyFont *mf)\r
1293\r
1294   LFfromMFP(&mf->lf, &mf->mfp);\r
1295   if (mf->hf) DeleteObject(mf->hf);\r
1296   mf->hf = CreateFontIndirect(&mf->lf);\r
1297 }\r
1298 \r
1299 // [HGM] This platform-dependent table provides the location for storing the color info\r
1300 void *\r
1301 colorVariable[] = {\r
1302   &whitePieceColor, \r
1303   &blackPieceColor, \r
1304   &lightSquareColor,\r
1305   &darkSquareColor, \r
1306   &highlightSquareColor,\r
1307   &premoveHighlightColor,\r
1308   NULL,\r
1309   &consoleBackgroundColor,\r
1310   &appData.fontForeColorWhite,\r
1311   &appData.fontBackColorWhite,\r
1312   &appData.fontForeColorBlack,\r
1313   &appData.fontBackColorBlack,\r
1314   &appData.evalHistColorWhite,\r
1315   &appData.evalHistColorBlack,\r
1316   &appData.highlightArrowColor,\r
1317 };\r
1318 \r
1319 /* Command line font name parser.  NULL name means do nothing.\r
1320    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1321    For backward compatibility, syntax without the colon is also\r
1322    accepted, but font names with digits in them won't work in that case.\r
1323 */\r
1324 VOID\r
1325 ParseFontName(char *name, MyFontParams *mfp)\r
1326 {\r
1327   char *p, *q;\r
1328   if (name == NULL) return;\r
1329   p = name;\r
1330   q = strchr(p, ':');\r
1331   if (q) {\r
1332     if (q - p >= sizeof(mfp->faceName))\r
1333       ExitArgError(_("Font name too long:"), name, TRUE);\r
1334     memcpy(mfp->faceName, p, q - p);\r
1335     mfp->faceName[q - p] = NULLCHAR;\r
1336     p = q + 1;\r
1337   } else {\r
1338     q = mfp->faceName;\r
1339 \r
1340     while (*p && !isdigit(*p)) {\r
1341       *q++ = *p++;\r
1342       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1343         ExitArgError(_("Font name too long:"), name, TRUE);\r
1344     }\r
1345     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1346     *q = NULLCHAR;\r
1347   }\r
1348   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1349   mfp->pointSize = (float) atof(p);\r
1350   mfp->bold = (strchr(p, 'b') != NULL);\r
1351   mfp->italic = (strchr(p, 'i') != NULL);\r
1352   mfp->underline = (strchr(p, 'u') != NULL);\r
1353   mfp->strikeout = (strchr(p, 's') != NULL);\r
1354   mfp->charset = DEFAULT_CHARSET;\r
1355   q = strchr(p, 'c');\r
1356   if (q)\r
1357     mfp->charset = (BYTE) atoi(q+1);\r
1358 }\r
1359 \r
1360 void\r
1361 ParseFont(char *name, int number)\r
1362 { // wrapper to shield back-end from 'font'\r
1363   ParseFontName(name, &font[boardSize][number]->mfp);\r
1364 }\r
1365 \r
1366 void\r
1367 SetFontDefaults()\r
1368 { // in WB  we have a 2D array of fonts; this initializes their description\r
1369   int i, j;\r
1370   /* Point font array elements to structures and\r
1371      parse default font names */\r
1372   for (i=0; i<NUM_FONTS; i++) {\r
1373     for (j=0; j<NUM_SIZES; j++) {\r
1374       font[j][i] = &fontRec[j][i];\r
1375       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1376     }\r
1377   }\r
1378 }\r
1379 \r
1380 void\r
1381 CreateFonts()\r
1382 { // here we create the actual fonts from the selected descriptions\r
1383   int i, j;\r
1384   for (i=0; i<NUM_FONTS; i++) {\r
1385     for (j=0; j<NUM_SIZES; j++) {\r
1386       CreateFontInMF(font[j][i]);\r
1387     }\r
1388   }\r
1389 }\r
1390 /* Color name parser.\r
1391    X version accepts X color names, but this one\r
1392    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1393 COLORREF\r
1394 ParseColorName(char *name)\r
1395 {\r
1396   int red, green, blue, count;\r
1397   char buf[MSG_SIZ];\r
1398 \r
1399   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1400   if (count != 3) {\r
1401     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1402       &red, &green, &blue);\r
1403   }\r
1404   if (count != 3) {\r
1405     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1406     DisplayError(buf, 0);\r
1407     return RGB(0, 0, 0);\r
1408   }\r
1409   return PALETTERGB(red, green, blue);\r
1410 }\r
1411 \r
1412 void\r
1413 ParseColor(int n, char *name)\r
1414 { // for WinBoard the color is an int, which needs to be derived from the string\r
1415   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1416 }\r
1417 \r
1418 void\r
1419 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1420 {\r
1421   char *e = argValue;\r
1422   int eff = 0;\r
1423 \r
1424   while (*e) {\r
1425     if (*e == 'b')      eff |= CFE_BOLD;\r
1426     else if (*e == 'i') eff |= CFE_ITALIC;\r
1427     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1428     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1429     else if (*e == '#' || isdigit(*e)) break;\r
1430     e++;\r
1431   }\r
1432   *effects = eff;\r
1433   *color   = ParseColorName(e);\r
1434 }\r
1435 \r
1436 void\r
1437 ParseTextAttribs(ColorClass cc, char *s)\r
1438 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1439     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1440     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1441 }\r
1442 \r
1443 void\r
1444 ParseBoardSize(void *addr, char *name)\r
1445 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1446   BoardSize bs = SizeTiny;\r
1447   while (sizeInfo[bs].name != NULL) {\r
1448     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1449         *(BoardSize *)addr = bs;\r
1450         return;\r
1451     }\r
1452     bs++;\r
1453   }\r
1454   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1455 }\r
1456 \r
1457 void\r
1458 LoadAllSounds()\r
1459 { // [HGM] import name from appData first\r
1460   ColorClass cc;\r
1461   SoundClass sc;\r
1462   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1463     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1464     textAttribs[cc].sound.data = NULL;\r
1465     MyLoadSound(&textAttribs[cc].sound);\r
1466   }\r
1467   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1468     textAttribs[cc].sound.name = strdup("");\r
1469     textAttribs[cc].sound.data = NULL;\r
1470   }\r
1471   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1472     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1473     sounds[sc].data = NULL;\r
1474     MyLoadSound(&sounds[sc]);\r
1475   }\r
1476 }\r
1477 \r
1478 void\r
1479 SetCommPortDefaults()\r
1480 {\r
1481    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1482   dcb.DCBlength = sizeof(DCB);\r
1483   dcb.BaudRate = 9600;\r
1484   dcb.fBinary = TRUE;\r
1485   dcb.fParity = FALSE;\r
1486   dcb.fOutxCtsFlow = FALSE;\r
1487   dcb.fOutxDsrFlow = FALSE;\r
1488   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1489   dcb.fDsrSensitivity = FALSE;\r
1490   dcb.fTXContinueOnXoff = TRUE;\r
1491   dcb.fOutX = FALSE;\r
1492   dcb.fInX = FALSE;\r
1493   dcb.fNull = FALSE;\r
1494   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1495   dcb.fAbortOnError = FALSE;\r
1496   dcb.ByteSize = 7;\r
1497   dcb.Parity = SPACEPARITY;\r
1498   dcb.StopBits = ONESTOPBIT;\r
1499 }\r
1500 \r
1501 // [HGM] args: these three cases taken out to stay in front-end\r
1502 void\r
1503 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1504 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1505         // while the curent board size determines the element. This system should be ported to XBoard.\r
1506         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1507         int bs;\r
1508         for (bs=0; bs<NUM_SIZES; bs++) {\r
1509           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1510           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1511           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1512             ad->argName, mfp->faceName, mfp->pointSize,\r
1513             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1514             mfp->bold ? "b" : "",\r
1515             mfp->italic ? "i" : "",\r
1516             mfp->underline ? "u" : "",\r
1517             mfp->strikeout ? "s" : "",\r
1518             (int)mfp->charset);\r
1519         }\r
1520       }\r
1521 \r
1522 void\r
1523 ExportSounds()\r
1524 { // [HGM] copy the names from the internal WB variables to appData\r
1525   ColorClass cc;\r
1526   SoundClass sc;\r
1527   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1528     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1529   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1530     (&appData.soundMove)[sc] = sounds[sc].name;\r
1531 }\r
1532 \r
1533 void\r
1534 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1535 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1536         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1537         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1538           (ta->effects & CFE_BOLD) ? "b" : "",\r
1539           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1540           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1541           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1542           (ta->effects) ? " " : "",\r
1543           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1544       }\r
1545 \r
1546 void\r
1547 SaveColor(FILE *f, ArgDescriptor *ad)\r
1548 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1549         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1550         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1551           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1552 }\r
1553 \r
1554 void\r
1555 SaveBoardSize(FILE *f, char *name, void *addr)\r
1556 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1557   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1558 }\r
1559 \r
1560 void\r
1561 ParseCommPortSettings(char *s)\r
1562 { // wrapper to keep dcb from back-end\r
1563   ParseCommSettings(s, &dcb);\r
1564 }\r
1565 \r
1566 void\r
1567 GetWindowCoords()\r
1568 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1569   GetActualPlacement(hwndMain, &wpMain);\r
1570   GetActualPlacement(hwndConsole, &wpConsole);\r
1571   GetActualPlacement(commentDialog, &wpComment);\r
1572   GetActualPlacement(editTagsDialog, &wpTags);\r
1573   GetActualPlacement(gameListDialog, &wpGameList);\r
1574   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1575   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1576   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1577 }\r
1578 \r
1579 void\r
1580 PrintCommPortSettings(FILE *f, char *name)\r
1581 { // wrapper to shield back-end from DCB\r
1582       PrintCommSettings(f, name, &dcb);\r
1583 }\r
1584 \r
1585 int\r
1586 MySearchPath(char *installDir, char *name, char *fullname)\r
1587 {\r
1588   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1589   if(name[0]== '%') {\r
1590     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1591     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1592       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1593       *strchr(buf, '%') = 0;\r
1594       strcat(fullname, getenv(buf));\r
1595       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1596     }\r
1597     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1598     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1599     return (int) strlen(fullname);\r
1600   }\r
1601   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1602 }\r
1603 \r
1604 int\r
1605 MyGetFullPathName(char *name, char *fullname)\r
1606 {\r
1607   char *dummy;\r
1608   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1609 }\r
1610 \r
1611 int\r
1612 MainWindowUp()\r
1613 { // [HGM] args: allows testing if main window is realized from back-end\r
1614   return hwndMain != NULL;\r
1615 }\r
1616 \r
1617 void\r
1618 PopUpStartupDialog()\r
1619 {\r
1620     FARPROC lpProc;\r
1621     \r
1622     LoadLanguageFile(appData.language);\r
1623     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1624     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1625     FreeProcInstance(lpProc);\r
1626 }\r
1627 \r
1628 /*---------------------------------------------------------------------------*\\r
1629  *\r
1630  * GDI board drawing routines\r
1631  *\r
1632 \*---------------------------------------------------------------------------*/\r
1633 \r
1634 /* [AS] Draw square using background texture */\r
1635 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1636 {\r
1637     XFORM   x;\r
1638 \r
1639     if( mode == 0 ) {\r
1640         return; /* Should never happen! */\r
1641     }\r
1642 \r
1643     SetGraphicsMode( dst, GM_ADVANCED );\r
1644 \r
1645     switch( mode ) {\r
1646     case 1:\r
1647         /* Identity */\r
1648         break;\r
1649     case 2:\r
1650         /* X reflection */\r
1651         x.eM11 = -1.0;\r
1652         x.eM12 = 0;\r
1653         x.eM21 = 0;\r
1654         x.eM22 = 1.0;\r
1655         x.eDx = (FLOAT) dw + dx - 1;\r
1656         x.eDy = 0;\r
1657         dx = 0;\r
1658         SetWorldTransform( dst, &x );\r
1659         break;\r
1660     case 3:\r
1661         /* Y reflection */\r
1662         x.eM11 = 1.0;\r
1663         x.eM12 = 0;\r
1664         x.eM21 = 0;\r
1665         x.eM22 = -1.0;\r
1666         x.eDx = 0;\r
1667         x.eDy = (FLOAT) dh + dy - 1;\r
1668         dy = 0;\r
1669         SetWorldTransform( dst, &x );\r
1670         break;\r
1671     case 4:\r
1672         /* X/Y flip */\r
1673         x.eM11 = 0;\r
1674         x.eM12 = 1.0;\r
1675         x.eM21 = 1.0;\r
1676         x.eM22 = 0;\r
1677         x.eDx = (FLOAT) dx;\r
1678         x.eDy = (FLOAT) dy;\r
1679         dx = 0;\r
1680         dy = 0;\r
1681         SetWorldTransform( dst, &x );\r
1682         break;\r
1683     }\r
1684 \r
1685     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1686 \r
1687     x.eM11 = 1.0;\r
1688     x.eM12 = 0;\r
1689     x.eM21 = 0;\r
1690     x.eM22 = 1.0;\r
1691     x.eDx = 0;\r
1692     x.eDy = 0;\r
1693     SetWorldTransform( dst, &x );\r
1694 \r
1695     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1696 }\r
1697 \r
1698 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1699 enum {\r
1700     PM_WP = (int) WhitePawn, \r
1701     PM_WN = (int) WhiteKnight, \r
1702     PM_WB = (int) WhiteBishop, \r
1703     PM_WR = (int) WhiteRook, \r
1704     PM_WQ = (int) WhiteQueen, \r
1705     PM_WF = (int) WhiteFerz, \r
1706     PM_WW = (int) WhiteWazir, \r
1707     PM_WE = (int) WhiteAlfil, \r
1708     PM_WM = (int) WhiteMan, \r
1709     PM_WO = (int) WhiteCannon, \r
1710     PM_WU = (int) WhiteUnicorn, \r
1711     PM_WH = (int) WhiteNightrider, \r
1712     PM_WA = (int) WhiteAngel, \r
1713     PM_WC = (int) WhiteMarshall, \r
1714     PM_WAB = (int) WhiteCardinal, \r
1715     PM_WD = (int) WhiteDragon, \r
1716     PM_WL = (int) WhiteLance, \r
1717     PM_WS = (int) WhiteCobra, \r
1718     PM_WV = (int) WhiteFalcon, \r
1719     PM_WSG = (int) WhiteSilver, \r
1720     PM_WG = (int) WhiteGrasshopper, \r
1721     PM_WK = (int) WhiteKing,\r
1722     PM_BP = (int) BlackPawn, \r
1723     PM_BN = (int) BlackKnight, \r
1724     PM_BB = (int) BlackBishop, \r
1725     PM_BR = (int) BlackRook, \r
1726     PM_BQ = (int) BlackQueen, \r
1727     PM_BF = (int) BlackFerz, \r
1728     PM_BW = (int) BlackWazir, \r
1729     PM_BE = (int) BlackAlfil, \r
1730     PM_BM = (int) BlackMan,\r
1731     PM_BO = (int) BlackCannon, \r
1732     PM_BU = (int) BlackUnicorn, \r
1733     PM_BH = (int) BlackNightrider, \r
1734     PM_BA = (int) BlackAngel, \r
1735     PM_BC = (int) BlackMarshall, \r
1736     PM_BG = (int) BlackGrasshopper, \r
1737     PM_BAB = (int) BlackCardinal,\r
1738     PM_BD = (int) BlackDragon,\r
1739     PM_BL = (int) BlackLance,\r
1740     PM_BS = (int) BlackCobra,\r
1741     PM_BV = (int) BlackFalcon,\r
1742     PM_BSG = (int) BlackSilver,\r
1743     PM_BK = (int) BlackKing\r
1744 };\r
1745 \r
1746 static HFONT hPieceFont = NULL;\r
1747 static HBITMAP hPieceMask[(int) EmptySquare];\r
1748 static HBITMAP hPieceFace[(int) EmptySquare];\r
1749 static int fontBitmapSquareSize = 0;\r
1750 static char pieceToFontChar[(int) EmptySquare] =\r
1751                               { 'p', 'n', 'b', 'r', 'q', \r
1752                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1753                       'k', 'o', 'm', 'v', 't', 'w', \r
1754                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1755                                                               'l' };\r
1756 \r
1757 extern BOOL SetCharTable( char *table, const char * map );\r
1758 /* [HGM] moved to backend.c */\r
1759 \r
1760 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1761 {\r
1762     HBRUSH hbrush;\r
1763     BYTE r1 = GetRValue( color );\r
1764     BYTE g1 = GetGValue( color );\r
1765     BYTE b1 = GetBValue( color );\r
1766     BYTE r2 = r1 / 2;\r
1767     BYTE g2 = g1 / 2;\r
1768     BYTE b2 = b1 / 2;\r
1769     RECT rc;\r
1770 \r
1771     /* Create a uniform background first */\r
1772     hbrush = CreateSolidBrush( color );\r
1773     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1774     FillRect( hdc, &rc, hbrush );\r
1775     DeleteObject( hbrush );\r
1776     \r
1777     if( mode == 1 ) {\r
1778         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1779         int steps = squareSize / 2;\r
1780         int i;\r
1781 \r
1782         for( i=0; i<steps; i++ ) {\r
1783             BYTE r = r1 - (r1-r2) * i / steps;\r
1784             BYTE g = g1 - (g1-g2) * i / steps;\r
1785             BYTE b = b1 - (b1-b2) * i / steps;\r
1786 \r
1787             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1788             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1789             FillRect( hdc, &rc, hbrush );\r
1790             DeleteObject(hbrush);\r
1791         }\r
1792     }\r
1793     else if( mode == 2 ) {\r
1794         /* Diagonal gradient, good more or less for every piece */\r
1795         POINT triangle[3];\r
1796         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1797         HBRUSH hbrush_old;\r
1798         int steps = squareSize;\r
1799         int i;\r
1800 \r
1801         triangle[0].x = squareSize - steps;\r
1802         triangle[0].y = squareSize;\r
1803         triangle[1].x = squareSize;\r
1804         triangle[1].y = squareSize;\r
1805         triangle[2].x = squareSize;\r
1806         triangle[2].y = squareSize - steps;\r
1807 \r
1808         for( i=0; i<steps; i++ ) {\r
1809             BYTE r = r1 - (r1-r2) * i / steps;\r
1810             BYTE g = g1 - (g1-g2) * i / steps;\r
1811             BYTE b = b1 - (b1-b2) * i / steps;\r
1812 \r
1813             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1814             hbrush_old = SelectObject( hdc, hbrush );\r
1815             Polygon( hdc, triangle, 3 );\r
1816             SelectObject( hdc, hbrush_old );\r
1817             DeleteObject(hbrush);\r
1818             triangle[0].x++;\r
1819             triangle[2].y++;\r
1820         }\r
1821 \r
1822         SelectObject( hdc, hpen );\r
1823     }\r
1824 }\r
1825 \r
1826 /*\r
1827     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1828     seems to work ok. The main problem here is to find the "inside" of a chess\r
1829     piece: follow the steps as explained below.\r
1830 */\r
1831 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1832 {\r
1833     HBITMAP hbm;\r
1834     HBITMAP hbm_old;\r
1835     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1836     RECT rc;\r
1837     SIZE sz;\r
1838 \r
1839 \r
1840     POINT pt;\r
1841     int backColor = whitePieceColor; \r
1842     int foreColor = blackPieceColor;\r
1843     \r
1844     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1845         backColor = appData.fontBackColorWhite;\r
1846         foreColor = appData.fontForeColorWhite;\r
1847     }\r
1848     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1849         backColor = appData.fontBackColorBlack;\r
1850         foreColor = appData.fontForeColorBlack;\r
1851     }\r
1852 \r
1853     /* Mask */\r
1854     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1855 \r
1856     hbm_old = SelectObject( hdc, hbm );\r
1857 \r
1858     rc.left = 0;\r
1859     rc.top = 0;\r
1860     rc.right = squareSize;\r
1861     rc.bottom = squareSize;\r
1862 \r
1863     /* Step 1: background is now black */\r
1864     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1865 \r
1866     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1867 \r
1868     pt.x = (squareSize - sz.cx) / 2;\r
1869     pt.y = (squareSize - sz.cy) / 2;\r
1870 \r
1871     SetBkMode( hdc, TRANSPARENT );\r
1872     SetTextColor( hdc, chroma );\r
1873     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1874     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1875 \r
1876     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1877     /* Step 3: the area outside the piece is filled with white */\r
1878 //    FloodFill( hdc, 0, 0, chroma );\r
1879     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1880     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1881     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1882     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1883     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1884     /* \r
1885         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1886         but if the start point is not inside the piece we're lost!\r
1887         There should be a better way to do this... if we could create a region or path\r
1888         from the fill operation we would be fine for example.\r
1889     */\r
1890 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1891     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1892 \r
1893     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1894         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1895         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1896 \r
1897         SelectObject( dc2, bm2 );\r
1898         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1899         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1900         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1901         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1902         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1903 \r
1904         DeleteDC( dc2 );\r
1905         DeleteObject( bm2 );\r
1906     }\r
1907 \r
1908     SetTextColor( hdc, 0 );\r
1909     /* \r
1910         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1911         draw the piece again in black for safety.\r
1912     */\r
1913     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1914 \r
1915     SelectObject( hdc, hbm_old );\r
1916 \r
1917     if( hPieceMask[index] != NULL ) {\r
1918         DeleteObject( hPieceMask[index] );\r
1919     }\r
1920 \r
1921     hPieceMask[index] = hbm;\r
1922 \r
1923     /* Face */\r
1924     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1925 \r
1926     SelectObject( hdc, hbm );\r
1927 \r
1928     {\r
1929         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1930         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1931         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1932 \r
1933         SelectObject( dc1, hPieceMask[index] );\r
1934         SelectObject( dc2, bm2 );\r
1935         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1936         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1937         \r
1938         /* \r
1939             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1940             the piece background and deletes (makes transparent) the rest.\r
1941             Thanks to that mask, we are free to paint the background with the greates\r
1942             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1943             We use this, to make gradients and give the pieces a "roundish" look.\r
1944         */\r
1945         SetPieceBackground( hdc, backColor, 2 );\r
1946         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1947 \r
1948         DeleteDC( dc2 );\r
1949         DeleteDC( dc1 );\r
1950         DeleteObject( bm2 );\r
1951     }\r
1952 \r
1953     SetTextColor( hdc, foreColor );\r
1954     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1955 \r
1956     SelectObject( hdc, hbm_old );\r
1957 \r
1958     if( hPieceFace[index] != NULL ) {\r
1959         DeleteObject( hPieceFace[index] );\r
1960     }\r
1961 \r
1962     hPieceFace[index] = hbm;\r
1963 }\r
1964 \r
1965 static int TranslatePieceToFontPiece( int piece )\r
1966 {\r
1967     switch( piece ) {\r
1968     case BlackPawn:\r
1969         return PM_BP;\r
1970     case BlackKnight:\r
1971         return PM_BN;\r
1972     case BlackBishop:\r
1973         return PM_BB;\r
1974     case BlackRook:\r
1975         return PM_BR;\r
1976     case BlackQueen:\r
1977         return PM_BQ;\r
1978     case BlackKing:\r
1979         return PM_BK;\r
1980     case WhitePawn:\r
1981         return PM_WP;\r
1982     case WhiteKnight:\r
1983         return PM_WN;\r
1984     case WhiteBishop:\r
1985         return PM_WB;\r
1986     case WhiteRook:\r
1987         return PM_WR;\r
1988     case WhiteQueen:\r
1989         return PM_WQ;\r
1990     case WhiteKing:\r
1991         return PM_WK;\r
1992 \r
1993     case BlackAngel:\r
1994         return PM_BA;\r
1995     case BlackMarshall:\r
1996         return PM_BC;\r
1997     case BlackFerz:\r
1998         return PM_BF;\r
1999     case BlackNightrider:\r
2000         return PM_BH;\r
2001     case BlackAlfil:\r
2002         return PM_BE;\r
2003     case BlackWazir:\r
2004         return PM_BW;\r
2005     case BlackUnicorn:\r
2006         return PM_BU;\r
2007     case BlackCannon:\r
2008         return PM_BO;\r
2009     case BlackGrasshopper:\r
2010         return PM_BG;\r
2011     case BlackMan:\r
2012         return PM_BM;\r
2013     case BlackSilver:\r
2014         return PM_BSG;\r
2015     case BlackLance:\r
2016         return PM_BL;\r
2017     case BlackFalcon:\r
2018         return PM_BV;\r
2019     case BlackCobra:\r
2020         return PM_BS;\r
2021     case BlackCardinal:\r
2022         return PM_BAB;\r
2023     case BlackDragon:\r
2024         return PM_BD;\r
2025 \r
2026     case WhiteAngel:\r
2027         return PM_WA;\r
2028     case WhiteMarshall:\r
2029         return PM_WC;\r
2030     case WhiteFerz:\r
2031         return PM_WF;\r
2032     case WhiteNightrider:\r
2033         return PM_WH;\r
2034     case WhiteAlfil:\r
2035         return PM_WE;\r
2036     case WhiteWazir:\r
2037         return PM_WW;\r
2038     case WhiteUnicorn:\r
2039         return PM_WU;\r
2040     case WhiteCannon:\r
2041         return PM_WO;\r
2042     case WhiteGrasshopper:\r
2043         return PM_WG;\r
2044     case WhiteMan:\r
2045         return PM_WM;\r
2046     case WhiteSilver:\r
2047         return PM_WSG;\r
2048     case WhiteLance:\r
2049         return PM_WL;\r
2050     case WhiteFalcon:\r
2051         return PM_WV;\r
2052     case WhiteCobra:\r
2053         return PM_WS;\r
2054     case WhiteCardinal:\r
2055         return PM_WAB;\r
2056     case WhiteDragon:\r
2057         return PM_WD;\r
2058     }\r
2059 \r
2060     return 0;\r
2061 }\r
2062 \r
2063 void CreatePiecesFromFont()\r
2064 {\r
2065     LOGFONT lf;\r
2066     HDC hdc_window = NULL;\r
2067     HDC hdc = NULL;\r
2068     HFONT hfont_old;\r
2069     int fontHeight;\r
2070     int i;\r
2071 \r
2072     if( fontBitmapSquareSize < 0 ) {\r
2073         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2074         return;\r
2075     }\r
2076 \r
2077     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2078             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2079         fontBitmapSquareSize = -1;\r
2080         return;\r
2081     }\r
2082 \r
2083     if( fontBitmapSquareSize != squareSize ) {\r
2084         hdc_window = GetDC( hwndMain );\r
2085         hdc = CreateCompatibleDC( hdc_window );\r
2086 \r
2087         if( hPieceFont != NULL ) {\r
2088             DeleteObject( hPieceFont );\r
2089         }\r
2090         else {\r
2091             for( i=0; i<=(int)BlackKing; i++ ) {\r
2092                 hPieceMask[i] = NULL;\r
2093                 hPieceFace[i] = NULL;\r
2094             }\r
2095         }\r
2096 \r
2097         fontHeight = 75;\r
2098 \r
2099         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2100             fontHeight = appData.fontPieceSize;\r
2101         }\r
2102 \r
2103         fontHeight = (fontHeight * squareSize) / 100;\r
2104 \r
2105         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2106         lf.lfWidth = 0;\r
2107         lf.lfEscapement = 0;\r
2108         lf.lfOrientation = 0;\r
2109         lf.lfWeight = FW_NORMAL;\r
2110         lf.lfItalic = 0;\r
2111         lf.lfUnderline = 0;\r
2112         lf.lfStrikeOut = 0;\r
2113         lf.lfCharSet = DEFAULT_CHARSET;\r
2114         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2115         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2116         lf.lfQuality = PROOF_QUALITY;\r
2117         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2118         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2119         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2120 \r
2121         hPieceFont = CreateFontIndirect( &lf );\r
2122 \r
2123         if( hPieceFont == NULL ) {\r
2124             fontBitmapSquareSize = -2;\r
2125         }\r
2126         else {\r
2127             /* Setup font-to-piece character table */\r
2128             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2129                 /* No (or wrong) global settings, try to detect the font */\r
2130                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2131                     /* Alpha */\r
2132                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2133                 }\r
2134                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2135                     /* DiagramTT* family */\r
2136                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2137                 }\r
2138                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2139                     /* Fairy symbols */\r
2140                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2141                 }\r
2142                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2143                     /* Good Companion (Some characters get warped as literal :-( */\r
2144                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2145                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2146                     SetCharTable(pieceToFontChar, s);\r
2147                 }\r
2148                 else {\r
2149                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2150                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2151                 }\r
2152             }\r
2153 \r
2154             /* Create bitmaps */\r
2155             hfont_old = SelectObject( hdc, hPieceFont );\r
2156             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2157                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2158                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2159 \r
2160             SelectObject( hdc, hfont_old );\r
2161 \r
2162             fontBitmapSquareSize = squareSize;\r
2163         }\r
2164     }\r
2165 \r
2166     if( hdc != NULL ) {\r
2167         DeleteDC( hdc );\r
2168     }\r
2169 \r
2170     if( hdc_window != NULL ) {\r
2171         ReleaseDC( hwndMain, hdc_window );\r
2172     }\r
2173 }\r
2174 \r
2175 HBITMAP\r
2176 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2177 {\r
2178   char name[128], buf[MSG_SIZ];\r
2179 \r
2180     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2181   if(appData.pieceDirectory[0]) {\r
2182     HBITMAP res;\r
2183     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2184     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2185     if(res) return res;\r
2186   }\r
2187   if (gameInfo.event &&\r
2188       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2189       strcmp(name, "k80s") == 0) {\r
2190     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2191   }\r
2192   return LoadBitmap(hinst, name);\r
2193 }\r
2194 \r
2195 \r
2196 /* Insert a color into the program's logical palette\r
2197    structure.  This code assumes the given color is\r
2198    the result of the RGB or PALETTERGB macro, and it\r
2199    knows how those macros work (which is documented).\r
2200 */\r
2201 VOID\r
2202 InsertInPalette(COLORREF color)\r
2203 {\r
2204   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2205 \r
2206   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2207     DisplayFatalError(_("Too many colors"), 0, 1);\r
2208     pLogPal->palNumEntries--;\r
2209     return;\r
2210   }\r
2211 \r
2212   pe->peFlags = (char) 0;\r
2213   pe->peRed = (char) (0xFF & color);\r
2214   pe->peGreen = (char) (0xFF & (color >> 8));\r
2215   pe->peBlue = (char) (0xFF & (color >> 16));\r
2216   return;\r
2217 }\r
2218 \r
2219 \r
2220 VOID\r
2221 InitDrawingColors()\r
2222 {\r
2223   int i;\r
2224   if (pLogPal == NULL) {\r
2225     /* Allocate enough memory for a logical palette with\r
2226      * PALETTESIZE entries and set the size and version fields\r
2227      * of the logical palette structure.\r
2228      */\r
2229     pLogPal = (NPLOGPALETTE)\r
2230       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2231                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2232     pLogPal->palVersion    = 0x300;\r
2233   }\r
2234   pLogPal->palNumEntries = 0;\r
2235 \r
2236   InsertInPalette(lightSquareColor);\r
2237   InsertInPalette(darkSquareColor);\r
2238   InsertInPalette(whitePieceColor);\r
2239   InsertInPalette(blackPieceColor);\r
2240   InsertInPalette(highlightSquareColor);\r
2241   InsertInPalette(premoveHighlightColor);\r
2242 \r
2243   /*  create a logical color palette according the information\r
2244    *  in the LOGPALETTE structure.\r
2245    */\r
2246   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2247 \r
2248   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2249   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2250   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2251   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2252   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2253   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2254   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2255     for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers\r
2256 \r
2257    /* [AS] Force rendering of the font-based pieces */\r
2258   if( fontBitmapSquareSize > 0 ) {\r
2259     fontBitmapSquareSize = 0;\r
2260   }\r
2261 }\r
2262 \r
2263 \r
2264 int\r
2265 BoardWidth(int boardSize, int n)\r
2266 { /* [HGM] argument n added to allow different width and height */\r
2267   int lineGap = sizeInfo[boardSize].lineGap;\r
2268 \r
2269   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2270       lineGap = appData.overrideLineGap;\r
2271   }\r
2272 \r
2273   return (n + 1) * lineGap +\r
2274           n * sizeInfo[boardSize].squareSize;\r
2275 }\r
2276 \r
2277 /* Respond to board resize by dragging edge */\r
2278 VOID\r
2279 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2280 {\r
2281   BoardSize newSize = NUM_SIZES - 1;\r
2282   static int recurse = 0;\r
2283   if (IsIconic(hwndMain)) return;\r
2284   if (recurse > 0) return;\r
2285   recurse++;\r
2286   while (newSize > 0) {\r
2287         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2288         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2289            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2290     newSize--;\r
2291   } \r
2292   boardSize = newSize;\r
2293   InitDrawingSizes(boardSize, flags);\r
2294   recurse--;\r
2295 }\r
2296 \r
2297 \r
2298 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2299 \r
2300 VOID\r
2301 InitDrawingSizes(BoardSize boardSize, int flags)\r
2302 {\r
2303   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2304   ChessSquare piece;\r
2305   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2306   HDC hdc;\r
2307   SIZE clockSize, messageSize;\r
2308   HFONT oldFont;\r
2309   char buf[MSG_SIZ];\r
2310   char *str;\r
2311   HMENU hmenu = GetMenu(hwndMain);\r
2312   RECT crect, wrect, oldRect;\r
2313   int offby;\r
2314   LOGBRUSH logbrush;\r
2315   VariantClass v = gameInfo.variant;\r
2316 \r
2317   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2318   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2319 \r
2320   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2321   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2322   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2323   oldBoardSize = boardSize;\r
2324 \r
2325   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2326   { // correct board size to one where built-in pieces exist\r
2327     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2328        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2329 \r
2330       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2331       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2332       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {\r
2333       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2334       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2335                                    boardSize = SizeMiddling;\r
2336     }\r
2337   }\r
2338   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2339 \r
2340   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2341   oldRect.top = wpMain.y;\r
2342   oldRect.right = wpMain.x + wpMain.width;\r
2343   oldRect.bottom = wpMain.y + wpMain.height;\r
2344 \r
2345   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2346   smallLayout = sizeInfo[boardSize].smallLayout;\r
2347   squareSize = sizeInfo[boardSize].squareSize;\r
2348   lineGap = sizeInfo[boardSize].lineGap;\r
2349   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2350   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2351 \r
2352   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2353       lineGap = appData.overrideLineGap;\r
2354   }\r
2355 \r
2356   if (tinyLayout != oldTinyLayout) {\r
2357     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2358     if (tinyLayout) {\r
2359       style &= ~WS_SYSMENU;\r
2360       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2361                  "&Minimize\tCtrl+F4");\r
2362     } else {\r
2363       style |= WS_SYSMENU;\r
2364       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2365     }\r
2366     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2367 \r
2368     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2369       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2370         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2371     }\r
2372     DrawMenuBar(hwndMain);\r
2373   }\r
2374 \r
2375   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2376   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2377 \r
2378   /* Get text area sizes */\r
2379   hdc = GetDC(hwndMain);\r
2380   if (appData.clockMode) {\r
2381     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2382   } else {\r
2383     snprintf(buf, MSG_SIZ, _("White"));\r
2384   }\r
2385   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2386   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2387   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2388   str = _("We only care about the height here");\r
2389   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2390   SelectObject(hdc, oldFont);\r
2391   ReleaseDC(hwndMain, hdc);\r
2392 \r
2393   /* Compute where everything goes */\r
2394   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2395         /* [HGM] logo: if either logo is on, reserve space for it */\r
2396         logoHeight =  2*clockSize.cy;\r
2397         leftLogoRect.left   = OUTER_MARGIN;\r
2398         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2399         leftLogoRect.top    = OUTER_MARGIN;\r
2400         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2401 \r
2402         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2403         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2404         rightLogoRect.top    = OUTER_MARGIN;\r
2405         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2406 \r
2407 \r
2408     whiteRect.left = leftLogoRect.right;\r
2409     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2410     whiteRect.top = OUTER_MARGIN;\r
2411     whiteRect.bottom = whiteRect.top + logoHeight;\r
2412 \r
2413     blackRect.right = rightLogoRect.left;\r
2414     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2415     blackRect.top = whiteRect.top;\r
2416     blackRect.bottom = whiteRect.bottom;\r
2417   } else {\r
2418     whiteRect.left = OUTER_MARGIN;\r
2419     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2420     whiteRect.top = OUTER_MARGIN;\r
2421     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2422 \r
2423     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2424     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2425     blackRect.top = whiteRect.top;\r
2426     blackRect.bottom = whiteRect.bottom;\r
2427 \r
2428     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2429   }\r
2430 \r
2431   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2432   if (appData.showButtonBar) {\r
2433     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2434       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2435   } else {\r
2436     messageRect.right = OUTER_MARGIN + boardWidth;\r
2437   }\r
2438   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2439   messageRect.bottom = messageRect.top + messageSize.cy;\r
2440 \r
2441   boardRect.left = OUTER_MARGIN;\r
2442   boardRect.right = boardRect.left + boardWidth;\r
2443   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2444   boardRect.bottom = boardRect.top + boardHeight;\r
2445 \r
2446   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2447   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2448   oldTinyLayout = tinyLayout;\r
2449   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2450   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2451     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2452   winW *= 1 + twoBoards;\r
2453   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2454   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2455   wpMain.height = winH; //       without disturbing window attachments\r
2456   GetWindowRect(hwndMain, &wrect);\r
2457   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2458                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2459 \r
2460   // [HGM] placement: let attached windows follow size change.\r
2461   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2462   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2463   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2464   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2465   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2466 \r
2467   /* compensate if menu bar wrapped */\r
2468   GetClientRect(hwndMain, &crect);\r
2469   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2470   wpMain.height += offby;\r
2471   switch (flags) {\r
2472   case WMSZ_TOPLEFT:\r
2473     SetWindowPos(hwndMain, NULL, \r
2474                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2475                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2476     break;\r
2477 \r
2478   case WMSZ_TOPRIGHT:\r
2479   case WMSZ_TOP:\r
2480     SetWindowPos(hwndMain, NULL, \r
2481                  wrect.left, wrect.bottom - wpMain.height, \r
2482                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2483     break;\r
2484 \r
2485   case WMSZ_BOTTOMLEFT:\r
2486   case WMSZ_LEFT:\r
2487     SetWindowPos(hwndMain, NULL, \r
2488                  wrect.right - wpMain.width, wrect.top, \r
2489                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2490     break;\r
2491 \r
2492   case WMSZ_BOTTOMRIGHT:\r
2493   case WMSZ_BOTTOM:\r
2494   case WMSZ_RIGHT:\r
2495   default:\r
2496     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2497                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2498     break;\r
2499   }\r
2500 \r
2501   hwndPause = NULL;\r
2502   for (i = 0; i < N_BUTTONS; i++) {\r
2503     if (buttonDesc[i].hwnd != NULL) {\r
2504       DestroyWindow(buttonDesc[i].hwnd);\r
2505       buttonDesc[i].hwnd = NULL;\r
2506     }\r
2507     if (appData.showButtonBar) {\r
2508       buttonDesc[i].hwnd =\r
2509         CreateWindow("BUTTON", buttonDesc[i].label,\r
2510                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2511                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2512                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2513                      (HMENU) buttonDesc[i].id,\r
2514                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2515       if (tinyLayout) {\r
2516         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2517                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2518                     MAKELPARAM(FALSE, 0));\r
2519       }\r
2520       if (buttonDesc[i].id == IDM_Pause)\r
2521         hwndPause = buttonDesc[i].hwnd;\r
2522       buttonDesc[i].wndproc = (WNDPROC)\r
2523         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2524     }\r
2525   }\r
2526   if (gridPen != NULL) DeleteObject(gridPen);\r
2527   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2528   if (premovePen != NULL) DeleteObject(premovePen);\r
2529   if (lineGap != 0) {\r
2530     logbrush.lbStyle = BS_SOLID;\r
2531     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2532     gridPen =\r
2533       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2534                    lineGap, &logbrush, 0, NULL);\r
2535     logbrush.lbColor = highlightSquareColor;\r
2536     highlightPen =\r
2537       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2538                    lineGap, &logbrush, 0, NULL);\r
2539 \r
2540     logbrush.lbColor = premoveHighlightColor; \r
2541     premovePen =\r
2542       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2543                    lineGap, &logbrush, 0, NULL);\r
2544 \r
2545     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2546     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2547       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2548       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2549         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2550       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2551         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2552       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2553     }\r
2554     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2555       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2556       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2557         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2558         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2559       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2560         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2561       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2562     }\r
2563   }\r
2564 \r
2565   /* [HGM] Licensing requirement */\r
2566 #ifdef GOTHIC\r
2567   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2568 #endif\r
2569 #ifdef FALCON\r
2570   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2571 #endif\r
2572   GothicPopUp( "", VariantNormal);\r
2573 \r
2574 \r
2575 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2576 \r
2577   /* Load piece bitmaps for this board size */\r
2578   for (i=0; i<=2; i++) {\r
2579     for (piece = WhitePawn;\r
2580          (int) piece < (int) BlackPawn;\r
2581          piece = (ChessSquare) ((int) piece + 1)) {\r
2582       if (pieceBitmap[i][piece] != NULL)\r
2583         DeleteObject(pieceBitmap[i][piece]);\r
2584     }\r
2585   }\r
2586 \r
2587   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2588   // Orthodox Chess pieces\r
2589   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2590   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2591   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2592   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2593   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2594   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2595   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2596   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2597   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2598   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2599   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2600   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2601   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2602   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2603   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2604   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2605     // in Shogi, Hijack the unused Queen for Lance\r
2606     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2607     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2608     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2609   } else {\r
2610     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2611     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2612     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2613   }\r
2614 \r
2615   if(squareSize <= 72 && squareSize >= 33) { \r
2616     /* A & C are available in most sizes now */\r
2617     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2618       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2619       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2620       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2621       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2622       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2623       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2624       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2625       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2626       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2627       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2628       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2629       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2630     } else { // Smirf-like\r
2631       if(gameInfo.variant == VariantSChess) {\r
2632         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2633         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2634         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2635       } else {\r
2636         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2637         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2638         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2639       }\r
2640     }\r
2641     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2642       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2643       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2644       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2645     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2646       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2647       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2648       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2649     } else { // WinBoard standard\r
2650       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2651       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2652       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2653     }\r
2654   }\r
2655 \r
2656 \r
2657   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2658     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2659     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2660     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2661     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2662     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2663     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2664     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2665     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2666     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2667     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2668     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2669     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2670     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2671     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2672     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2673     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2674     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2675     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2676     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2677     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2678     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2679     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2680     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2681     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2682     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2683     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2684     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2685     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2686     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2687     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2688     pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2689     pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2690     pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2691 \r
2692     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2693       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2694       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2695       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2696       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2697       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2698       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2699       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2700       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2701       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2702       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2703       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2704       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2705     } else {\r
2706       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2707       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2708       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2709       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2710       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2711       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2712       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2713       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2714       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2715       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2716       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2717       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2718     }\r
2719 \r
2720   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2721     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2722     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2723     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2724     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2725     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2726     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2727     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2728     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2729     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2730     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2731     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2732     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2733     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2734     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2735   }\r
2736 \r
2737 \r
2738   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2739   /* special Shogi support in this size */\r
2740   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2741       for (piece = WhitePawn;\r
2742            (int) piece < (int) BlackPawn;\r
2743            piece = (ChessSquare) ((int) piece + 1)) {\r
2744         if (pieceBitmap[i][piece] != NULL)\r
2745           DeleteObject(pieceBitmap[i][piece]);\r
2746       }\r
2747     }\r
2748   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2749   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2750   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2751   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2752   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2753   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2754   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2755   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2756   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2757   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2758   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2759   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2760   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2761   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2762   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2763   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2764   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2765   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2766   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2767   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2768   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2769   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2770   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2771   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2772   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2773   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2774   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2775   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2776   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2777   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2778   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2779   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2780   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2781   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2782   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2783   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2784   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2785   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2786   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2787   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2788   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2789   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2790   minorSize = 0;\r
2791   }\r
2792 }\r
2793 \r
2794 HBITMAP\r
2795 PieceBitmap(ChessSquare p, int kind)\r
2796 {\r
2797   if ((int) p >= (int) BlackPawn)\r
2798     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2799 \r
2800   return pieceBitmap[kind][(int) p];\r
2801 }\r
2802 \r
2803 /***************************************************************/\r
2804 \r
2805 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2806 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2807 /*\r
2808 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2809 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2810 */\r
2811 \r
2812 VOID\r
2813 SquareToPos(int row, int column, int * x, int * y)\r
2814 {\r
2815   if (flipView) {\r
2816     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2817     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2818   } else {\r
2819     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2820     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2821   }\r
2822 }\r
2823 \r
2824 VOID\r
2825 DrawCoordsOnDC(HDC hdc)\r
2826 {\r
2827   static char files[] = "0123456789012345678901221098765432109876543210";\r
2828   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2829   char str[2] = { NULLCHAR, NULLCHAR };\r
2830   int oldMode, oldAlign, x, y, start, i;\r
2831   HFONT oldFont;\r
2832   HBRUSH oldBrush;\r
2833 \r
2834   if (!appData.showCoords)\r
2835     return;\r
2836 \r
2837   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2838 \r
2839   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2840   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2841   oldAlign = GetTextAlign(hdc);\r
2842   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2843 \r
2844   y = boardRect.top + lineGap;\r
2845   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2846 \r
2847   if(border) {\r
2848     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2849     x += border - lineGap - 4; y += squareSize - 6;\r
2850   } else\r
2851   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2852   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2853     str[0] = files[start + i];\r
2854     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2855     y += squareSize + lineGap;\r
2856   }\r
2857 \r
2858   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2859 \r
2860   if(border) {\r
2861     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2862     x += -border + 4; y += border - squareSize + 6;\r
2863   } else\r
2864   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2865   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2866     str[0] = ranks[start + i];\r
2867     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2868     x += squareSize + lineGap;\r
2869   }    \r
2870 \r
2871   SelectObject(hdc, oldBrush);\r
2872   SetBkMode(hdc, oldMode);\r
2873   SetTextAlign(hdc, oldAlign);\r
2874   SelectObject(hdc, oldFont);\r
2875 }\r
2876 \r
2877 VOID\r
2878 DrawGridOnDC(HDC hdc)\r
2879 {\r
2880   HPEN oldPen;\r
2881  \r
2882   if (lineGap != 0) {\r
2883     oldPen = SelectObject(hdc, gridPen);\r
2884     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2885     SelectObject(hdc, oldPen);\r
2886   }\r
2887 }\r
2888 \r
2889 #define HIGHLIGHT_PEN 0\r
2890 #define PREMOVE_PEN   1\r
2891 \r
2892 VOID\r
2893 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2894 {\r
2895   int x1, y1;\r
2896   HPEN oldPen, hPen;\r
2897   if (lineGap == 0) return;\r
2898   if (flipView) {\r
2899     x1 = boardRect.left +\r
2900       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2901     y1 = boardRect.top +\r
2902       lineGap/2 + y * (squareSize + lineGap) + border;\r
2903   } else {\r
2904     x1 = boardRect.left +\r
2905       lineGap/2 + x * (squareSize + lineGap) + border;\r
2906     y1 = boardRect.top +\r
2907       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2908   }\r
2909   hPen = pen ? premovePen : highlightPen;\r
2910   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2911   MoveToEx(hdc, x1, y1, NULL);\r
2912   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2913   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2914   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2915   LineTo(hdc, x1, y1);\r
2916   SelectObject(hdc, oldPen);\r
2917 }\r
2918 \r
2919 VOID\r
2920 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2921 {\r
2922   int i;\r
2923   for (i=0; i<2; i++) {\r
2924     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2925       DrawHighlightOnDC(hdc, TRUE,\r
2926                         h->sq[i].x, h->sq[i].y,\r
2927                         pen);\r
2928   }\r
2929 }\r
2930 \r
2931 /* Note: sqcolor is used only in monoMode */\r
2932 /* Note that this code is largely duplicated in woptions.c,\r
2933    function DrawSampleSquare, so that needs to be updated too */\r
2934 VOID\r
2935 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2936 {\r
2937   HBITMAP oldBitmap;\r
2938   HBRUSH oldBrush;\r
2939   int tmpSize;\r
2940 \r
2941   if (appData.blindfold) return;\r
2942 \r
2943   /* [AS] Use font-based pieces if needed */\r
2944   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2945     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2946     CreatePiecesFromFont();\r
2947 \r
2948     if( fontBitmapSquareSize == squareSize ) {\r
2949         int index = TranslatePieceToFontPiece(piece);\r
2950 \r
2951         SelectObject( tmphdc, hPieceMask[ index ] );\r
2952 \r
2953       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2954         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2955       else\r
2956         BitBlt( hdc,\r
2957             x, y,\r
2958             squareSize, squareSize,\r
2959             tmphdc,\r
2960             0, 0,\r
2961             SRCAND );\r
2962 \r
2963         SelectObject( tmphdc, hPieceFace[ index ] );\r
2964 \r
2965       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2966         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2967       else\r
2968         BitBlt( hdc,\r
2969             x, y,\r
2970             squareSize, squareSize,\r
2971             tmphdc,\r
2972             0, 0,\r
2973             SRCPAINT );\r
2974 \r
2975         return;\r
2976     }\r
2977   }\r
2978 \r
2979   if (appData.monoMode) {\r
2980     SelectObject(tmphdc, PieceBitmap(piece, \r
2981       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2982     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2983            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2984   } else {\r
2985     HBRUSH xBrush = whitePieceBrush;\r
2986     tmpSize = squareSize;\r
2987     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2988     if(minorSize &&\r
2989         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2990          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2991       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2992       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2993       x += (squareSize - minorSize)>>1;\r
2994       y += squareSize - minorSize - 2;\r
2995       tmpSize = minorSize;\r
2996     }\r
2997     if (color || appData.allWhite ) {\r
2998       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2999       if( color )\r
3000               oldBrush = SelectObject(hdc, xBrush);\r
3001       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3002       if(appData.upsideDown && color==flipView)\r
3003         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3004       else\r
3005         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3006       /* Use black for outline of white pieces */\r
3007       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3008       if(appData.upsideDown && color==flipView)\r
3009         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3010       else\r
3011         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3012     } else if(appData.pieceDirectory[0]) {\r
3013       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3014       oldBrush = SelectObject(hdc, xBrush);\r
3015       if(appData.upsideDown && color==flipView)\r
3016         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3017       else\r
3018         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3019       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3020       if(appData.upsideDown && color==flipView)\r
3021         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3022       else\r
3023         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3024     } else {\r
3025       /* Use square color for details of black pieces */\r
3026       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3027       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3028       if(appData.upsideDown && !flipView)\r
3029         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3030       else\r
3031         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3032     }\r
3033     SelectObject(hdc, oldBrush);\r
3034     SelectObject(tmphdc, oldBitmap);\r
3035   }\r
3036 }\r
3037 \r
3038 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3039 int GetBackTextureMode( int algo )\r
3040 {\r
3041     int result = BACK_TEXTURE_MODE_DISABLED;\r
3042 \r
3043     switch( algo ) \r
3044     {\r
3045         case BACK_TEXTURE_MODE_PLAIN:\r
3046             result = 1; /* Always use identity map */\r
3047             break;\r
3048         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3049             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3050             break;\r
3051     }\r
3052 \r
3053     return result;\r
3054 }\r
3055 \r
3056 /* \r
3057     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3058     to handle redraws cleanly (as random numbers would always be different).\r
3059 */\r
3060 VOID RebuildTextureSquareInfo()\r
3061 {\r
3062     BITMAP bi;\r
3063     int lite_w = 0;\r
3064     int lite_h = 0;\r
3065     int dark_w = 0;\r
3066     int dark_h = 0;\r
3067     int row;\r
3068     int col;\r
3069 \r
3070     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3071 \r
3072     if( liteBackTexture != NULL ) {\r
3073         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3074             lite_w = bi.bmWidth;\r
3075             lite_h = bi.bmHeight;\r
3076         }\r
3077     }\r
3078 \r
3079     if( darkBackTexture != NULL ) {\r
3080         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3081             dark_w = bi.bmWidth;\r
3082             dark_h = bi.bmHeight;\r
3083         }\r
3084     }\r
3085 \r
3086     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3087         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3088             if( (col + row) & 1 ) {\r
3089                 /* Lite square */\r
3090                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3091                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3092                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3093                   else\r
3094                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3095                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3096                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3097                   else\r
3098                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3099                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3100                 }\r
3101             }\r
3102             else {\r
3103                 /* Dark square */\r
3104                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3105                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3106                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3107                   else\r
3108                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3109                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3110                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3111                   else\r
3112                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3113                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3114                 }\r
3115             }\r
3116         }\r
3117     }\r
3118 }\r
3119 \r
3120 /* [AS] Arrow highlighting support */\r
3121 \r
3122 static double A_WIDTH = 5; /* Width of arrow body */\r
3123 \r
3124 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3125 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3126 \r
3127 static double Sqr( double x )\r
3128 {\r
3129     return x*x;\r
3130 }\r
3131 \r
3132 static int Round( double x )\r
3133 {\r
3134     return (int) (x + 0.5);\r
3135 }\r
3136 \r
3137 /* Draw an arrow between two points using current settings */\r
3138 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3139 {\r
3140     POINT arrow[7];\r
3141     double dx, dy, j, k, x, y;\r
3142 \r
3143     if( d_x == s_x ) {\r
3144         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3145 \r
3146         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3147         arrow[0].y = s_y;\r
3148 \r
3149         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3150         arrow[1].y = d_y - h;\r
3151 \r
3152         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3153         arrow[2].y = d_y - h;\r
3154 \r
3155         arrow[3].x = d_x;\r
3156         arrow[3].y = d_y;\r
3157 \r
3158         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3159         arrow[5].y = d_y - h;\r
3160 \r
3161         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3162         arrow[4].y = d_y - h;\r
3163 \r
3164         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3165         arrow[6].y = s_y;\r
3166     }\r
3167     else if( d_y == s_y ) {\r
3168         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3169 \r
3170         arrow[0].x = s_x;\r
3171         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3172 \r
3173         arrow[1].x = d_x - w;\r
3174         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3175 \r
3176         arrow[2].x = d_x - w;\r
3177         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3178 \r
3179         arrow[3].x = d_x;\r
3180         arrow[3].y = d_y;\r
3181 \r
3182         arrow[5].x = d_x - w;\r
3183         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3184 \r
3185         arrow[4].x = d_x - w;\r
3186         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3187 \r
3188         arrow[6].x = s_x;\r
3189         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r