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