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