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