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