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