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