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