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