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