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