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