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