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
3680                 blackLogo = second.programLogo; // ICS logo\r
3681                 break;\r
3682             case IcsPlayingBlack:\r
3683                 whiteLogo = second.programLogo; // ICS logo\r
3684                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3685                 break;\r
3686             case TwoMachinesPlay:\r
3687                 if(first.twoMachinesColor[0] == 'b') {\r
3688                     whiteLogo = second.programLogo;\r
3689                     blackLogo = first.programLogo;\r
3690                 }\r
3691                 break;\r
3692             case MachinePlaysWhite:\r
3693                 blackLogo = userLogo;\r
3694                 break;\r
3695             case MachinePlaysBlack:\r
3696                 whiteLogo = userLogo;\r
3697                 blackLogo = first.programLogo;\r
3698           }\r
3699         }\r
3700         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3701         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3702   }\r
3703 \r
3704   if( appData.highlightMoveWithArrow ) {\r
3705     DrawArrowHighlight(hdcmem);\r
3706   }\r
3707 \r
3708   DrawCoordsOnDC(hdcmem);\r
3709 \r
3710   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3711                  /* to make sure lastDrawn contains what is actually drawn */\r
3712 \r
3713   /* Put the dragged piece back into place and draw it (out of place!) */\r
3714     if (dragged_piece != EmptySquare) {\r
3715     /* [HGM] or restack */\r
3716     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3717                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3718     else\r
3719     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3720                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3721     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3722     x = dragInfo.pos.x - squareSize / 2;\r
3723     y = dragInfo.pos.y - squareSize / 2;\r
3724     DrawPieceOnDC(hdcmem, dragged_piece,\r
3725                   ((int) dragged_piece < (int) BlackPawn), \r
3726                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3727   }   \r
3728   \r
3729   /* Put the animated piece back into place and draw it */\r
3730   if (animInfo.piece != EmptySquare) {\r
3731     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3732     x = boardRect.left + animInfo.pos.x;\r
3733     y = boardRect.top + animInfo.pos.y;\r
3734     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3735                   ((int) animInfo.piece < (int) BlackPawn),\r
3736                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3737   }\r
3738 \r
3739   /* Release the bufferBitmap by selecting in the old bitmap \r
3740    * and delete the memory DC\r
3741    */\r
3742   SelectObject(hdcmem, oldBitmap);\r
3743   DeleteDC(hdcmem);\r
3744 \r
3745   /* Set clipping on the target DC */\r
3746   if (!fullrepaint) {\r
3747     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3748         RECT rect;\r
3749         GetRgnBox(clips[x], &rect);\r
3750         DeleteObject(clips[x]);\r
3751         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3752                           rect.right + wpMain.width/2, rect.bottom);\r
3753     }\r
3754     SelectClipRgn(hdc, clips[0]);\r
3755     for (x = 1; x < num_clips; x++) {\r
3756       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3757         abort();   // this should never ever happen!\r
3758     } \r
3759   }\r
3760 \r
3761   /* Copy the new bitmap onto the screen in one go.\r
3762    * This way we avoid any flickering\r
3763    */\r
3764   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3765   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3766          boardRect.right - boardRect.left,\r
3767          boardRect.bottom - boardRect.top,\r
3768          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3769   if(saveDiagFlag) { \r
3770     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
3771     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3772 \r
3773     GetObject(bufferBitmap, sizeof(b), &b);\r
3774     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
3775         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3776         bih.biWidth = b.bmWidth;\r
3777         bih.biHeight = b.bmHeight;\r
3778         bih.biPlanes = 1;\r
3779         bih.biBitCount = b.bmBitsPixel;\r
3780         bih.biCompression = 0;\r
3781         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3782         bih.biXPelsPerMeter = 0;\r
3783         bih.biYPelsPerMeter = 0;\r
3784         bih.biClrUsed = 0;\r
3785         bih.biClrImportant = 0;\r
3786 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3787 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3788         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3789 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3790 \r
3791         wb = b.bmWidthBytes;\r
3792         // count colors\r
3793         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3794                 int k = ((int*) pData)[i];\r
3795                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3796                 if(j >= 16) break;\r
3797                 color[j] = k;\r
3798                 if(j >= nrColors) nrColors = j+1;\r
3799         }\r
3800         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3801                 INT p = 0;\r
3802                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3803                     for(w=0; w<(wb>>2); w+=2) {\r
3804                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3805                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3806                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3807                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3808                         pData[p++] = m | j<<4;\r
3809                     }\r
3810                     while(p&3) pData[p++] = 0;\r
3811                 }\r
3812                 fac = 3;\r
3813                 wb = ((wb+31)>>5)<<2;\r
3814         }\r
3815         // write BITMAPFILEHEADER\r
3816         fprintf(diagFile, "BM");\r
3817         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3818         fputDW(diagFile, 0);\r
3819         fputDW(diagFile, 0x36 + (fac?64:0));\r
3820         // write BITMAPINFOHEADER\r
3821         fputDW(diagFile, 40);\r
3822         fputDW(diagFile, b.bmWidth);\r
3823         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3824         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3825         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3826         fputDW(diagFile, 0);\r
3827         fputDW(diagFile, 0);\r
3828         fputDW(diagFile, 0);\r
3829         fputDW(diagFile, 0);\r
3830         fputDW(diagFile, 0);\r
3831         fputDW(diagFile, 0);\r
3832         // write color table\r
3833         if(fac)\r
3834         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3835         // write bitmap data\r
3836         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3837                 fputc(pData[i], diagFile);\r
3838      }\r
3839   }\r
3840 \r
3841   SelectObject(tmphdc, oldBitmap);\r
3842 \r
3843   /* Massive cleanup */\r
3844   for (x = 0; x < num_clips; x++)\r
3845     DeleteObject(clips[x]);\r
3846 \r
3847   DeleteDC(tmphdc);\r
3848   DeleteObject(bufferBitmap);\r
3849 \r
3850   if (releaseDC) \r
3851     ReleaseDC(hwndMain, hdc);\r
3852   \r
3853   if (lastDrawnFlipView != flipView && nr == 0) {\r
3854     if (flipView)\r
3855       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3856     else\r
3857       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3858   }\r
3859 \r
3860 /*  CopyBoard(lastDrawn, board);*/\r
3861   lastDrawnHighlight = highlightInfo;\r
3862   lastDrawnPremove   = premoveHighlightInfo;\r
3863   lastDrawnFlipView = flipView;\r
3864   lastDrawnValid[nr] = 1;\r
3865 }\r
3866 \r
3867 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3868 int\r
3869 SaveDiagram(f)\r
3870      FILE *f;\r
3871 {\r
3872     saveDiagFlag = 1; diagFile = f;\r
3873     HDCDrawPosition(NULL, TRUE, NULL);\r
3874 \r
3875     saveDiagFlag = 0;\r
3876 \r
3877 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
3878     \r
3879     fclose(f);\r
3880     return TRUE;\r
3881 }\r
3882 \r
3883 \r
3884 /*---------------------------------------------------------------------------*\\r
3885 | CLIENT PAINT PROCEDURE\r
3886 |   This is the main event-handler for the WM_PAINT message.\r
3887 |\r
3888 \*---------------------------------------------------------------------------*/\r
3889 VOID\r
3890 PaintProc(HWND hwnd)\r
3891 {\r
3892   HDC         hdc;\r
3893   PAINTSTRUCT ps;\r
3894   HFONT       oldFont;\r
3895 \r
3896   if((hdc = BeginPaint(hwnd, &ps))) {\r
3897     if (IsIconic(hwnd)) {\r
3898       DrawIcon(hdc, 2, 2, iconCurrent);\r
3899     } else {\r
3900       if (!appData.monoMode) {\r
3901         SelectPalette(hdc, hPal, FALSE);\r
3902         RealizePalette(hdc);\r
3903       }\r
3904       HDCDrawPosition(hdc, 1, NULL);\r
3905       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3906         flipView = !flipView; partnerUp = !partnerUp;\r
3907         HDCDrawPosition(hdc, 1, NULL);\r
3908         flipView = !flipView; partnerUp = !partnerUp;\r
3909       }\r
3910       oldFont =\r
3911         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3912       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3913                  ETO_CLIPPED|ETO_OPAQUE,\r
3914                  &messageRect, messageText, strlen(messageText), NULL);\r
3915       SelectObject(hdc, oldFont);\r
3916       DisplayBothClocks();\r
3917     }\r
3918     EndPaint(hwnd,&ps);\r
3919   }\r
3920 \r
3921   return;\r
3922 }\r
3923 \r
3924 \r
3925 /*\r
3926  * If the user selects on a border boundary, return -1; if off the board,\r
3927  *   return -2.  Otherwise map the event coordinate to the square.\r
3928  * The offset boardRect.left or boardRect.top must already have been\r
3929  *   subtracted from x.\r
3930  */\r
3931 int EventToSquare(x, limit)\r
3932      int x, limit;\r
3933 {\r
3934   if (x <= 0)\r
3935     return -2;\r
3936   if (x < lineGap)\r
3937     return -1;\r
3938   x -= lineGap;\r
3939   if ((x % (squareSize + lineGap)) >= squareSize)\r
3940     return -1;\r
3941   x /= (squareSize + lineGap);\r
3942     if (x >= limit)\r
3943     return -2;\r
3944   return x;\r
3945 }\r
3946 \r
3947 typedef struct {\r
3948   char piece;\r
3949   int command;\r
3950   char* name;\r
3951 } DropEnable;\r
3952 \r
3953 DropEnable dropEnables[] = {\r
3954   { 'P', DP_Pawn, N_("Pawn") },\r
3955   { 'N', DP_Knight, N_("Knight") },\r
3956   { 'B', DP_Bishop, N_("Bishop") },\r
3957   { 'R', DP_Rook, N_("Rook") },\r
3958   { 'Q', DP_Queen, N_("Queen") },\r
3959 };\r
3960 \r
3961 VOID\r
3962 SetupDropMenu(HMENU hmenu)\r
3963 {\r
3964   int i, count, enable;\r
3965   char *p;\r
3966   extern char white_holding[], black_holding[];\r
3967   char item[MSG_SIZ];\r
3968 \r
3969   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
3970     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
3971                dropEnables[i].piece);\r
3972     count = 0;\r
3973     while (p && *p++ == dropEnables[i].piece) count++;\r
3974     sprintf(item, "%s  %d", T_(dropEnables[i].name), count);\r
3975     enable = count > 0 || !appData.testLegality\r
3976       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
3977                       && !appData.icsActive);\r
3978     ModifyMenu(hmenu, dropEnables[i].command,\r
3979                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
3980                dropEnables[i].command, item);\r
3981   }\r
3982 }\r
3983 \r
3984 void DragPieceBegin(int x, int y)\r
3985 {\r
3986       dragInfo.lastpos.x = boardRect.left + x;\r
3987       dragInfo.lastpos.y = boardRect.top + y;\r
3988       dragInfo.from.x = fromX;\r
3989       dragInfo.from.y = fromY;\r
3990       dragInfo.start = dragInfo.from;\r
3991       SetCapture(hwndMain);\r
3992 }\r
3993 \r
3994 void DragPieceEnd(int x, int y)\r
3995 {\r
3996     ReleaseCapture();\r
3997     dragInfo.start.x = dragInfo.start.y = -1;\r
3998     dragInfo.from = dragInfo.start;\r
3999     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4000 }\r
4001 \r
4002 /* Event handler for mouse messages */\r
4003 VOID\r
4004 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4005 {\r
4006   int x, y, menuNr;\r
4007   POINT pt;\r
4008   static int recursive = 0;\r
4009   HMENU hmenu;\r
4010   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4011 \r
4012   if (recursive) {\r
4013     if (message == WM_MBUTTONUP) {\r
4014       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4015          to the middle button: we simulate pressing the left button too!\r
4016          */\r
4017       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4018       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4019     }\r
4020     return;\r
4021   }\r
4022   recursive++;\r
4023   \r
4024   pt.x = LOWORD(lParam);\r
4025   pt.y = HIWORD(lParam);\r
4026   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4027   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4028   if (!flipView && y >= 0) {\r
4029     y = BOARD_HEIGHT - 1 - y;\r
4030   }\r
4031   if (flipView && x >= 0) {\r
4032     x = BOARD_WIDTH - 1 - x;\r
4033   }\r
4034 \r
4035   switch (message) {\r
4036   case WM_LBUTTONDOWN:\r
4037       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4038         if (gameMode == EditPosition) {\r
4039           SetWhiteToPlayEvent();\r
4040         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4041           AdjustClock(flipClock, -1);\r
4042         } else if (gameMode == IcsPlayingBlack ||\r
4043                    gameMode == MachinePlaysWhite) {\r
4044           CallFlagEvent();\r
4045         }\r
4046       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4047         if (gameMode == EditPosition) {\r
4048           SetBlackToPlayEvent();\r
4049         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4050           AdjustClock(!flipClock, -1);\r
4051         } else if (gameMode == IcsPlayingWhite ||\r
4052                    gameMode == MachinePlaysBlack) {\r
4053           CallFlagEvent();\r
4054         }\r
4055       }\r
4056       dragInfo.start.x = dragInfo.start.y = -1;\r
4057       dragInfo.from = dragInfo.start;\r
4058     if(fromX == -1 && frozen) { // not sure where this is for\r
4059                 fromX = fromY = -1; \r
4060       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4061       break;\r
4062     }\r
4063       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4064       DrawPosition(TRUE, NULL);\r
4065     break;\r
4066 \r
4067   case WM_LBUTTONUP:\r
4068       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4069       DrawPosition(TRUE, NULL);\r
4070     break;\r
4071 \r
4072   case WM_MOUSEMOVE:\r
4073     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4074     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4075     if ((appData.animateDragging || appData.highlightDragging)\r
4076         && (wParam & MK_LBUTTON)\r
4077         && dragInfo.from.x >= 0) \r
4078     {\r
4079       BOOL full_repaint = FALSE;\r
4080 \r
4081       if (appData.animateDragging) {\r
4082         dragInfo.pos = pt;\r
4083       }\r
4084       if (appData.highlightDragging) {\r
4085         SetHighlights(fromX, fromY, x, y);\r
4086         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4087             full_repaint = TRUE;\r
4088         }\r
4089       }\r
4090       \r
4091       DrawPosition( full_repaint, NULL);\r
4092       \r
4093       dragInfo.lastpos = dragInfo.pos;\r
4094     }\r
4095     break;\r
4096 \r
4097   case WM_MOUSEWHEEL: // [DM]\r
4098     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4099        /* Mouse Wheel is being rolled forward\r
4100         * Play moves forward\r
4101         */\r
4102        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4103                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4104        /* Mouse Wheel is being rolled backward\r
4105         * Play moves backward\r
4106         */\r
4107        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4108                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4109     }\r
4110     break;\r
4111 \r
4112   case WM_MBUTTONUP:\r
4113   case WM_RBUTTONUP:\r
4114     ReleaseCapture();\r
4115     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4116     break;\r
4117  \r
4118   case WM_MBUTTONDOWN:\r
4119   case WM_RBUTTONDOWN:\r
4120     ErrorPopDown();\r
4121     ReleaseCapture();\r
4122     fromX = fromY = -1;\r
4123     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4124     dragInfo.start.x = dragInfo.start.y = -1;\r
4125     dragInfo.from = dragInfo.start;\r
4126     dragInfo.lastpos = dragInfo.pos;\r
4127     if (appData.highlightDragging) {\r
4128       ClearHighlights();\r
4129     }\r
4130     if(y == -2) {\r
4131       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4132       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4133           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4134       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4135           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4136       }\r
4137       break;\r
4138     }\r
4139     DrawPosition(TRUE, NULL);\r
4140 \r
4141     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4142     switch (menuNr) {\r
4143     case 0:\r
4144       if (message == WM_MBUTTONDOWN) {\r
4145         buttonCount = 3;  /* even if system didn't think so */\r
4146         if (wParam & MK_SHIFT) \r
4147           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4148         else\r
4149           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4150       } else { /* message == WM_RBUTTONDOWN */\r
4151         /* Just have one menu, on the right button.  Windows users don't\r
4152            think to try the middle one, and sometimes other software steals\r
4153            it, or it doesn't really exist. */\r
4154         if(gameInfo.variant != VariantShogi)\r
4155             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4156         else\r
4157             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4158       }\r
4159       break;\r
4160     case 2:\r
4161       SetCapture(hwndMain);
4162       break;\r
4163     case 1:\r
4164       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4165       SetupDropMenu(hmenu);\r
4166       MenuPopup(hwnd, pt, hmenu, -1);\r
4167     default:\r
4168       break;\r
4169     }\r
4170     break;\r
4171   }\r
4172 \r
4173   recursive--;\r
4174 }\r
4175 \r
4176 /* Preprocess messages for buttons in main window */\r
4177 LRESULT CALLBACK\r
4178 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4179 {\r
4180   int id = GetWindowLong(hwnd, GWL_ID);\r
4181   int i, dir;\r
4182 \r
4183   for (i=0; i<N_BUTTONS; i++) {\r
4184     if (buttonDesc[i].id == id) break;\r
4185   }\r
4186   if (i == N_BUTTONS) return 0;\r
4187   switch (message) {\r
4188   case WM_KEYDOWN:\r
4189     switch (wParam) {\r
4190     case VK_LEFT:\r
4191     case VK_RIGHT:\r
4192       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4193       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4194       return TRUE;\r
4195     }\r
4196     break;\r
4197   case WM_CHAR:\r
4198     switch (wParam) {\r
4199     case '\r':\r
4200       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4201       return TRUE;\r
4202     default:\r
4203       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4204         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4205         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4206         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4207         SetFocus(h);\r
4208         SendMessage(h, WM_CHAR, wParam, lParam);\r
4209         return TRUE;\r
4210       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4211         PopUpMoveDialog((char)wParam);\r
4212       }\r
4213       break;\r
4214     }\r
4215     break;\r
4216   }\r
4217   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4218 }\r
4219 \r
4220 /* Process messages for Promotion dialog box */\r
4221 LRESULT CALLBACK\r
4222 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4223 {\r
4224   char promoChar;\r
4225 \r
4226   switch (message) {\r
4227   case WM_INITDIALOG: /* message: initialize dialog box */\r
4228     /* Center the dialog over the application window */\r
4229     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4230     Translate(hDlg, DLG_PromotionKing);\r
4231     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4232       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4233        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4234                SW_SHOW : SW_HIDE);\r
4235     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4236     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4237        ((PieceToChar(WhiteAngel) >= 'A' &&\r
4238          PieceToChar(WhiteAngel) != '~') ||\r
4239         (PieceToChar(BlackAngel) >= 'A' &&\r
4240          PieceToChar(BlackAngel) != '~')   ) ?\r
4241                SW_SHOW : SW_HIDE);\r
4242     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4243        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
4244          PieceToChar(WhiteMarshall) != '~') ||\r
4245         (PieceToChar(BlackMarshall) >= 'A' &&\r
4246          PieceToChar(BlackMarshall) != '~')   ) ?\r
4247                SW_SHOW : SW_HIDE);\r
4248     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4249     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4250        gameInfo.variant != VariantShogi ?\r
4251                SW_SHOW : SW_HIDE);\r
4252     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4253        gameInfo.variant != VariantShogi ?\r
4254                SW_SHOW : SW_HIDE);\r
4255     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
4256        gameInfo.variant == VariantShogi ?\r
4257                SW_SHOW : SW_HIDE);\r
4258     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
4259        gameInfo.variant == VariantShogi ?\r
4260                SW_SHOW : SW_HIDE);\r
4261     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4262        gameInfo.variant == VariantSuper ?\r
4263                SW_SHOW : SW_HIDE);\r
4264     return TRUE;\r
4265 \r
4266   case WM_COMMAND: /* message: received a command */\r
4267     switch (LOWORD(wParam)) {\r
4268     case IDCANCEL:\r
4269       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4270       ClearHighlights();\r
4271       DrawPosition(FALSE, NULL);\r
4272       return TRUE;\r
4273     case PB_King:\r
4274       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4275       break;\r
4276     case PB_Queen:\r
4277       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
4278       break;\r
4279     case PB_Rook:\r
4280       promoChar = PieceToChar(BlackRook);\r
4281       break;\r
4282     case PB_Bishop:\r
4283       promoChar = PieceToChar(BlackBishop);\r
4284       break;\r
4285     case PB_Chancellor:\r
4286       promoChar = PieceToChar(BlackMarshall);\r
4287       break;\r
4288     case PB_Archbishop:\r
4289       promoChar = PieceToChar(BlackAngel);\r
4290       break;\r
4291     case PB_Knight:\r
4292       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
4293       break;\r
4294     default:\r
4295       return FALSE;\r
4296     }\r
4297     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4298     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
4299        only show the popup when we are already sure the move is valid or\r
4300        legal. We pass a faulty move type, but the kludge is that FinishMove\r
4301        will figure out it is a promotion from the promoChar. */\r
4302     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4303     fromX = fromY = -1;\r
4304     if (!appData.highlightLastMove) {\r
4305       ClearHighlights();\r
4306       DrawPosition(FALSE, NULL);\r
4307     }\r
4308     return TRUE;\r
4309   }\r
4310   return FALSE;\r
4311 }\r
4312 \r
4313 /* Pop up promotion dialog */\r
4314 VOID\r
4315 PromotionPopup(HWND hwnd)\r
4316 {\r
4317   FARPROC lpProc;\r
4318 \r
4319   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4320   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4321     hwnd, (DLGPROC)lpProc);\r
4322   FreeProcInstance(lpProc);\r
4323 }\r
4324 \r
4325 void\r
4326 PromotionPopUp()\r
4327 {\r
4328   DrawPosition(TRUE, NULL);\r
4329   PromotionPopup(hwndMain);\r
4330 }\r
4331 \r
4332 /* Toggle ShowThinking */\r
4333 VOID\r
4334 ToggleShowThinking()\r
4335 {\r
4336   appData.showThinking = !appData.showThinking;\r
4337   ShowThinkingEvent();\r
4338 }\r
4339 \r
4340 VOID\r
4341 LoadGameDialog(HWND hwnd, char* title)\r
4342 {\r
4343   UINT number = 0;\r
4344   FILE *f;\r
4345   char fileTitle[MSG_SIZ];\r
4346   f = OpenFileDialog(hwnd, "rb", "",\r
4347                      appData.oldSaveStyle ? "gam" : "pgn",\r
4348                      GAME_FILT,\r
4349                      title, &number, fileTitle, NULL);\r
4350   if (f != NULL) {\r
4351     cmailMsgLoaded = FALSE;\r
4352     if (number == 0) {\r
4353       int error = GameListBuild(f);\r
4354       if (error) {\r
4355         DisplayError(_("Cannot build game list"), error);\r
4356       } else if (!ListEmpty(&gameList) &&\r
4357                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4358         GameListPopUp(f, fileTitle);\r
4359         return;\r
4360       }\r
4361       GameListDestroy();\r
4362       number = 1;\r
4363     }\r
4364     LoadGame(f, number, fileTitle, FALSE);\r
4365   }\r
4366 }\r
4367 \r
4368 int get_term_width()\r
4369 {\r
4370     HDC hdc;\r
4371     TEXTMETRIC tm;\r
4372     RECT rc;\r
4373     HFONT hfont, hold_font;\r
4374     LOGFONT lf;\r
4375     HWND hText;\r
4376 \r
4377     if (hwndConsole)\r
4378         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4379     else\r
4380         return 79;\r
4381 \r
4382     // get the text metrics\r
4383     hdc = GetDC(hText);\r
4384     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4385     if (consoleCF.dwEffects & CFE_BOLD)\r
4386         lf.lfWeight = FW_BOLD;\r
4387     if (consoleCF.dwEffects & CFE_ITALIC)\r
4388         lf.lfItalic = TRUE;\r
4389     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4390         lf.lfStrikeOut = TRUE;\r
4391     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4392         lf.lfUnderline = TRUE;\r
4393     hfont = CreateFontIndirect(&lf);\r
4394     hold_font = SelectObject(hdc, hfont);\r
4395     GetTextMetrics(hdc, &tm);\r
4396     SelectObject(hdc, hold_font);\r
4397     DeleteObject(hfont);\r
4398     ReleaseDC(hText, hdc);\r
4399 \r
4400     // get the rectangle\r
4401     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4402 \r
4403     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4404 }\r
4405 \r
4406 void UpdateICSWidth(HWND hText)\r
4407 {\r
4408     LONG old_width, new_width;\r
4409 \r
4410     new_width = get_term_width(hText, FALSE);\r
4411     old_width = GetWindowLong(hText, GWL_USERDATA);\r
4412     if (new_width != old_width)\r
4413     {\r
4414         ics_update_width(new_width);\r
4415         SetWindowLong(hText, GWL_USERDATA, new_width);\r
4416     }\r
4417 }\r
4418 \r
4419 VOID\r
4420 ChangedConsoleFont()\r
4421 {\r
4422   CHARFORMAT cfmt;\r
4423   CHARRANGE tmpsel, sel;\r
4424   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4425   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4426   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4427   PARAFORMAT paraf;\r
4428 \r
4429   cfmt.cbSize = sizeof(CHARFORMAT);\r
4430   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4431   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
4432   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4433    * size.  This was undocumented in the version of MSVC++ that I had\r
4434    * when I wrote the code, but is apparently documented now.\r
4435    */\r
4436   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4437   cfmt.bCharSet = f->lf.lfCharSet;\r
4438   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4439   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4440   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4441   /* Why are the following seemingly needed too? */\r
4442   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4443   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4444   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4445   tmpsel.cpMin = 0;\r
4446   tmpsel.cpMax = -1; /*999999?*/\r
4447   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4448   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4449   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4450    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4451    */\r
4452   paraf.cbSize = sizeof(paraf);\r
4453   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4454   paraf.dxStartIndent = 0;\r
4455   paraf.dxOffset = WRAP_INDENT;\r
4456   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4457   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4458   UpdateICSWidth(hText);\r
4459 }\r
4460 \r
4461 /*---------------------------------------------------------------------------*\\r
4462  *\r
4463  * Window Proc for main window\r
4464  *\r
4465 \*---------------------------------------------------------------------------*/\r
4466 \r
4467 /* Process messages for main window, etc. */\r
4468 LRESULT CALLBACK\r
4469 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4470 {\r
4471   FARPROC lpProc;\r
4472   int wmId, wmEvent;\r
4473   char *defName;\r
4474   FILE *f;\r
4475   UINT number;\r
4476   char fileTitle[MSG_SIZ];\r
4477   char buf[MSG_SIZ];\r
4478   static SnapData sd;\r
4479 \r
4480   switch (message) {\r
4481 \r
4482   case WM_PAINT: /* message: repaint portion of window */\r
4483     PaintProc(hwnd);\r
4484     break;\r
4485 \r
4486   case WM_ERASEBKGND:\r
4487     if (IsIconic(hwnd)) {\r
4488       /* Cheat; change the message */\r
4489       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4490     } else {\r
4491       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4492     }\r
4493     break;\r
4494 \r
4495   case WM_LBUTTONDOWN:\r
4496   case WM_MBUTTONDOWN:\r
4497   case WM_RBUTTONDOWN:\r
4498   case WM_LBUTTONUP:\r
4499   case WM_MBUTTONUP:\r
4500   case WM_RBUTTONUP:\r
4501   case WM_MOUSEMOVE:\r
4502   case WM_MOUSEWHEEL:\r
4503     MouseEvent(hwnd, message, wParam, lParam);\r
4504     break;\r
4505 \r
4506   JAWS_KB_NAVIGATION\r
4507 \r
4508   case WM_CHAR:\r
4509     \r
4510     JAWS_ALT_INTERCEPT\r
4511 \r
4512     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4513         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4514         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4515         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4516         SetFocus(h);\r
4517         SendMessage(h, message, wParam, lParam);\r
4518     } else if(lParam != KF_REPEAT) {\r
4519         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4520                 PopUpMoveDialog((char)wParam);\r
4521         } else if((char)wParam == 003) CopyGameToClipboard();\r
4522          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4523     }\r
4524 \r
4525     break;\r
4526 \r
4527   case WM_PALETTECHANGED:\r
4528     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4529       int nnew;\r
4530       HDC hdc = GetDC(hwndMain);\r
4531       SelectPalette(hdc, hPal, TRUE);\r
4532       nnew = RealizePalette(hdc);\r
4533       if (nnew > 0) {\r
4534         paletteChanged = TRUE;\r
4535         InvalidateRect(hwnd, &boardRect, FALSE);\r
4536       }\r
4537       ReleaseDC(hwnd, hdc);\r
4538     }\r
4539     break;\r
4540 \r
4541   case WM_QUERYNEWPALETTE:\r
4542     if (!appData.monoMode /*&& paletteChanged*/) {\r
4543       int nnew;\r
4544       HDC hdc = GetDC(hwndMain);\r
4545       paletteChanged = FALSE;\r
4546       SelectPalette(hdc, hPal, FALSE);\r
4547       nnew = RealizePalette(hdc);\r
4548       if (nnew > 0) {\r
4549         InvalidateRect(hwnd, &boardRect, FALSE);\r
4550       }\r
4551       ReleaseDC(hwnd, hdc);\r
4552       return TRUE;\r
4553     }\r
4554     return FALSE;\r
4555 \r
4556   case WM_COMMAND: /* message: command from application menu */\r
4557     wmId    = LOWORD(wParam);\r
4558     wmEvent = HIWORD(wParam);\r
4559 \r
4560     switch (wmId) {\r
4561     case IDM_NewGame:\r
4562       ResetGameEvent();\r
4563       SAY("new game enter a move to play against the computer with white");\r
4564       break;\r
4565 \r
4566     case IDM_NewGameFRC:\r
4567       if( NewGameFRC() == 0 ) {\r
4568         ResetGameEvent();\r
4569       }\r
4570       break;\r
4571 \r
4572     case IDM_NewVariant:\r
4573       NewVariantPopup(hwnd);\r
4574       break;\r
4575 \r
4576     case IDM_LoadGame:\r
4577       LoadGameDialog(hwnd, _("Load Game from File"));\r
4578       break;\r
4579 \r
4580     case IDM_LoadNextGame:\r
4581       ReloadGame(1);\r
4582       break;\r
4583 \r
4584     case IDM_LoadPrevGame:\r
4585       ReloadGame(-1);\r
4586       break;\r
4587 \r
4588     case IDM_ReloadGame:\r
4589       ReloadGame(0);\r
4590       break;\r
4591 \r
4592     case IDM_LoadPosition:\r
4593       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4594         Reset(FALSE, TRUE);\r
4595       }\r
4596       number = 1;\r
4597       f = OpenFileDialog(hwnd, "rb", "",\r
4598                          appData.oldSaveStyle ? "pos" : "fen",\r
4599                          POSITION_FILT,\r
4600                          _("Load Position from File"), &number, fileTitle, NULL);\r
4601       if (f != NULL) {\r
4602         LoadPosition(f, number, fileTitle);\r
4603       }\r
4604       break;\r
4605 \r
4606     case IDM_LoadNextPosition:\r
4607       ReloadPosition(1);\r
4608       break;\r
4609 \r
4610     case IDM_LoadPrevPosition:\r
4611       ReloadPosition(-1);\r
4612       break;\r
4613 \r
4614     case IDM_ReloadPosition:\r
4615       ReloadPosition(0);\r
4616       break;\r
4617 \r
4618     case IDM_SaveGame:\r
4619       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4620       f = OpenFileDialog(hwnd, "a", defName,\r
4621                          appData.oldSaveStyle ? "gam" : "pgn",\r
4622                          GAME_FILT,\r
4623                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4624       if (f != NULL) {\r
4625         SaveGame(f, 0, "");\r
4626       }\r
4627       break;\r
4628 \r
4629     case IDM_SavePosition:\r
4630       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4631       f = OpenFileDialog(hwnd, "a", defName,\r
4632                          appData.oldSaveStyle ? "pos" : "fen",\r
4633                          POSITION_FILT,\r
4634                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4635       if (f != NULL) {\r
4636         SavePosition(f, 0, "");\r
4637       }\r
4638       break;\r
4639 \r
4640     case IDM_SaveDiagram:\r
4641       defName = "diagram";\r
4642       f = OpenFileDialog(hwnd, "wb", defName,\r
4643                          "bmp",\r
4644                          DIAGRAM_FILT,\r
4645                          "Save Diagram to File", NULL, fileTitle, NULL);\r
4646       if (f != NULL) {\r
4647         SaveDiagram(f);\r
4648       }\r
4649       break;\r
4650 \r
4651     case IDM_CopyGame:\r
4652       CopyGameToClipboard();\r
4653       break;\r
4654 \r
4655     case IDM_PasteGame:\r
4656       PasteGameFromClipboard();\r
4657       break;\r
4658 \r
4659     case IDM_CopyGameListToClipboard:\r
4660       CopyGameListToClipboard();\r
4661       break;\r
4662 \r
4663     /* [AS] Autodetect FEN or PGN data */\r
4664     case IDM_PasteAny:\r
4665       PasteGameOrFENFromClipboard();\r
4666       break;\r
4667 \r
4668     /* [AS] Move history */\r
4669     case IDM_ShowMoveHistory:\r
4670         if( MoveHistoryIsUp() ) {\r
4671             MoveHistoryPopDown();\r
4672         }\r
4673         else {\r
4674             MoveHistoryPopUp();\r
4675         }\r
4676         break;\r
4677 \r
4678     /* [AS] Eval graph */\r
4679     case IDM_ShowEvalGraph:\r
4680         if( EvalGraphIsUp() ) {\r
4681             EvalGraphPopDown();\r
4682         }\r
4683         else {\r
4684             EvalGraphPopUp();\r
4685             SetFocus(hwndMain);\r
4686         }\r
4687         break;\r
4688 \r
4689     /* [AS] Engine output */\r
4690     case IDM_ShowEngineOutput:\r
4691         if( EngineOutputIsUp() ) {\r
4692             EngineOutputPopDown();\r
4693         }\r
4694         else {\r
4695             EngineOutputPopUp();\r
4696         }\r
4697         break;\r
4698 \r
4699     /* [AS] User adjudication */\r
4700     case IDM_UserAdjudication_White:\r
4701         UserAdjudicationEvent( +1 );\r
4702         break;\r
4703 \r
4704     case IDM_UserAdjudication_Black:\r
4705         UserAdjudicationEvent( -1 );\r
4706         break;\r
4707 \r
4708     case IDM_UserAdjudication_Draw:\r
4709         UserAdjudicationEvent( 0 );\r
4710         break;\r
4711 \r
4712     /* [AS] Game list options dialog */\r
4713     case IDM_GameListOptions:\r
4714       GameListOptions();\r
4715       break;\r
4716 \r
4717     case IDM_NewChat:\r
4718       ChatPopUp(NULL);\r
4719       break;\r
4720 \r
4721     case IDM_CopyPosition:\r
4722       CopyFENToClipboard();\r
4723       break;\r
4724 \r
4725     case IDM_PastePosition:\r
4726       PasteFENFromClipboard();\r
4727       break;\r
4728 \r
4729     case IDM_MailMove:\r
4730       MailMoveEvent();\r
4731       break;\r
4732 \r
4733     case IDM_ReloadCMailMsg:\r
4734       Reset(TRUE, TRUE);\r
4735       ReloadCmailMsgEvent(FALSE);\r
4736       break;\r
4737 \r
4738     case IDM_Minimize:\r
4739       ShowWindow(hwnd, SW_MINIMIZE);\r
4740       break;\r
4741 \r
4742     case IDM_Exit:\r
4743       ExitEvent(0);\r
4744       break;\r
4745 \r
4746     case IDM_MachineWhite:\r
4747       MachineWhiteEvent();\r
4748       /*\r
4749        * refresh the tags dialog only if it's visible\r
4750        */\r
4751       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4752           char *tags;\r
4753           tags = PGNTags(&gameInfo);\r
4754           TagsPopUp(tags, CmailMsg());\r
4755           free(tags);\r
4756       }\r
4757       SAY("computer starts playing white");\r
4758       break;\r
4759 \r
4760     case IDM_MachineBlack:\r
4761       MachineBlackEvent();\r
4762       /*\r
4763        * refresh the tags dialog only if it's visible\r
4764        */\r
4765       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4766           char *tags;\r
4767           tags = PGNTags(&gameInfo);\r
4768           TagsPopUp(tags, CmailMsg());\r
4769           free(tags);\r
4770       }\r
4771       SAY("computer starts playing black");\r
4772       break;\r
4773 \r
4774     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4775       if(gameMode != BeginningOfGame) break; // allow menu item to remain enabled for better mode highligting\r
4776       matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)\r
4777       appData.matchGames = appData.defaultMatchGames;\r
4778       matchGame = 1;\r
4779 \r
4780     case IDM_TwoMachines:\r
4781       TwoMachinesEvent();\r
4782       /*\r
4783        * refresh the tags dialog only if it's visible\r
4784        */\r
4785       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4786           char *tags;\r
4787           tags = PGNTags(&gameInfo);\r
4788           TagsPopUp(tags, CmailMsg());\r
4789           free(tags);\r
4790       }\r
4791       SAY("computer starts playing both sides");\r
4792       break;\r
4793 \r
4794     case IDM_AnalysisMode:\r
4795       if (!first.analysisSupport) {\r
4796         sprintf(buf, _("%s does not support analysis"), first.tidy);\r
4797         DisplayError(buf, 0);\r
4798       } else {\r
4799         SAY("analyzing current position");\r
4800         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4801         if (appData.icsActive) {\r
4802                if (gameMode != IcsObserving) {\r
4803                        sprintf(buf, "You are not observing a game");\r
4804                        DisplayError(buf, 0);\r
4805                        /* secure check */\r
4806                        if (appData.icsEngineAnalyze) {\r
4807                                if (appData.debugMode) \r
4808                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4809                                ExitAnalyzeMode();\r
4810                                ModeHighlight();\r
4811                                break;\r
4812                        }\r
4813                        break;\r
4814                } else {\r
4815                        /* if enable, user want disable icsEngineAnalyze */\r
4816                        if (appData.icsEngineAnalyze) {\r
4817                                ExitAnalyzeMode();\r
4818                                ModeHighlight();\r
4819                                break;\r
4820                        }\r
4821                        appData.icsEngineAnalyze = TRUE;\r
4822                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4823                }\r
4824         } \r
4825         if (!appData.showThinking) ToggleShowThinking();\r
4826         AnalyzeModeEvent();\r
4827       }\r
4828       break;\r
4829 \r
4830     case IDM_AnalyzeFile:\r
4831       if (!first.analysisSupport) {\r
4832         char buf[MSG_SIZ];\r
4833         sprintf(buf, _("%s does not support analysis"), first.tidy);\r
4834         DisplayError(buf, 0);\r
4835       } else {\r
4836         if (!appData.showThinking) ToggleShowThinking();\r
4837         AnalyzeFileEvent();\r
4838         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4839         AnalysisPeriodicEvent(1);\r
4840       }\r
4841       break;\r
4842 \r
4843     case IDM_IcsClient:\r
4844       IcsClientEvent();\r
4845       break;\r
4846 \r
4847     case IDM_EditGame:\r
4848       EditGameEvent();\r
4849       SAY("edit game");\r
4850       break;\r
4851 \r
4852     case IDM_EditPosition:\r
4853       EditPositionEvent();\r
4854       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4855       break;\r
4856 \r
4857     case IDM_Training:\r
4858       TrainingEvent();\r
4859       break;\r
4860 \r
4861     case IDM_ShowGameList:\r
4862       ShowGameListProc();\r
4863       break;\r
4864 \r
4865     case IDM_EditTags:\r
4866       EditTagsProc();\r
4867       break;\r
4868 \r
4869     case IDM_EditComment:\r
4870       if (commentUp && editComment) {\r
4871         CommentPopDown();\r
4872       } else {\r
4873         EditCommentEvent();\r
4874       }\r
4875       break;\r
4876 \r
4877     case IDM_Pause:\r
4878       PauseEvent();\r
4879       break;\r
4880 \r
4881     case IDM_Accept:\r
4882       AcceptEvent();\r
4883       break;\r
4884 \r
4885     case IDM_Decline:\r
4886       DeclineEvent();\r
4887       break;\r
4888 \r
4889     case IDM_Rematch:\r
4890       RematchEvent();\r
4891       break;\r
4892 \r
4893     case IDM_CallFlag:\r
4894       CallFlagEvent();\r
4895       break;\r
4896 \r
4897     case IDM_Draw:\r
4898       DrawEvent();\r
4899       break;\r
4900 \r
4901     case IDM_Adjourn:\r
4902       AdjournEvent();\r
4903       break;\r
4904 \r
4905     case IDM_Abort:\r
4906       AbortEvent();\r
4907       break;\r
4908 \r
4909     case IDM_Resign:\r
4910       ResignEvent();\r
4911       break;\r
4912 \r
4913     case IDM_StopObserving:\r
4914       StopObservingEvent();\r
4915       break;\r
4916 \r
4917     case IDM_StopExamining:\r
4918       StopExaminingEvent();\r
4919       break;\r
4920 \r
4921     case IDM_Upload:\r
4922       UploadGameEvent();\r
4923       break;\r
4924 \r
4925     case IDM_TypeInMove:\r
4926       PopUpMoveDialog('\000');\r
4927       break;\r
4928 \r
4929     case IDM_TypeInName:\r
4930       PopUpNameDialog('\000');\r
4931       break;\r
4932 \r
4933     case IDM_Backward:\r
4934       BackwardEvent();\r
4935       SetFocus(hwndMain);\r
4936       break;\r
4937 \r
4938     JAWS_MENU_ITEMS\r
4939 \r
4940     case IDM_Forward:\r
4941       ForwardEvent();\r
4942       SetFocus(hwndMain);\r
4943       break;\r
4944 \r
4945     case IDM_ToStart:\r
4946       ToStartEvent();\r
4947       SetFocus(hwndMain);\r
4948       break;\r
4949 \r
4950     case IDM_ToEnd:\r
4951       ToEndEvent();\r
4952       SetFocus(hwndMain);\r
4953       break;\r
4954 \r
4955     case IDM_Revert:\r
4956       RevertEvent(FALSE);\r
4957       break;\r
4958 \r
4959     case IDM_Annotate: // [HGM] vari: revert with annotation\r
4960       RevertEvent(TRUE);\r
4961       break;\r
4962 \r
4963     case IDM_TruncateGame:\r
4964       TruncateGameEvent();\r
4965       break;\r
4966 \r
4967     case IDM_MoveNow:\r
4968       MoveNowEvent();\r
4969       break;\r
4970 \r
4971     case IDM_RetractMove:\r
4972       RetractMoveEvent();\r
4973       break;\r
4974 \r
4975     case IDM_FlipView:\r
4976       flipView = !flipView;\r
4977       DrawPosition(FALSE, NULL);\r
4978       break;\r
4979 \r
4980     case IDM_FlipClock:\r
4981       flipClock = !flipClock;\r
4982       DisplayBothClocks();\r
4983       DrawPosition(FALSE, NULL);\r
4984       break;\r
4985 \r
4986     case IDM_MuteSounds:\r
4987       mute = !mute; // [HGM] mute: keep track of global muting variable\r
4988       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
4989                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
4990       break;\r
4991 \r
4992     case IDM_GeneralOptions:\r
4993       GeneralOptionsPopup(hwnd);\r
4994       DrawPosition(TRUE, NULL);\r
4995       break;\r
4996 \r
4997     case IDM_BoardOptions:\r
4998       BoardOptionsPopup(hwnd);\r
4999       break;\r
5000 \r
5001     case IDM_EnginePlayOptions:\r
5002       EnginePlayOptionsPopup(hwnd);\r
5003       break;\r
5004 \r
5005     case IDM_Engine1Options:\r
5006       EngineOptionsPopup(hwnd, &first);\r
5007       break;\r
5008 \r
5009     case IDM_Engine2Options:\r
5010       EngineOptionsPopup(hwnd, &second);\r
5011       break;\r
5012 \r
5013     case IDM_OptionsUCI:\r
5014       UciOptionsPopup(hwnd);\r
5015       break;\r
5016 \r
5017     case IDM_IcsOptions:\r
5018       IcsOptionsPopup(hwnd);\r
5019       break;\r
5020 \r
5021     case IDM_Fonts:\r
5022       FontsOptionsPopup(hwnd);\r
5023       break;\r
5024 \r
5025     case IDM_Sounds:\r
5026       SoundOptionsPopup(hwnd);\r
5027       break;\r
5028 \r
5029     case IDM_CommPort:\r
5030       CommPortOptionsPopup(hwnd);\r
5031       break;\r
5032 \r
5033     case IDM_LoadOptions:\r
5034       LoadOptionsPopup(hwnd);\r
5035       break;\r
5036 \r
5037     case IDM_SaveOptions:\r
5038       SaveOptionsPopup(hwnd);\r
5039       break;\r
5040 \r
5041     case IDM_TimeControl:\r
5042       TimeControlOptionsPopup(hwnd);\r
5043       break;\r
5044 \r
5045     case IDM_SaveSettings:\r
5046       SaveSettings(settingsFileName);\r
5047       break;\r
5048 \r
5049     case IDM_SaveSettingsOnExit:\r
5050       saveSettingsOnExit = !saveSettingsOnExit;\r
5051       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5052                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5053                                          MF_CHECKED : MF_UNCHECKED));\r
5054       break;\r
5055 \r
5056     case IDM_Hint:\r
5057       HintEvent();\r
5058       break;\r
5059 \r
5060     case IDM_Book:\r
5061       BookEvent();\r
5062       break;\r
5063 \r
5064     case IDM_AboutGame:\r
5065       AboutGameEvent();\r
5066       break;\r
5067 \r
5068     case IDM_Debug:\r
5069       appData.debugMode = !appData.debugMode;\r
5070       if (appData.debugMode) {\r
5071         char dir[MSG_SIZ];\r
5072         GetCurrentDirectory(MSG_SIZ, dir);\r
5073         SetCurrentDirectory(installDir);\r
5074         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5075         SetCurrentDirectory(dir);\r
5076         setbuf(debugFP, NULL);\r
5077       } else {\r
5078         fclose(debugFP);\r
5079         debugFP = NULL;\r
5080       }\r
5081       break;\r
5082 \r
5083     case IDM_HELPCONTENTS:\r
5084       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5085           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5086           MessageBox (GetFocus(),\r
5087                     _("Unable to activate help"),\r
5088                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5089       }\r
5090       break;\r
5091 \r
5092     case IDM_HELPSEARCH:\r
5093         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5094             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5095         MessageBox (GetFocus(),\r
5096                     _("Unable to activate help"),\r
5097                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5098       }\r
5099       break;\r
5100 \r
5101     case IDM_HELPHELP:\r
5102       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5103         MessageBox (GetFocus(),\r
5104                     _("Unable to activate help"),\r
5105                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5106       }\r
5107       break;\r
5108 \r
5109     case IDM_ABOUT:\r
5110       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5111       DialogBox(hInst, \r
5112         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5113         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5114       FreeProcInstance(lpProc);\r
5115       break;\r
5116 \r
5117     case IDM_DirectCommand1:\r
5118       AskQuestionEvent(_("Direct Command"),\r
5119                        _("Send to chess program:"), "", "1");\r
5120       break;\r
5121     case IDM_DirectCommand2:\r
5122       AskQuestionEvent(_("Direct Command"),\r
5123                        _("Send to second chess program:"), "", "2");\r
5124       break;\r
5125 \r
5126     case EP_WhitePawn:\r
5127       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5128       fromX = fromY = -1;\r
5129       break;\r
5130 \r
5131     case EP_WhiteKnight:\r
5132       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5133       fromX = fromY = -1;\r
5134       break;\r
5135 \r
5136     case EP_WhiteBishop:\r
5137       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5138       fromX = fromY = -1;\r
5139       break;\r
5140 \r
5141     case EP_WhiteRook:\r
5142       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5143       fromX = fromY = -1;\r
5144       break;\r
5145 \r
5146     case EP_WhiteQueen:\r
5147       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5148       fromX = fromY = -1;\r
5149       break;\r
5150 \r
5151     case EP_WhiteFerz:\r
5152       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5153       fromX = fromY = -1;\r
5154       break;\r
5155 \r
5156     case EP_WhiteWazir:\r
5157       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5158       fromX = fromY = -1;\r
5159       break;\r
5160 \r
5161     case EP_WhiteAlfil:\r
5162       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5163       fromX = fromY = -1;\r
5164       break;\r
5165 \r
5166     case EP_WhiteCannon:\r
5167       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5168       fromX = fromY = -1;\r
5169       break;\r
5170 \r
5171     case EP_WhiteCardinal:\r
5172       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5173       fromX = fromY = -1;\r
5174       break;\r
5175 \r
5176     case EP_WhiteMarshall:\r
5177       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5178       fromX = fromY = -1;\r
5179       break;\r
5180 \r
5181     case EP_WhiteKing:\r
5182       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5183       fromX = fromY = -1;\r
5184       break;\r
5185 \r
5186     case EP_BlackPawn:\r
5187       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5188       fromX = fromY = -1;\r
5189       break;\r
5190 \r
5191     case EP_BlackKnight:\r
5192       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5193       fromX = fromY = -1;\r
5194       break;\r
5195 \r
5196     case EP_BlackBishop:\r
5197       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5198       fromX = fromY = -1;\r
5199       break;\r
5200 \r
5201     case EP_BlackRook:\r
5202       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5203       fromX = fromY = -1;\r
5204       break;\r
5205 \r
5206     case EP_BlackQueen:\r
5207       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5208       fromX = fromY = -1;\r
5209       break;\r
5210 \r
5211     case EP_BlackFerz:\r
5212       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5213       fromX = fromY = -1;\r
5214       break;\r
5215 \r
5216     case EP_BlackWazir:\r
5217       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5218       fromX = fromY = -1;\r
5219       break;\r
5220 \r
5221     case EP_BlackAlfil:\r
5222       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5223       fromX = fromY = -1;\r
5224       break;\r
5225 \r
5226     case EP_BlackCannon:\r
5227       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5228       fromX = fromY = -1;\r
5229       break;\r
5230 \r
5231     case EP_BlackCardinal:\r
5232       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5233       fromX = fromY = -1;\r
5234       break;\r
5235 \r
5236     case EP_BlackMarshall:\r
5237       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5238       fromX = fromY = -1;\r
5239       break;\r
5240 \r
5241     case EP_BlackKing:\r
5242       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5243       fromX = fromY = -1;\r
5244       break;\r
5245 \r
5246     case EP_EmptySquare:\r
5247       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5248       fromX = fromY = -1;\r
5249       break;\r
5250 \r
5251     case EP_ClearBoard:\r
5252       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5253       fromX = fromY = -1;\r
5254       break;\r
5255 \r
5256     case EP_White:\r
5257       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5258       fromX = fromY = -1;\r
5259       break;\r
5260 \r
5261     case EP_Black:\r
5262       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5263       fromX = fromY = -1;\r
5264       break;\r
5265 \r
5266     case EP_Promote:\r
5267       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5268       fromX = fromY = -1;\r
5269       break;\r
5270 \r
5271     case EP_Demote:\r
5272       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5273       fromX = fromY = -1;\r
5274       break;\r
5275 \r
5276     case DP_Pawn:\r
5277       DropMenuEvent(WhitePawn, fromX, fromY);\r
5278       fromX = fromY = -1;\r
5279       break;\r
5280 \r
5281     case DP_Knight:\r
5282       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5283       fromX = fromY = -1;\r
5284       break;\r
5285 \r
5286     case DP_Bishop:\r
5287       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5288       fromX = fromY = -1;\r
5289       break;\r
5290 \r
5291     case DP_Rook:\r
5292       DropMenuEvent(WhiteRook, fromX, fromY);\r
5293       fromX = fromY = -1;\r
5294       break;\r
5295 \r
5296     case DP_Queen:\r
5297       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5298       fromX = fromY = -1;\r
5299       break;\r
5300 \r
5301     default:\r
5302       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5303     }\r
5304     break;\r
5305 \r
5306   case WM_TIMER:\r
5307     switch (wParam) {\r
5308     case CLOCK_TIMER_ID:\r
5309       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5310       clockTimerEvent = 0;\r
5311       DecrementClocks(); /* call into back end */\r
5312       break;\r
5313     case LOAD_GAME_TIMER_ID:\r
5314       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5315       loadGameTimerEvent = 0;\r
5316       AutoPlayGameLoop(); /* call into back end */\r
5317       break;\r
5318     case ANALYSIS_TIMER_ID:\r
5319       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5320                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5321         AnalysisPeriodicEvent(0);\r
5322       } else {\r
5323         KillTimer(hwnd, analysisTimerEvent);\r
5324         analysisTimerEvent = 0;\r
5325       }\r
5326       break;\r
5327     case DELAYED_TIMER_ID:\r
5328       KillTimer(hwnd, delayedTimerEvent);\r
5329       delayedTimerEvent = 0;\r
5330       delayedTimerCallback();\r
5331       break;\r
5332     }\r
5333     break;\r
5334 \r
5335   case WM_USER_Input:\r
5336     InputEvent(hwnd, message, wParam, lParam);\r
5337     break;\r
5338 \r
5339   /* [AS] Also move "attached" child windows */\r
5340   case WM_WINDOWPOSCHANGING:\r
5341 \r
5342     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5343         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5344 \r
5345         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5346             /* Window is moving */\r
5347             RECT rcMain;\r
5348 \r
5349 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5350             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5351             rcMain.right  = wpMain.x + wpMain.width;\r
5352             rcMain.top    = wpMain.y;\r
5353             rcMain.bottom = wpMain.y + wpMain.height;\r
5354             \r
5355             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5356             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5357             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5358             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5359             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5360             wpMain.x = lpwp->x;\r
5361             wpMain.y = lpwp->y;\r
5362         }\r
5363     }\r
5364     break;\r
5365 \r
5366   /* [AS] Snapping */\r
5367   case WM_ENTERSIZEMOVE:\r
5368     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5369     if (hwnd == hwndMain) {\r
5370       doingSizing = TRUE;\r
5371       lastSizing = 0;\r
5372     }\r
5373     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5374     break;\r
5375 \r
5376   case WM_SIZING:\r
5377     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5378     if (hwnd == hwndMain) {\r
5379       lastSizing = wParam;\r
5380     }\r
5381     break;\r
5382 \r
5383   case WM_MOVING:\r
5384     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5385       return OnMoving( &sd, hwnd, wParam, lParam );\r
5386 \r
5387   case WM_EXITSIZEMOVE:\r
5388     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5389     if (hwnd == hwndMain) {\r
5390       RECT client;\r
5391       doingSizing = FALSE;\r
5392       InvalidateRect(hwnd, &boardRect, FALSE);\r
5393       GetClientRect(hwnd, &client);\r
5394       ResizeBoard(client.right, client.bottom, lastSizing);\r
5395       lastSizing = 0;\r
5396       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5397     }\r
5398     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5399     break;\r
5400 \r
5401   case WM_DESTROY: /* message: window being destroyed */\r
5402     PostQuitMessage(0);\r
5403     break;\r
5404 \r
5405   case WM_CLOSE:\r
5406     if (hwnd == hwndMain) {\r
5407       ExitEvent(0);\r
5408     }\r
5409     break;\r
5410 \r
5411   default:      /* Passes it on if unprocessed */\r
5412     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5413   }\r
5414   return 0;\r
5415 }\r
5416 \r
5417 /*---------------------------------------------------------------------------*\\r
5418  *\r
5419  * Misc utility routines\r
5420  *\r
5421 \*---------------------------------------------------------------------------*/\r
5422 \r
5423 /*\r
5424  * Decent random number generator, at least not as bad as Windows\r
5425  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5426  */\r
5427 unsigned int randstate;\r
5428 \r
5429 int\r
5430 myrandom(void)\r
5431 {\r
5432   randstate = randstate * 1664525 + 1013904223;\r
5433   return (int) randstate & 0x7fffffff;\r
5434 }\r
5435 \r
5436 void\r
5437 mysrandom(unsigned int seed)\r
5438 {\r
5439   randstate = seed;\r
5440 }\r
5441 \r
5442 \r
5443 /* \r
5444  * returns TRUE if user selects a different color, FALSE otherwise \r
5445  */\r
5446 \r
5447 BOOL\r
5448 ChangeColor(HWND hwnd, COLORREF *which)\r
5449 {\r
5450   static BOOL firstTime = TRUE;\r
5451   static DWORD customColors[16];\r
5452   CHOOSECOLOR cc;\r
5453   COLORREF newcolor;\r
5454   int i;\r
5455   ColorClass ccl;\r
5456 \r
5457   if (firstTime) {\r
5458     /* Make initial colors in use available as custom colors */\r
5459     /* Should we put the compiled-in defaults here instead? */\r
5460     i = 0;\r
5461     customColors[i++] = lightSquareColor & 0xffffff;\r
5462     customColors[i++] = darkSquareColor & 0xffffff;\r
5463     customColors[i++] = whitePieceColor & 0xffffff;\r
5464     customColors[i++] = blackPieceColor & 0xffffff;\r
5465     customColors[i++] = highlightSquareColor & 0xffffff;\r
5466     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5467 \r
5468     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5469       customColors[i++] = textAttribs[ccl].color;\r
5470     }\r
5471     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5472     firstTime = FALSE;\r
5473   }\r
5474 \r
5475   cc.lStructSize = sizeof(cc);\r
5476   cc.hwndOwner = hwnd;\r
5477   cc.hInstance = NULL;\r
5478   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5479   cc.lpCustColors = (LPDWORD) customColors;\r
5480   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5481 \r
5482   if (!ChooseColor(&cc)) return FALSE;\r
5483 \r
5484   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5485   if (newcolor == *which) return FALSE;\r
5486   *which = newcolor;\r
5487   return TRUE;\r
5488 \r
5489   /*\r
5490   InitDrawingColors();\r
5491   InvalidateRect(hwnd, &boardRect, FALSE);\r
5492   */\r
5493 }\r
5494 \r
5495 BOOLEAN\r
5496 MyLoadSound(MySound *ms)\r
5497 {\r
5498   BOOL ok = FALSE;\r
5499   struct stat st;\r
5500   FILE *f;\r
5501 \r
5502   if (ms->data) free(ms->data);\r
5503   ms->data = NULL;\r
5504 \r
5505   switch (ms->name[0]) {\r
5506   case NULLCHAR:\r
5507     /* Silence */\r
5508     ok = TRUE;\r
5509     break;\r
5510   case '$':\r
5511     /* System sound from Control Panel.  Don't preload here. */\r
5512     ok = TRUE;\r
5513     break;\r
5514   case '!':\r
5515     if (ms->name[1] == NULLCHAR) {\r
5516       /* "!" alone = silence */\r
5517       ok = TRUE;\r
5518     } else {\r
5519       /* Builtin wave resource.  Error if not found. */\r
5520       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5521       if (h == NULL) break;\r
5522       ms->data = (void *)LoadResource(hInst, h);\r
5523       if (h == NULL) break;\r
5524       ok = TRUE;\r
5525     }\r
5526     break;\r
5527   default:\r
5528     /* .wav file.  Error if not found. */\r
5529     f = fopen(ms->name, "rb");\r
5530     if (f == NULL) break;\r
5531     if (fstat(fileno(f), &st) < 0) break;\r
5532     ms->data = malloc(st.st_size);\r
5533     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5534     fclose(f);\r
5535     ok = TRUE;\r
5536     break;\r
5537   }\r
5538   if (!ok) {\r
5539     char buf[MSG_SIZ];\r
5540     sprintf(buf, _("Error loading sound %s"), ms->name);\r
5541     DisplayError(buf, GetLastError());\r
5542   }\r
5543   return ok;\r
5544 }\r
5545 \r
5546 BOOLEAN\r
5547 MyPlaySound(MySound *ms)\r
5548 {\r
5549   BOOLEAN ok = FALSE;\r
5550 \r
5551   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5552   switch (ms->name[0]) {\r
5553   case NULLCHAR:\r
5554         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5555     /* Silence */\r
5556     ok = TRUE;\r
5557     break;\r
5558   case '$':\r
5559     /* System sound from Control Panel (deprecated feature).\r
5560        "$" alone or an unset sound name gets default beep (still in use). */\r
5561     if (ms->name[1]) {\r
5562       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5563     }\r
5564     if (!ok) ok = MessageBeep(MB_OK);\r
5565     break; \r
5566   case '!':\r
5567     /* Builtin wave resource, or "!" alone for silence */\r
5568     if (ms->name[1]) {\r
5569       if (ms->data == NULL) return FALSE;\r
5570       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5571     } else {\r
5572       ok = TRUE;\r
5573     }\r
5574     break;\r
5575   default:\r
5576     /* .wav file.  Error if not found. */\r
5577     if (ms->data == NULL) return FALSE;\r
5578     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5579     break;\r
5580   }\r
5581   /* Don't print an error: this can happen innocently if the sound driver\r
5582      is busy; for instance, if another instance of WinBoard is playing\r
5583      a sound at about the same time. */\r
5584   return ok;\r
5585 }\r
5586 \r
5587 \r
5588 LRESULT CALLBACK\r
5589 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5590 {\r
5591   BOOL ok;\r
5592   OPENFILENAME *ofn;\r
5593   static UINT *number; /* gross that this is static */\r
5594 \r
5595   switch (message) {\r
5596   case WM_INITDIALOG: /* message: initialize dialog box */\r
5597     /* Center the dialog over the application window */\r
5598     ofn = (OPENFILENAME *) lParam;\r
5599     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5600       number = (UINT *) ofn->lCustData;\r
5601       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5602     } else {\r
5603       number = NULL;\r
5604     }\r
5605     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5606     return FALSE;  /* Allow for further processing */\r
5607 \r
5608   case WM_COMMAND:\r
5609     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5610       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5611     }\r
5612     return FALSE;  /* Allow for further processing */\r
5613   }\r
5614   return FALSE;\r
5615 }\r
5616 \r
5617 UINT APIENTRY\r
5618 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5619 {\r
5620   static UINT *number;\r
5621   OPENFILENAME *ofname;\r
5622   OFNOTIFY *ofnot;\r
5623   switch (uiMsg) {\r
5624   case WM_INITDIALOG:\r
5625     ofname = (OPENFILENAME *)lParam;\r
5626     number = (UINT *)(ofname->lCustData);\r
5627     break;\r
5628   case WM_NOTIFY:\r
5629     ofnot = (OFNOTIFY *)lParam;\r
5630     if (ofnot->hdr.code == CDN_FILEOK) {\r
5631       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5632     }\r
5633     break;\r
5634   }\r
5635   return 0;\r
5636 }\r
5637 \r
5638 \r
5639 FILE *\r
5640 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5641                char *nameFilt, char *dlgTitle, UINT *number,\r
5642                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5643 {\r
5644   OPENFILENAME openFileName;\r
5645   char buf1[MSG_SIZ];\r
5646   FILE *f;\r
5647 \r
5648   if (fileName == NULL) fileName = buf1;\r
5649   if (defName == NULL) {\r
5650     strcpy(fileName, "*.");\r
5651     strcat(fileName, defExt);\r
5652   } else {\r
5653     strcpy(fileName, defName);\r
5654   }\r
5655   if (fileTitle) strcpy(fileTitle, "");\r
5656   if (number) *number = 0;\r
5657 \r
5658   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5659   openFileName.hwndOwner         = hwnd;\r
5660   openFileName.hInstance         = (HANDLE) hInst;\r
5661   openFileName.lpstrFilter       = nameFilt;\r
5662   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5663   openFileName.nMaxCustFilter    = 0L;\r
5664   openFileName.nFilterIndex      = 1L;\r
5665   openFileName.lpstrFile         = fileName;\r
5666   openFileName.nMaxFile          = MSG_SIZ;\r
5667   openFileName.lpstrFileTitle    = fileTitle;\r
5668   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5669   openFileName.lpstrInitialDir   = NULL;\r
5670   openFileName.lpstrTitle        = dlgTitle;\r
5671   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5672     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5673     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5674     | (oldDialog ? 0 : OFN_EXPLORER);\r
5675   openFileName.nFileOffset       = 0;\r
5676   openFileName.nFileExtension    = 0;\r
5677   openFileName.lpstrDefExt       = defExt;\r
5678   openFileName.lCustData         = (LONG) number;\r
5679   openFileName.lpfnHook          = oldDialog ?\r
5680     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5681   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5682 \r
5683   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5684                         GetOpenFileName(&openFileName)) {\r
5685     /* open the file */\r
5686     f = fopen(openFileName.lpstrFile, write);\r
5687     if (f == NULL) {\r
5688       MessageBox(hwnd, _("File open failed"), NULL,\r
5689                  MB_OK|MB_ICONEXCLAMATION);\r
5690       return NULL;\r
5691     }\r
5692   } else {\r
5693     int err = CommDlgExtendedError();\r
5694     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5695     return FALSE;\r
5696   }\r
5697   return f;\r
5698 }\r
5699 \r
5700 \r
5701 \r
5702 VOID APIENTRY\r
5703 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5704 {\r
5705   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5706 \r
5707   /*\r
5708    * Get the first pop-up menu in the menu template. This is the\r
5709    * menu that TrackPopupMenu displays.\r
5710    */\r
5711   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5712 \r
5713   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5714 \r
5715   /*\r
5716    * TrackPopup uses screen coordinates, so convert the\r
5717    * coordinates of the mouse click to screen coordinates.\r
5718    */\r
5719   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5720 \r
5721   /* Draw and track the floating pop-up menu. */\r
5722   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5723                  pt.x, pt.y, 0, hwnd, NULL);\r
5724 \r
5725   /* Destroy the menu.*/\r
5726   DestroyMenu(hmenu);\r
5727 }\r
5728    \r
5729 typedef struct {\r
5730   HWND hDlg, hText;\r
5731   int sizeX, sizeY, newSizeX, newSizeY;\r
5732   HDWP hdwp;\r
5733 } ResizeEditPlusButtonsClosure;\r
5734 \r
5735 BOOL CALLBACK\r
5736 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5737 {\r
5738   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5739   RECT rect;\r
5740   POINT pt;\r
5741 \r
5742   if (hChild == cl->hText) return TRUE;\r
5743   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5744   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5745   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5746   ScreenToClient(cl->hDlg, &pt);\r
5747   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5748     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5749   return TRUE;\r
5750 }\r
5751 \r
5752 /* Resize a dialog that has a (rich) edit field filling most of\r
5753    the top, with a row of buttons below */\r
5754 VOID\r
5755 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5756 {\r
5757   RECT rectText;\r
5758   int newTextHeight, newTextWidth;\r
5759   ResizeEditPlusButtonsClosure cl;\r
5760   \r
5761   /*if (IsIconic(hDlg)) return;*/\r
5762   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5763   \r
5764   cl.hdwp = BeginDeferWindowPos(8);\r
5765 \r
5766   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5767   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5768   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5769   if (newTextHeight < 0) {\r
5770     newSizeY += -newTextHeight;\r
5771     newTextHeight = 0;\r
5772   }\r
5773   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5774     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5775 \r
5776   cl.hDlg = hDlg;\r
5777   cl.hText = hText;\r
5778   cl.sizeX = sizeX;\r
5779   cl.sizeY = sizeY;\r
5780   cl.newSizeX = newSizeX;\r
5781   cl.newSizeY = newSizeY;\r
5782   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5783 \r
5784   EndDeferWindowPos(cl.hdwp);\r
5785 }\r
5786 \r
5787 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5788 {\r
5789     RECT    rChild, rParent;\r
5790     int     wChild, hChild, wParent, hParent;\r
5791     int     wScreen, hScreen, xNew, yNew;\r
5792     HDC     hdc;\r
5793 \r
5794     /* Get the Height and Width of the child window */\r
5795     GetWindowRect (hwndChild, &rChild);\r
5796     wChild = rChild.right - rChild.left;\r
5797     hChild = rChild.bottom - rChild.top;\r
5798 \r
5799     /* Get the Height and Width of the parent window */\r
5800     GetWindowRect (hwndParent, &rParent);\r
5801     wParent = rParent.right - rParent.left;\r
5802     hParent = rParent.bottom - rParent.top;\r
5803 \r
5804     /* Get the display limits */\r
5805     hdc = GetDC (hwndChild);\r
5806     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5807     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5808     ReleaseDC(hwndChild, hdc);\r
5809 \r
5810     /* Calculate new X position, then adjust for screen */\r
5811     xNew = rParent.left + ((wParent - wChild) /2);\r
5812     if (xNew < 0) {\r
5813         xNew = 0;\r
5814     } else if ((xNew+wChild) > wScreen) {\r
5815         xNew = wScreen - wChild;\r
5816     }\r
5817 \r
5818     /* Calculate new Y position, then adjust for screen */\r
5819     if( mode == 0 ) {\r
5820         yNew = rParent.top  + ((hParent - hChild) /2);\r
5821     }\r
5822     else {\r
5823         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5824     }\r
5825 \r
5826     if (yNew < 0) {\r
5827         yNew = 0;\r
5828     } else if ((yNew+hChild) > hScreen) {\r
5829         yNew = hScreen - hChild;\r
5830     }\r
5831 \r
5832     /* Set it, and return */\r
5833     return SetWindowPos (hwndChild, NULL,\r
5834                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5835 }\r
5836 \r
5837 /* Center one window over another */\r
5838 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5839 {\r
5840     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5841 }\r
5842 \r
5843 /*---------------------------------------------------------------------------*\\r
5844  *\r
5845  * Startup Dialog functions\r
5846  *\r
5847 \*---------------------------------------------------------------------------*/\r
5848 void\r
5849 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5850 {\r
5851   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5852 \r
5853   while (*cd != NULL) {\r
5854     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
5855     cd++;\r
5856   }\r
5857 }\r
5858 \r
5859 void\r
5860 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5861 {\r
5862   char buf1[MAX_ARG_LEN];\r
5863   int len;\r
5864 \r
5865   if (str[0] == '@') {\r
5866     FILE* f = fopen(str + 1, "r");\r
5867     if (f == NULL) {\r
5868       DisplayFatalError(str + 1, errno, 2);\r
5869       return;\r
5870     }\r
5871     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5872     fclose(f);\r
5873     buf1[len] = NULLCHAR;\r
5874     str = buf1;\r
5875   }\r
5876 \r
5877   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5878 \r
5879   for (;;) {\r
5880     char buf[MSG_SIZ];\r
5881     char *end = strchr(str, '\n');\r
5882     if (end == NULL) return;\r
5883     memcpy(buf, str, end - str);\r
5884     buf[end - str] = NULLCHAR;\r
5885     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5886     str = end + 1;\r
5887   }\r
5888 }\r
5889 \r
5890 void\r
5891 SetStartupDialogEnables(HWND hDlg)\r
5892 {\r
5893   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
5894     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5895     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
5896   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5897     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
5898   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
5899     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
5900   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
5901     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
5902   EnableWindow(GetDlgItem(hDlg, IDOK),\r
5903     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5904     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
5905     IsDlgButtonChecked(hDlg, OPT_View));\r
5906 }\r
5907 \r
5908 char *\r
5909 QuoteForFilename(char *filename)\r
5910 {\r
5911   int dquote, space;\r
5912   dquote = strchr(filename, '"') != NULL;\r
5913   space = strchr(filename, ' ') != NULL;\r
5914   if (dquote || space) {\r
5915     if (dquote) {\r
5916       return "'";\r
5917     } else {\r
5918       return "\"";\r
5919     }\r
5920   } else {\r
5921     return "";\r
5922   }\r
5923 }\r
5924 \r
5925 VOID\r
5926 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
5927 {\r
5928   char buf[MSG_SIZ];\r
5929   char *q;\r
5930 \r
5931   InitComboStringsFromOption(hwndCombo, nthnames);\r
5932   q = QuoteForFilename(nthcp);\r
5933   sprintf(buf, "%s%s%s", q, nthcp, q);\r
5934   if (*nthdir != NULLCHAR) {\r
5935     q = QuoteForFilename(nthdir);\r
5936     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
5937   }\r
5938   if (*nthcp == NULLCHAR) {\r
5939     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
5940   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
5941     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
5942     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
5943   }\r
5944 }\r
5945 \r
5946 LRESULT CALLBACK\r
5947 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5948 {\r
5949   char buf[MSG_SIZ];\r
5950   HANDLE hwndCombo;\r
5951   char *p;\r
5952 \r
5953   switch (message) {\r
5954   case WM_INITDIALOG:\r
5955     /* Center the dialog */\r
5956     CenterWindow (hDlg, GetDesktopWindow());\r
5957     Translate(hDlg, DLG_Startup);\r
5958     /* Initialize the dialog items */\r
5959     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
5960                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
5961                   firstChessProgramNames);\r
5962     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5963                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
5964                   secondChessProgramNames);\r
5965     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
5966     InitComboStringsFromOption(hwndCombo, icsNames);    \r
5967     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
5968     if (*appData.icsHelper != NULLCHAR) {\r
5969       char *q = QuoteForFilename(appData.icsHelper);\r
5970       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
5971     }\r
5972     if (*appData.icsHost == NULLCHAR) {\r
5973       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
5974       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
5975     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
5976       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
5977       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
5978     }\r
5979 \r
5980     if (appData.icsActive) {\r
5981       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
5982     }\r
5983     else if (appData.noChessProgram) {\r
5984       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
5985     }\r
5986     else {\r
5987       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
5988     }\r
5989 \r
5990     SetStartupDialogEnables(hDlg);\r
5991     return TRUE;\r
5992 \r
5993   case WM_COMMAND:\r
5994     switch (LOWORD(wParam)) {\r
5995     case IDOK:\r
5996       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
5997         strcpy(buf, "/fcp=");\r
5998         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5999         p = buf;\r
6000         ParseArgs(StringGet, &p);\r
6001         strcpy(buf, "/scp=");\r
6002         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6003         p = buf;\r
6004         ParseArgs(StringGet, &p);\r
6005         appData.noChessProgram = FALSE;\r
6006         appData.icsActive = FALSE;\r
6007       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6008         strcpy(buf, "/ics /icshost=");\r
6009         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6010         p = buf;\r
6011         ParseArgs(StringGet, &p);\r
6012         if (appData.zippyPlay) {\r
6013           strcpy(buf, "/fcp=");\r
6014           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6015           p = buf;\r
6016           ParseArgs(StringGet, &p);\r
6017         }\r
6018       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6019         appData.noChessProgram = TRUE;\r
6020         appData.icsActive = FALSE;\r
6021       } else {\r
6022         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6023                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6024         return TRUE;\r
6025       }\r
6026       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6027         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6028         p = buf;\r
6029         ParseArgs(StringGet, &p);\r
6030       }\r
6031       EndDialog(hDlg, TRUE);\r
6032       return TRUE;\r
6033 \r
6034     case IDCANCEL:\r
6035       ExitEvent(0);\r
6036       return TRUE;\r
6037 \r
6038     case IDM_HELPCONTENTS:\r
6039       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6040         MessageBox (GetFocus(),\r
6041                     _("Unable to activate help"),\r
6042                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6043       }\r
6044       break;\r
6045 \r
6046     default:\r
6047       SetStartupDialogEnables(hDlg);\r
6048       break;\r
6049     }\r
6050     break;\r
6051   }\r
6052   return FALSE;\r
6053 }\r
6054 \r
6055 /*---------------------------------------------------------------------------*\\r
6056  *\r
6057  * About box dialog functions\r
6058  *\r
6059 \*---------------------------------------------------------------------------*/\r
6060 \r
6061 /* Process messages for "About" dialog box */\r
6062 LRESULT CALLBACK\r
6063 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6064 {\r
6065   switch (message) {\r
6066   case WM_INITDIALOG: /* message: initialize dialog box */\r
6067     /* Center the dialog over the application window */\r
6068     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6069     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6070     JAWS_COPYRIGHT\r
6071     return (TRUE);\r
6072 \r
6073   case WM_COMMAND: /* message: received a command */\r
6074     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6075         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6076       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6077       return (TRUE);\r
6078     }\r
6079     break;\r
6080   }\r
6081   return (FALSE);\r
6082 }\r
6083 \r
6084 /*---------------------------------------------------------------------------*\\r
6085  *\r
6086  * Comment Dialog functions\r
6087  *\r
6088 \*---------------------------------------------------------------------------*/\r
6089 \r
6090 LRESULT CALLBACK\r
6091 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6092 {\r
6093   static HANDLE hwndText = NULL;\r
6094   int len, newSizeX, newSizeY, flags;\r
6095   static int sizeX, sizeY;\r
6096   char *str;\r
6097   RECT rect;\r
6098   MINMAXINFO *mmi;\r
6099 \r
6100   switch (message) {\r
6101   case WM_INITDIALOG: /* message: initialize dialog box */\r
6102     /* Initialize the dialog items */\r
6103     Translate(hDlg, DLG_EditComment);\r
6104     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6105     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6106     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6107     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6108     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6109     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6110     SetWindowText(hDlg, commentTitle);\r
6111     if (editComment) {\r
6112       SetFocus(hwndText);\r
6113     } else {\r
6114       SetFocus(GetDlgItem(hDlg, IDOK));\r
6115     }\r
6116     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6117                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6118                 MAKELPARAM(FALSE, 0));\r
6119     /* Size and position the dialog */\r
6120     if (!commentDialog) {\r
6121       commentDialog = hDlg;\r
6122       flags = SWP_NOZORDER;\r
6123       GetClientRect(hDlg, &rect);\r
6124       sizeX = rect.right;\r
6125       sizeY = rect.bottom;\r
6126       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6127           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6128         WINDOWPLACEMENT wp;\r
6129         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6130         wp.length = sizeof(WINDOWPLACEMENT);\r
6131         wp.flags = 0;\r
6132         wp.showCmd = SW_SHOW;\r
6133         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6134         wp.rcNormalPosition.left = wpComment.x;\r
6135         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6136         wp.rcNormalPosition.top = wpComment.y;\r
6137         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6138         SetWindowPlacement(hDlg, &wp);\r
6139 \r
6140         GetClientRect(hDlg, &rect);\r
6141         newSizeX = rect.right;\r
6142         newSizeY = rect.bottom;\r
6143         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6144                               newSizeX, newSizeY);\r
6145         sizeX = newSizeX;\r
6146         sizeY = newSizeY;\r
6147       }\r
6148     }\r
6149     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );\r
6150     return FALSE;\r
6151 \r
6152   case WM_COMMAND: /* message: received a command */\r
6153     switch (LOWORD(wParam)) {\r
6154     case IDOK:\r
6155       if (editComment) {\r
6156         char *p, *q;\r
6157         /* Read changed options from the dialog box */\r
6158         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6159         len = GetWindowTextLength(hwndText);\r
6160         str = (char *) malloc(len + 1);\r
6161         GetWindowText(hwndText, str, len + 1);\r
6162         p = q = str;\r
6163         while (*q) {\r
6164           if (*q == '\r')\r
6165             q++;\r
6166           else\r
6167             *p++ = *q++;\r
6168         }\r
6169         *p = NULLCHAR;\r
6170         ReplaceComment(commentIndex, str);\r
6171         free(str);\r
6172       }\r
6173       CommentPopDown();\r
6174       return TRUE;\r
6175 \r
6176     case IDCANCEL:\r
6177     case OPT_CancelComment:\r
6178       CommentPopDown();\r
6179       return TRUE;\r
6180 \r
6181     case OPT_ClearComment:\r
6182       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6183       break;\r
6184 \r
6185     case OPT_EditComment:\r
6186       EditCommentEvent();\r
6187       return TRUE;\r
6188 \r
6189     default:\r
6190       break;\r
6191     }\r
6192     break;\r
6193 \r
6194   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6195         if( wParam == OPT_CommentText ) {\r
6196             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6197 \r
6198             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {\r
6199                 POINTL pt;\r
6200                 LRESULT index;\r
6201 \r
6202                 pt.x = LOWORD( lpMF->lParam );\r
6203                 pt.y = HIWORD( lpMF->lParam );\r
6204 \r
6205                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6206 \r
6207                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6208                 len = GetWindowTextLength(hwndText);\r
6209                 str = (char *) malloc(len + 1);\r
6210                 GetWindowText(hwndText, str, len + 1);\r
6211                 ReplaceComment(commentIndex, str);\r
6212                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6213                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6214                 free(str);\r
6215 \r
6216                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6217                 lpMF->msg = WM_USER;\r
6218 \r
6219                 return TRUE;\r
6220             }\r
6221         }\r
6222         break;\r
6223 \r
6224   case WM_SIZE:\r
6225     newSizeX = LOWORD(lParam);\r
6226     newSizeY = HIWORD(lParam);\r
6227     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6228     sizeX = newSizeX;\r
6229     sizeY = newSizeY;\r
6230     break;\r
6231 \r
6232   case WM_GETMINMAXINFO:\r
6233     /* Prevent resizing window too small */\r
6234     mmi = (MINMAXINFO *) lParam;\r
6235     mmi->ptMinTrackSize.x = 100;\r
6236     mmi->ptMinTrackSize.y = 100;\r
6237     break;\r
6238   }\r
6239   return FALSE;\r
6240 }\r
6241 \r
6242 VOID\r
6243 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6244 {\r
6245   FARPROC lpProc;\r
6246   char *p, *q;\r
6247 \r
6248   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6249 \r
6250   if (str == NULL) str = "";\r
6251   p = (char *) malloc(2 * strlen(str) + 2);\r
6252   q = p;\r
6253   while (*str) {\r
6254     if (*str == '\n') *q++ = '\r';\r
6255     *q++ = *str++;\r
6256   }\r
6257   *q = NULLCHAR;\r
6258   if (commentText != NULL) free(commentText);\r
6259 \r
6260   commentIndex = index;\r
6261   commentTitle = title;\r
6262   commentText = p;\r
6263   editComment = edit;\r
6264 \r
6265   if (commentDialog) {\r
6266     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6267     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6268   } else {\r
6269     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6270     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6271                  hwndMain, (DLGPROC)lpProc);\r
6272     FreeProcInstance(lpProc);\r
6273   }\r
6274   commentUp = TRUE;\r
6275 }\r
6276 \r
6277 \r
6278 /*---------------------------------------------------------------------------*\\r
6279  *\r
6280  * Type-in move dialog functions\r
6281  * \r
6282 \*---------------------------------------------------------------------------*/\r
6283 \r
6284 LRESULT CALLBACK\r
6285 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6286 {\r
6287   char move[MSG_SIZ];\r
6288   HWND hInput;\r
6289   ChessMove moveType;\r
6290   int fromX, fromY, toX, toY;\r
6291   char promoChar;\r
6292 \r
6293   switch (message) {\r
6294   case WM_INITDIALOG:\r
6295     move[0] = (char) lParam;\r
6296     move[1] = NULLCHAR;\r
6297     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6298     Translate(hDlg, DLG_TypeInMove);\r
6299     hInput = GetDlgItem(hDlg, OPT_Move);\r
6300     SetWindowText(hInput, move);\r
6301     SetFocus(hInput);\r
6302     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6303     return FALSE;\r
6304 \r
6305   case WM_COMMAND:\r
6306     switch (LOWORD(wParam)) {\r
6307     case IDOK:\r
6308       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6309       { int n; Board board;\r
6310         // [HGM] FENedit\r
6311         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
6312                 EditPositionPasteFEN(move);\r
6313                 EndDialog(hDlg, TRUE);\r
6314                 return TRUE;\r
6315         }\r
6316         // [HGM] movenum: allow move number to be typed in any mode\r
6317         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
6318           ToNrEvent(2*n-1);\r
6319           EndDialog(hDlg, TRUE);\r
6320           return TRUE;\r
6321         }\r
6322       }\r
6323       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6324         gameMode != Training) {\r
6325         DisplayMoveError(_("Displayed move is not current"));\r
6326       } else {\r
6327 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
6328         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6329           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
6330         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
6331         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6332           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6333           if (gameMode != Training)\r
6334               forwardMostMove = currentMove;\r
6335           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6336         } else {\r
6337           DisplayMoveError(_("Could not parse move"));\r
6338         }\r
6339       }\r
6340       EndDialog(hDlg, TRUE);\r
6341       return TRUE;\r
6342     case IDCANCEL:\r
6343       EndDialog(hDlg, FALSE);\r
6344       return TRUE;\r
6345     default:\r
6346       break;\r
6347     }\r
6348     break;\r
6349   }\r
6350   return FALSE;\r
6351 }\r
6352 \r
6353 VOID\r
6354 PopUpMoveDialog(char firstchar)\r
6355 {\r
6356     FARPROC lpProc;\r
6357     \r
6358     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6359         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6360         gameMode == AnalyzeMode || gameMode == EditGame || \r
6361         gameMode == EditPosition || gameMode == IcsExamining ||\r
6362         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6363         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
6364                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
6365                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
6366         gameMode == Training) {\r
6367       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6368       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6369         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6370       FreeProcInstance(lpProc);\r
6371     }\r
6372 }\r
6373 \r
6374 /*---------------------------------------------------------------------------*\\r
6375  *\r
6376  * Type-in name dialog functions\r
6377  * \r
6378 \*---------------------------------------------------------------------------*/\r
6379 \r
6380 LRESULT CALLBACK\r
6381 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6382 {\r
6383   char move[MSG_SIZ];\r
6384   HWND hInput;\r
6385 \r
6386   switch (message) {\r
6387   case WM_INITDIALOG:\r
6388     move[0] = (char) lParam;\r
6389     move[1] = NULLCHAR;\r
6390     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6391     Translate(hDlg, DLG_TypeInName);\r
6392     hInput = GetDlgItem(hDlg, OPT_Name);\r
6393     SetWindowText(hInput, move);\r
6394     SetFocus(hInput);\r
6395     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6396     return FALSE;\r
6397 \r
6398   case WM_COMMAND:\r
6399     switch (LOWORD(wParam)) {\r
6400     case IDOK:\r
6401       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6402       appData.userName = strdup(move);\r
6403       SetUserLogo();\r
6404       SetGameInfo();\r
6405       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6406         sprintf(move, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6407         DisplayTitle(move);\r
6408       }\r
6409 \r
6410 \r
6411       EndDialog(hDlg, TRUE);\r
6412       return TRUE;\r
6413     case IDCANCEL:\r
6414       EndDialog(hDlg, FALSE);\r
6415       return TRUE;\r
6416     default:\r
6417       break;\r
6418     }\r
6419     break;\r
6420   }\r
6421   return FALSE;\r
6422 }\r
6423 \r
6424 VOID\r
6425 PopUpNameDialog(char firstchar)\r
6426 {\r
6427     FARPROC lpProc;\r
6428     \r
6429       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6430       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6431         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6432       FreeProcInstance(lpProc);\r
6433 }\r
6434 \r
6435 /*---------------------------------------------------------------------------*\\r
6436  *\r
6437  *  Error dialogs\r
6438  * \r
6439 \*---------------------------------------------------------------------------*/\r
6440 \r
6441 /* Nonmodal error box */\r
6442 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6443                              WPARAM wParam, LPARAM lParam);\r
6444 \r
6445 VOID\r
6446 ErrorPopUp(char *title, char *content)\r
6447 {\r
6448   FARPROC lpProc;\r
6449   char *p, *q;\r
6450   BOOLEAN modal = hwndMain == NULL;\r
6451 \r
6452   p = content;\r
6453   q = errorMessage;\r
6454   while (*p) {\r
6455     if (*p == '\n') {\r
6456       if (modal) {\r
6457         *q++ = ' ';\r
6458         p++;\r
6459       } else {\r
6460         *q++ = '\r';\r
6461         *q++ = *p++;\r
6462       }\r
6463     } else {\r
6464       *q++ = *p++;\r
6465     }\r
6466   }\r
6467   *q = NULLCHAR;\r
6468   strncpy(errorTitle, title, sizeof(errorTitle));\r
6469   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6470   \r
6471   if (modal) {\r
6472     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6473   } else {\r
6474     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6475     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6476                  hwndMain, (DLGPROC)lpProc);\r
6477     FreeProcInstance(lpProc);\r
6478   }\r
6479 }\r
6480 \r
6481 VOID\r
6482 ErrorPopDown()\r
6483 {\r
6484   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6485   if (errorDialog == NULL) return;\r
6486   DestroyWindow(errorDialog);\r
6487   errorDialog = NULL;\r
6488   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6489 }\r
6490 \r
6491 LRESULT CALLBACK\r
6492 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6493 {\r
6494   HANDLE hwndText;\r
6495   RECT rChild;\r
6496 \r
6497   switch (message) {\r
6498   case WM_INITDIALOG:\r
6499     GetWindowRect(hDlg, &rChild);\r
6500 \r
6501     /*\r
6502     SetWindowPos(hDlg, NULL, rChild.left,\r
6503       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6504       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6505     */\r
6506 \r
6507     /* \r
6508         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6509         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6510         and it doesn't work when you resize the dialog.\r
6511         For now, just give it a default position.\r
6512     */\r
6513     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6514     Translate(hDlg, DLG_Error);\r
6515 \r
6516     errorDialog = hDlg;\r
6517     SetWindowText(hDlg, errorTitle);\r
6518     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6519     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6520     return FALSE;\r
6521 \r
6522   case WM_COMMAND:\r
6523     switch (LOWORD(wParam)) {\r
6524     case IDOK:\r
6525     case IDCANCEL:\r
6526       if (errorDialog == hDlg) errorDialog = NULL;\r
6527       DestroyWindow(hDlg);\r
6528       return TRUE;\r
6529 \r
6530     default:\r
6531       break;\r
6532     }\r
6533     break;\r
6534   }\r
6535   return FALSE;\r
6536 }\r
6537 \r
6538 #ifdef GOTHIC\r
6539 HWND gothicDialog = NULL;\r
6540 \r
6541 LRESULT CALLBACK\r
6542 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6543 {\r
6544   HANDLE hwndText;\r
6545   RECT rChild;\r
6546   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6547 \r
6548   switch (message) {\r
6549   case WM_INITDIALOG:\r
6550     GetWindowRect(hDlg, &rChild);\r
6551 \r
6552     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6553                                                              SWP_NOZORDER);\r
6554 \r
6555     /* \r
6556         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6557         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6558         and it doesn't work when you resize the dialog.\r
6559         For now, just give it a default position.\r
6560     */\r
6561     gothicDialog = hDlg;\r
6562     SetWindowText(hDlg, errorTitle);\r
6563     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6564     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6565     return FALSE;\r
6566 \r
6567   case WM_COMMAND:\r
6568     switch (LOWORD(wParam)) {\r
6569     case IDOK:\r
6570     case IDCANCEL:\r
6571       if (errorDialog == hDlg) errorDialog = NULL;\r
6572       DestroyWindow(hDlg);\r
6573       return TRUE;\r
6574 \r
6575     default:\r
6576       break;\r
6577     }\r
6578     break;\r
6579   }\r
6580   return FALSE;\r
6581 }\r
6582 \r
6583 VOID\r
6584 GothicPopUp(char *title, VariantClass variant)\r
6585 {\r
6586   FARPROC lpProc;\r
6587   static char *lastTitle;\r
6588 \r
6589   strncpy(errorTitle, title, sizeof(errorTitle));\r
6590   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6591 \r
6592   if(lastTitle != title && gothicDialog != NULL) {\r
6593     DestroyWindow(gothicDialog);\r
6594     gothicDialog = NULL;\r
6595   }\r
6596   if(variant != VariantNormal && gothicDialog == NULL) {\r
6597     title = lastTitle;\r
6598     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6599     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6600                  hwndMain, (DLGPROC)lpProc);\r
6601     FreeProcInstance(lpProc);\r
6602   }\r
6603 }\r
6604 #endif\r
6605 \r
6606 /*---------------------------------------------------------------------------*\\r
6607  *\r
6608  *  Ics Interaction console functions\r
6609  *\r
6610 \*---------------------------------------------------------------------------*/\r
6611 \r
6612 #define HISTORY_SIZE 64\r
6613 static char *history[HISTORY_SIZE];\r
6614 int histIn = 0, histP = 0;\r
6615 \r
6616 VOID\r
6617 SaveInHistory(char *cmd)\r
6618 {\r
6619   if (history[histIn] != NULL) {\r
6620     free(history[histIn]);\r
6621     history[histIn] = NULL;\r
6622   }\r
6623   if (*cmd == NULLCHAR) return;\r
6624   history[histIn] = StrSave(cmd);\r
6625   histIn = (histIn + 1) % HISTORY_SIZE;\r
6626   if (history[histIn] != NULL) {\r
6627     free(history[histIn]);\r
6628     history[histIn] = NULL;\r
6629   }\r
6630   histP = histIn;\r
6631 }\r
6632 \r
6633 char *\r
6634 PrevInHistory(char *cmd)\r
6635 {\r
6636   int newhp;\r
6637   if (histP == histIn) {\r
6638     if (history[histIn] != NULL) free(history[histIn]);\r
6639     history[histIn] = StrSave(cmd);\r
6640   }\r
6641   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6642   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6643   histP = newhp;\r
6644   return history[histP];\r
6645 }\r
6646 \r
6647 char *\r
6648 NextInHistory()\r
6649 {\r
6650   if (histP == histIn) return NULL;\r
6651   histP = (histP + 1) % HISTORY_SIZE;\r
6652   return history[histP];   \r
6653 }\r
6654 \r
6655 HMENU\r
6656 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6657 {\r
6658   HMENU hmenu, h;\r
6659   int i = 0;\r
6660   hmenu = LoadMenu(hInst, "TextMenu");\r
6661   h = GetSubMenu(hmenu, 0);\r
6662   while (e->item) {\r
6663     if (strcmp(e->item, "-") == 0) {\r
6664       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6665     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6666       int flags = MF_STRING, j = 0;\r
6667       if (e->item[0] == '|') {\r
6668         flags |= MF_MENUBARBREAK;\r
6669         j++;\r
6670       }\r
6671       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6672       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6673     }\r
6674     e++;\r
6675     i++;\r
6676   } \r
6677   return hmenu;\r
6678 }\r
6679 \r
6680 WNDPROC consoleTextWindowProc;\r
6681 \r
6682 void\r
6683 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6684 {\r
6685   char buf[MSG_SIZ], name[MSG_SIZ];\r
6686   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6687   CHARRANGE sel;\r
6688 \r
6689   if (!getname) {\r
6690     SetWindowText(hInput, command);\r
6691     if (immediate) {\r
6692       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6693     } else {\r
6694       sel.cpMin = 999999;\r
6695       sel.cpMax = 999999;\r
6696       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6697       SetFocus(hInput);\r
6698     }\r
6699     return;\r
6700   }    \r
6701   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6702   if (sel.cpMin == sel.cpMax) {\r
6703     /* Expand to surrounding word */\r
6704     TEXTRANGE tr;\r
6705     do {\r
6706       tr.chrg.cpMax = sel.cpMin;\r
6707       tr.chrg.cpMin = --sel.cpMin;\r
6708       if (sel.cpMin < 0) break;\r
6709       tr.lpstrText = name;\r
6710       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6711     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6712     sel.cpMin++;\r
6713 \r
6714     do {\r
6715       tr.chrg.cpMin = sel.cpMax;\r
6716       tr.chrg.cpMax = ++sel.cpMax;\r
6717       tr.lpstrText = name;\r
6718       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6719     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6720     sel.cpMax--;\r
6721 \r
6722     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6723       MessageBeep(MB_ICONEXCLAMATION);\r
6724       return;\r
6725     }\r
6726     tr.chrg = sel;\r
6727     tr.lpstrText = name;\r
6728     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6729   } else {\r
6730     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6731       MessageBeep(MB_ICONEXCLAMATION);\r
6732       return;\r
6733     }\r
6734     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6735   }\r
6736   if (immediate) {\r
6737     sprintf(buf, "%s %s", command, name);\r
6738     SetWindowText(hInput, buf);\r
6739     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6740   } else {\r
6741     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6742     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
6743     SetWindowText(hInput, buf);\r
6744     sel.cpMin = 999999;\r
6745     sel.cpMax = 999999;\r
6746     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6747     SetFocus(hInput);\r
6748   }\r
6749 }\r
6750 \r
6751 LRESULT CALLBACK \r
6752 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6753 {\r
6754   HWND hInput;\r
6755   CHARRANGE sel;\r
6756 \r
6757   switch (message) {\r
6758   case WM_KEYDOWN:\r
6759     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6760     switch (wParam) {\r
6761     case VK_PRIOR:\r
6762       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6763       return 0;\r
6764     case VK_NEXT:\r
6765       sel.cpMin = 999999;\r
6766       sel.cpMax = 999999;\r
6767       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6768       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6769       return 0;\r
6770     }\r
6771     break;\r
6772   case WM_CHAR:\r
6773    if(wParam != '\022') {\r
6774     if (wParam == '\t') {\r
6775       if (GetKeyState(VK_SHIFT) < 0) {\r
6776         /* shifted */\r
6777         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6778         if (buttonDesc[0].hwnd) {\r
6779           SetFocus(buttonDesc[0].hwnd);\r
6780         } else {\r
6781           SetFocus(hwndMain);\r
6782         }\r
6783       } else {\r
6784         /* unshifted */\r
6785         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6786       }\r
6787     } else {\r
6788       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6789       JAWS_DELETE( SetFocus(hInput); )\r
6790       SendMessage(hInput, message, wParam, lParam);\r
6791     }\r
6792     return 0;\r
6793    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
6794   case WM_RBUTTONDOWN:\r
6795     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6796       /* Move selection here if it was empty */\r
6797       POINT pt;\r
6798       pt.x = LOWORD(lParam);\r
6799       pt.y = HIWORD(lParam);\r
6800       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6801       if (sel.cpMin == sel.cpMax) {\r
6802         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6803         sel.cpMax = sel.cpMin;\r
6804         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6805       }\r
6806       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6807 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6808       POINT pt;\r
6809       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6810       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6811       if (sel.cpMin == sel.cpMax) {\r
6812         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6813         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6814       }\r
6815       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6816         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6817       }\r
6818       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6819       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6820       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6821       MenuPopup(hwnd, pt, hmenu, -1);\r
6822 }\r
6823     }\r
6824     return 0;\r
6825   case WM_RBUTTONUP:\r
6826     if (GetKeyState(VK_SHIFT) & ~1) {\r
6827       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6828         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6829     }\r
6830     return 0;\r
6831   case WM_PASTE:\r
6832     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6833     SetFocus(hInput);\r
6834     return SendMessage(hInput, message, wParam, lParam);\r
6835   case WM_MBUTTONDOWN:\r
6836     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6837   case WM_COMMAND:\r
6838     switch (LOWORD(wParam)) {\r
6839     case IDM_QuickPaste:\r
6840       {\r
6841         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6842         if (sel.cpMin == sel.cpMax) {\r
6843           MessageBeep(MB_ICONEXCLAMATION);\r
6844           return 0;\r
6845         }\r
6846         SendMessage(hwnd, WM_COPY, 0, 0);\r
6847         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6848         SendMessage(hInput, WM_PASTE, 0, 0);\r
6849         SetFocus(hInput);\r
6850         return 0;\r
6851       }\r
6852     case IDM_Cut:\r
6853       SendMessage(hwnd, WM_CUT, 0, 0);\r
6854       return 0;\r
6855     case IDM_Paste:\r
6856       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6857       return 0;\r
6858     case IDM_Copy:\r
6859       SendMessage(hwnd, WM_COPY, 0, 0);\r
6860       return 0;\r
6861     default:\r
6862       {\r
6863         int i = LOWORD(wParam) - IDM_CommandX;\r
6864         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6865             icsTextMenuEntry[i].command != NULL) {\r
6866           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6867                    icsTextMenuEntry[i].getname,\r
6868                    icsTextMenuEntry[i].immediate);\r
6869           return 0;\r
6870         }\r
6871       }\r
6872       break;\r
6873     }\r
6874     break;\r
6875   }\r
6876   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6877 }\r
6878 \r
6879 WNDPROC consoleInputWindowProc;\r
6880 \r
6881 LRESULT CALLBACK\r
6882 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6883 {\r
6884   char buf[MSG_SIZ];\r
6885   char *p;\r
6886   static BOOL sendNextChar = FALSE;\r
6887   static BOOL quoteNextChar = FALSE;\r
6888   InputSource *is = consoleInputSource;\r
6889   CHARFORMAT cf;\r
6890   CHARRANGE sel;\r
6891 \r
6892   switch (message) {\r
6893   case WM_CHAR:\r
6894     if (!appData.localLineEditing || sendNextChar) {\r
6895       is->buf[0] = (CHAR) wParam;\r
6896       is->count = 1;\r
6897       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6898       sendNextChar = FALSE;\r
6899       return 0;\r
6900     }\r
6901     if (quoteNextChar) {\r
6902       buf[0] = (char) wParam;\r
6903       buf[1] = NULLCHAR;\r
6904       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
6905       quoteNextChar = FALSE;\r
6906       return 0;\r
6907     }\r
6908     switch (wParam) {\r
6909     case '\r':   /* Enter key */\r
6910       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
6911       if (consoleEcho) SaveInHistory(is->buf);\r
6912       is->buf[is->count++] = '\n';\r
6913       is->buf[is->count] = NULLCHAR;\r
6914       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6915       if (consoleEcho) {\r
6916         ConsoleOutput(is->buf, is->count, TRUE);\r
6917       } else if (appData.localLineEditing) {\r
6918         ConsoleOutput("\n", 1, TRUE);\r
6919       }\r
6920       /* fall thru */\r
6921     case '\033': /* Escape key */\r
6922       SetWindowText(hwnd, "");\r
6923       cf.cbSize = sizeof(CHARFORMAT);\r
6924       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
6925       if (consoleEcho) {\r
6926         cf.crTextColor = textAttribs[ColorNormal].color;\r
6927       } else {\r
6928         cf.crTextColor = COLOR_ECHOOFF;\r
6929       }\r
6930       cf.dwEffects = textAttribs[ColorNormal].effects;\r
6931       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
6932       return 0;\r
6933     case '\t':   /* Tab key */\r
6934       if (GetKeyState(VK_SHIFT) < 0) {\r
6935         /* shifted */\r
6936         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
6937       } else {\r
6938         /* unshifted */\r
6939         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6940         if (buttonDesc[0].hwnd) {\r
6941           SetFocus(buttonDesc[0].hwnd);\r
6942         } else {\r
6943           SetFocus(hwndMain);\r
6944         }\r
6945       }\r
6946       return 0;\r
6947     case '\023': /* Ctrl+S */\r
6948       sendNextChar = TRUE;\r
6949       return 0;\r
6950     case '\021': /* Ctrl+Q */\r
6951       quoteNextChar = TRUE;\r
6952       return 0;\r
6953     JAWS_REPLAY\r
6954     default:\r
6955       break;\r
6956     }\r
6957     break;\r
6958   case WM_KEYDOWN:\r
6959     switch (wParam) {\r
6960     case VK_UP:\r
6961       GetWindowText(hwnd, buf, MSG_SIZ);\r
6962       p = PrevInHistory(buf);\r
6963       if (p != NULL) {\r
6964         SetWindowText(hwnd, p);\r
6965         sel.cpMin = 999999;\r
6966         sel.cpMax = 999999;\r
6967         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6968         return 0;\r
6969       }\r
6970       break;\r
6971     case VK_DOWN:\r
6972       p = NextInHistory();\r
6973       if (p != NULL) {\r
6974         SetWindowText(hwnd, p);\r
6975         sel.cpMin = 999999;\r
6976         sel.cpMax = 999999;\r
6977         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6978         return 0;\r
6979       }\r
6980       break;\r
6981     case VK_HOME:\r
6982     case VK_END:\r
6983       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6984       /* fall thru */\r
6985     case VK_PRIOR:\r
6986     case VK_NEXT:\r
6987       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
6988       return 0;\r
6989     }\r
6990     break;\r
6991   case WM_MBUTTONDOWN:\r
6992     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6993       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6994     break;\r
6995   case WM_RBUTTONUP:\r
6996     if (GetKeyState(VK_SHIFT) & ~1) {\r
6997       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6998         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6999     } else {\r
7000       POINT pt;\r
7001       HMENU hmenu;\r
7002       hmenu = LoadMenu(hInst, "InputMenu");\r
7003       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7004       if (sel.cpMin == sel.cpMax) {\r
7005         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7006         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7007       }\r
7008       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7009         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7010       }\r
7011       pt.x = LOWORD(lParam);\r
7012       pt.y = HIWORD(lParam);\r
7013       MenuPopup(hwnd, pt, hmenu, -1);\r
7014     }\r
7015     return 0;\r
7016   case WM_COMMAND:\r
7017     switch (LOWORD(wParam)) { \r
7018     case IDM_Undo:\r
7019       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7020       return 0;\r
7021     case IDM_SelectAll:\r
7022       sel.cpMin = 0;\r
7023       sel.cpMax = -1; /*999999?*/\r
7024       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7025       return 0;\r
7026     case IDM_Cut:\r
7027       SendMessage(hwnd, WM_CUT, 0, 0);\r
7028       return 0;\r
7029     case IDM_Paste:\r
7030       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7031       return 0;\r
7032     case IDM_Copy:\r
7033       SendMessage(hwnd, WM_COPY, 0, 0);\r
7034       return 0;\r
7035     }\r
7036     break;\r
7037   }\r
7038   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7039 }\r
7040 \r
7041 #define CO_MAX  100000\r
7042 #define CO_TRIM   1000\r
7043 \r
7044 LRESULT CALLBACK\r
7045 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7046 {\r
7047   static SnapData sd;\r
7048   HWND hText, hInput;\r
7049   RECT rect;\r
7050   static int sizeX, sizeY;\r
7051   int newSizeX, newSizeY;\r
7052   MINMAXINFO *mmi;\r
7053   WORD wMask;\r
7054 \r
7055   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7056   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7057 \r
7058   switch (message) {\r
7059   case WM_NOTIFY:\r
7060     if (((NMHDR*)lParam)->code == EN_LINK)\r
7061     {\r
7062       ENLINK *pLink = (ENLINK*)lParam;\r
7063       if (pLink->msg == WM_LBUTTONUP)\r
7064       {\r
7065         TEXTRANGE tr;\r
7066 \r
7067         tr.chrg = pLink->chrg;\r
7068         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7069         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7070         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7071         free(tr.lpstrText);\r
7072       }\r
7073     }\r
7074     break;\r
7075   case WM_INITDIALOG: /* message: initialize dialog box */\r
7076     hwndConsole = hDlg;\r
7077     SetFocus(hInput);\r
7078     consoleTextWindowProc = (WNDPROC)\r
7079       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7080     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7081     consoleInputWindowProc = (WNDPROC)\r
7082       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7083     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7084     Colorize(ColorNormal, TRUE);\r
7085     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7086     ChangedConsoleFont();\r
7087     GetClientRect(hDlg, &rect);\r
7088     sizeX = rect.right;\r
7089     sizeY = rect.bottom;\r
7090     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7091         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7092       WINDOWPLACEMENT wp;\r
7093       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7094       wp.length = sizeof(WINDOWPLACEMENT);\r
7095       wp.flags = 0;\r
7096       wp.showCmd = SW_SHOW;\r
7097       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7098       wp.rcNormalPosition.left = wpConsole.x;\r
7099       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7100       wp.rcNormalPosition.top = wpConsole.y;\r
7101       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7102       SetWindowPlacement(hDlg, &wp);\r
7103     }\r
7104 \r
7105    // [HGM] Chessknight's change 2004-07-13\r
7106    else { /* Determine Defaults */\r
7107        WINDOWPLACEMENT wp;\r
7108        wpConsole.x = wpMain.width + 1;\r
7109        wpConsole.y = wpMain.y;\r
7110        wpConsole.width = screenWidth -  wpMain.width;\r
7111        wpConsole.height = wpMain.height;\r
7112        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7113        wp.length = sizeof(WINDOWPLACEMENT);\r
7114        wp.flags = 0;\r
7115        wp.showCmd = SW_SHOW;\r
7116        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7117        wp.rcNormalPosition.left = wpConsole.x;\r
7118        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7119        wp.rcNormalPosition.top = wpConsole.y;\r
7120        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7121        SetWindowPlacement(hDlg, &wp);\r
7122     }\r
7123 \r
7124    // Allow hText to highlight URLs and send notifications on them\r
7125    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7126    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7127    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7128    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
7129 \r
7130     return FALSE;\r
7131 \r
7132   case WM_SETFOCUS:\r
7133     SetFocus(hInput);\r
7134     return 0;\r
7135 \r
7136   case WM_CLOSE:\r
7137     ExitEvent(0);\r
7138     /* not reached */\r
7139     break;\r
7140 \r
7141   case WM_SIZE:\r
7142     if (IsIconic(hDlg)) break;\r
7143     newSizeX = LOWORD(lParam);\r
7144     newSizeY = HIWORD(lParam);\r
7145     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7146       RECT rectText, rectInput;\r
7147       POINT pt;\r
7148       int newTextHeight, newTextWidth;\r
7149       GetWindowRect(hText, &rectText);\r
7150       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7151       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7152       if (newTextHeight < 0) {\r
7153         newSizeY += -newTextHeight;\r
7154         newTextHeight = 0;\r
7155       }\r
7156       SetWindowPos(hText, NULL, 0, 0,\r
7157         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7158       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7159       pt.x = rectInput.left;\r
7160       pt.y = rectInput.top + newSizeY - sizeY;\r
7161       ScreenToClient(hDlg, &pt);\r
7162       SetWindowPos(hInput, NULL, \r
7163         pt.x, pt.y, /* needs client coords */   \r
7164         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7165         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7166     }\r
7167     sizeX = newSizeX;\r
7168     sizeY = newSizeY;\r
7169     break;\r
7170 \r
7171   case WM_GETMINMAXINFO:\r
7172     /* Prevent resizing window too small */\r
7173     mmi = (MINMAXINFO *) lParam;\r
7174     mmi->ptMinTrackSize.x = 100;\r
7175     mmi->ptMinTrackSize.y = 100;\r
7176     break;\r
7177 \r
7178   /* [AS] Snapping */\r
7179   case WM_ENTERSIZEMOVE:\r
7180     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7181 \r
7182   case WM_SIZING:\r
7183     return OnSizing( &sd, hDlg, wParam, lParam );\r
7184 \r
7185   case WM_MOVING:\r
7186     return OnMoving( &sd, hDlg, wParam, lParam );\r
7187 \r
7188   case WM_EXITSIZEMOVE:\r
7189         UpdateICSWidth(hText);\r
7190     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7191   }\r
7192 \r
7193   return DefWindowProc(hDlg, message, wParam, lParam);\r
7194 }\r
7195 \r
7196 \r
7197 VOID\r
7198 ConsoleCreate()\r
7199 {\r
7200   HWND hCons;\r
7201   if (hwndConsole) return;\r
7202   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7203   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7204 }\r
7205 \r
7206 \r
7207 VOID\r
7208 ConsoleOutput(char* data, int length, int forceVisible)\r
7209 {\r
7210   HWND hText;\r
7211   int trim, exlen;\r
7212   char *p, *q;\r
7213   char buf[CO_MAX+1];\r
7214   POINT pEnd;\r
7215   RECT rect;\r
7216   static int delayLF = 0;\r
7217   CHARRANGE savesel, sel;\r
7218 \r
7219   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7220   p = data;\r
7221   q = buf;\r
7222   if (delayLF) {\r
7223     *q++ = '\r';\r
7224     *q++ = '\n';\r
7225     delayLF = 0;\r
7226   }\r
7227   while (length--) {\r
7228     if (*p == '\n') {\r
7229       if (*++p) {\r
7230         *q++ = '\r';\r
7231         *q++ = '\n';\r
7232       } else {\r
7233         delayLF = 1;\r
7234       }\r
7235     } else if (*p == '\007') {\r
7236        MyPlaySound(&sounds[(int)SoundBell]);\r
7237        p++;\r
7238     } else {\r
7239       *q++ = *p++;\r
7240     }\r
7241   }\r
7242   *q = NULLCHAR;\r
7243   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7244   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7245   /* Save current selection */\r
7246   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7247   exlen = GetWindowTextLength(hText);\r
7248   /* Find out whether current end of text is visible */\r
7249   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7250   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7251   /* Trim existing text if it's too long */\r
7252   if (exlen + (q - buf) > CO_MAX) {\r
7253     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7254     sel.cpMin = 0;\r
7255     sel.cpMax = trim;\r
7256     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7257     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7258     exlen -= trim;\r
7259     savesel.cpMin -= trim;\r
7260     savesel.cpMax -= trim;\r
7261     if (exlen < 0) exlen = 0;\r
7262     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7263     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7264   }\r
7265   /* Append the new text */\r
7266   sel.cpMin = exlen;\r
7267   sel.cpMax = exlen;\r
7268   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7269   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7270   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7271   if (forceVisible || exlen == 0 ||\r
7272       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7273        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7274     /* Scroll to make new end of text visible if old end of text\r
7275        was visible or new text is an echo of user typein */\r
7276     sel.cpMin = 9999999;\r
7277     sel.cpMax = 9999999;\r
7278     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7279     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7280     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7281     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7282   }\r
7283   if (savesel.cpMax == exlen || forceVisible) {\r
7284     /* Move insert point to new end of text if it was at the old\r
7285        end of text or if the new text is an echo of user typein */\r
7286     sel.cpMin = 9999999;\r
7287     sel.cpMax = 9999999;\r
7288     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7289   } else {\r
7290     /* Restore previous selection */\r
7291     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7292   }\r
7293   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7294 }\r
7295 \r
7296 /*---------*/\r
7297 \r
7298 \r
7299 void\r
7300 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7301 {\r
7302   char buf[100];\r
7303   char *str;\r
7304   COLORREF oldFg, oldBg;\r
7305   HFONT oldFont;\r
7306   RECT rect;\r
7307 \r
7308   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
7309 \r
7310   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7311   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7312   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7313 \r
7314   rect.left = x;\r
7315   rect.right = x + squareSize;\r
7316   rect.top  = y;\r
7317   rect.bottom = y + squareSize;\r
7318   str = buf;\r
7319 \r
7320   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7321                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7322              y, ETO_CLIPPED|ETO_OPAQUE,\r
7323              &rect, str, strlen(str), NULL);\r
7324 \r
7325   (void) SetTextColor(hdc, oldFg);\r
7326   (void) SetBkColor(hdc, oldBg);\r
7327   (void) SelectObject(hdc, oldFont);\r
7328 }\r
7329 \r
7330 void\r
7331 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7332               RECT *rect, char *color, char *flagFell)\r
7333 {\r
7334   char buf[100];\r
7335   char *str;\r
7336   COLORREF oldFg, oldBg;\r
7337   HFONT oldFont;\r
7338 \r
7339   if (appData.clockMode) {\r
7340     if (tinyLayout)\r
7341       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7342     else\r
7343       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7344     str = buf;\r
7345   } else {\r
7346     str = color;\r
7347   }\r
7348 \r
7349   if (highlight) {\r
7350     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7351     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7352   } else {\r
7353     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7354     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7355   }\r
7356   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7357 \r
7358   JAWS_SILENCE\r
7359 \r
7360   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7361              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7362              rect, str, strlen(str), NULL);\r
7363   if(logoHeight > 0 && appData.clockMode) {\r
7364       RECT r;\r
7365       sprintf(buf, "%s %s", buf+7, flagFell);\r
7366       r.top = rect->top + logoHeight/2;\r
7367       r.left = rect->left;\r
7368       r.right = rect->right;\r
7369       r.bottom = rect->bottom;\r
7370       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7371                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7372                  &r, str, strlen(str), NULL);\r
7373   }\r
7374   (void) SetTextColor(hdc, oldFg);\r
7375   (void) SetBkColor(hdc, oldBg);\r
7376   (void) SelectObject(hdc, oldFont);\r
7377 }\r
7378 \r
7379 \r
7380 int\r
7381 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7382            OVERLAPPED *ovl)\r
7383 {\r
7384   int ok, err;\r
7385 \r
7386   /* [AS]  */\r
7387   if( count <= 0 ) {\r
7388     if (appData.debugMode) {\r
7389       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7390     }\r
7391 \r
7392     return ERROR_INVALID_USER_BUFFER;\r
7393   }\r
7394 \r
7395   ResetEvent(ovl->hEvent);\r
7396   ovl->Offset = ovl->OffsetHigh = 0;\r
7397   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7398   if (ok) {\r
7399     err = NO_ERROR;\r
7400   } else {\r
7401     err = GetLastError();\r
7402     if (err == ERROR_IO_PENDING) {\r
7403       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7404       if (ok)\r
7405         err = NO_ERROR;\r
7406       else\r
7407         err = GetLastError();\r
7408     }\r
7409   }\r
7410   return err;\r
7411 }\r
7412 \r
7413 int\r
7414 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7415             OVERLAPPED *ovl)\r
7416 {\r
7417   int ok, err;\r
7418 \r
7419   ResetEvent(ovl->hEvent);\r
7420   ovl->Offset = ovl->OffsetHigh = 0;\r
7421   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7422   if (ok) {\r
7423     err = NO_ERROR;\r
7424   } else {\r
7425     err = GetLastError();\r
7426     if (err == ERROR_IO_PENDING) {\r
7427       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7428       if (ok)\r
7429         err = NO_ERROR;\r
7430       else\r
7431         err = GetLastError();\r
7432     }\r
7433   }\r
7434   return err;\r
7435 }\r
7436 \r
7437 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7438 void CheckForInputBufferFull( InputSource * is )\r
7439 {\r
7440     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7441         /* Look for end of line */\r
7442         char * p = is->buf;\r
7443         \r
7444         while( p < is->next && *p != '\n' ) {\r
7445             p++;\r
7446         }\r
7447 \r
7448         if( p >= is->next ) {\r
7449             if (appData.debugMode) {\r
7450                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7451             }\r
7452 \r
7453             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7454             is->count = (DWORD) -1;\r
7455             is->next = is->buf;\r
7456         }\r
7457     }\r
7458 }\r
7459 \r
7460 DWORD\r
7461 InputThread(LPVOID arg)\r
7462 {\r
7463   InputSource *is;\r
7464   OVERLAPPED ovl;\r
7465 \r
7466   is = (InputSource *) arg;\r
7467   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7468   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7469   while (is->hThread != NULL) {\r
7470     is->error = DoReadFile(is->hFile, is->next,\r
7471                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7472                            &is->count, &ovl);\r
7473     if (is->error == NO_ERROR) {\r
7474       is->next += is->count;\r
7475     } else {\r
7476       if (is->error == ERROR_BROKEN_PIPE) {\r
7477         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7478         is->count = 0;\r
7479       } else {\r
7480         is->count = (DWORD) -1;\r
7481         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7482         break; \r
7483       }\r
7484     }\r
7485 \r
7486     CheckForInputBufferFull( is );\r
7487 \r
7488     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7489 \r
7490     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7491 \r
7492     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7493   }\r
7494 \r
7495   CloseHandle(ovl.hEvent);\r
7496   CloseHandle(is->hFile);\r
7497 \r
7498   if (appData.debugMode) {\r
7499     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7500   }\r
7501 \r
7502   return 0;\r
7503 }\r
7504 \r
7505 \r
7506 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7507 DWORD\r
7508 NonOvlInputThread(LPVOID arg)\r
7509 {\r
7510   InputSource *is;\r
7511   char *p, *q;\r
7512   int i;\r
7513   char prev;\r
7514 \r
7515   is = (InputSource *) arg;\r
7516   while (is->hThread != NULL) {\r
7517     is->error = ReadFile(is->hFile, is->next,\r
7518                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7519                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7520     if (is->error == NO_ERROR) {\r
7521       /* Change CRLF to LF */\r
7522       if (is->next > is->buf) {\r
7523         p = is->next - 1;\r
7524         i = is->count + 1;\r
7525       } else {\r
7526         p = is->next;\r
7527         i = is->count;\r
7528       }\r
7529       q = p;\r
7530       prev = NULLCHAR;\r
7531       while (i > 0) {\r
7532         if (prev == '\r' && *p == '\n') {\r
7533           *(q-1) = '\n';\r
7534           is->count--;\r
7535         } else { \r
7536           *q++ = *p;\r
7537         }\r
7538         prev = *p++;\r
7539         i--;\r
7540       }\r
7541       *q = NULLCHAR;\r
7542       is->next = q;\r
7543     } else {\r
7544       if (is->error == ERROR_BROKEN_PIPE) {\r
7545         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7546         is->count = 0; \r
7547       } else {\r
7548         is->count = (DWORD) -1;\r
7549       }\r
7550     }\r
7551 \r
7552     CheckForInputBufferFull( is );\r
7553 \r
7554     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7555 \r
7556     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7557 \r
7558     if (is->count < 0) break;  /* Quit on error */\r
7559   }\r
7560   CloseHandle(is->hFile);\r
7561   return 0;\r
7562 }\r
7563 \r
7564 DWORD\r
7565 SocketInputThread(LPVOID arg)\r
7566 {\r
7567   InputSource *is;\r
7568 \r
7569   is = (InputSource *) arg;\r
7570   while (is->hThread != NULL) {\r
7571     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7572     if ((int)is->count == SOCKET_ERROR) {\r
7573       is->count = (DWORD) -1;\r
7574       is->error = WSAGetLastError();\r
7575     } else {\r
7576       is->error = NO_ERROR;\r
7577       is->next += is->count;\r
7578       if (is->count == 0 && is->second == is) {\r
7579         /* End of file on stderr; quit with no message */\r
7580         break;\r
7581       }\r
7582     }\r
7583     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7584 \r
7585     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7586 \r
7587     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7588   }\r
7589   return 0;\r
7590 }\r
7591 \r
7592 VOID\r
7593 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7594 {\r
7595   InputSource *is;\r
7596 \r
7597   is = (InputSource *) lParam;\r
7598   if (is->lineByLine) {\r
7599     /* Feed in lines one by one */\r
7600     char *p = is->buf;\r
7601     char *q = p;\r
7602     while (q < is->next) {\r
7603       if (*q++ == '\n') {\r
7604         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7605         p = q;\r
7606       }\r
7607     }\r
7608     \r
7609     /* Move any partial line to the start of the buffer */\r
7610     q = is->buf;\r
7611     while (p < is->next) {\r
7612       *q++ = *p++;\r
7613     }\r
7614     is->next = q;\r
7615 \r
7616     if (is->error != NO_ERROR || is->count == 0) {\r
7617       /* Notify backend of the error.  Note: If there was a partial\r
7618          line at the end, it is not flushed through. */\r
7619       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7620     }\r
7621   } else {\r
7622     /* Feed in the whole chunk of input at once */\r
7623     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7624     is->next = is->buf;\r
7625   }\r
7626 }\r
7627 \r
7628 /*---------------------------------------------------------------------------*\\r
7629  *\r
7630  *  Menu enables. Used when setting various modes.\r
7631  *\r
7632 \*---------------------------------------------------------------------------*/\r
7633 \r
7634 typedef struct {\r
7635   int item;\r
7636   int flags;\r
7637 } Enables;\r
7638 \r
7639 VOID\r
7640 GreyRevert(Boolean grey)\r
7641 { // [HGM] vari: for retracting variations in local mode\r
7642   HMENU hmenu = GetMenu(hwndMain);\r
7643   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7644   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7645 }\r
7646 \r
7647 VOID\r
7648 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7649 {\r
7650   while (enab->item > 0) {\r
7651     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7652     enab++;\r
7653   }\r
7654 }\r
7655 \r
7656 Enables gnuEnables[] = {\r
7657   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7658   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7659   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7660   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7661   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7662   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7663   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7664   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7665   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7666   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7667   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7668   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7669   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7670   { -1, -1 }\r
7671 };\r
7672 \r
7673 Enables icsEnables[] = {\r
7674   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7675   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7676   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7677   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7678   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7679   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7680   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7681   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7682   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7683   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7684   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7685   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7686   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7687   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7688   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7689   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7690   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7691   { -1, -1 }\r
7692 };\r
7693 \r
7694 #if ZIPPY\r
7695 Enables zippyEnables[] = {\r
7696   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7697   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7698   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7699   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7700   { -1, -1 }\r
7701 };\r
7702 #endif\r
7703 \r
7704 Enables ncpEnables[] = {\r
7705   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7706   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7707   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7708   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7709   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7710   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7711   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7712   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7713   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7714   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7715   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7716   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7717   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7718   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7719   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7720   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7721   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7722   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7723   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7724   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7725   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7726   { -1, -1 }\r
7727 };\r
7728 \r
7729 Enables trainingOnEnables[] = {\r
7730   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7731   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7732   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7733   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7734   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7735   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7736   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7737   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7738   { -1, -1 }\r
7739 };\r
7740 \r
7741 Enables trainingOffEnables[] = {\r
7742   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7743   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7744   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7745   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7746   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7747   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7748   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7749   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7750   { -1, -1 }\r
7751 };\r
7752 \r
7753 /* These modify either ncpEnables or gnuEnables */\r
7754 Enables cmailEnables[] = {\r
7755   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7756   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7757   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7758   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7759   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7760   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7761   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7762   { -1, -1 }\r
7763 };\r
7764 \r
7765 Enables machineThinkingEnables[] = {\r
7766   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7767   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7768   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7769   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7770   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7771   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7772   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7773   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7774   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7775   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7776   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7777   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7778   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7779   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7780   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7781   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7782   { -1, -1 }\r
7783 };\r
7784 \r
7785 Enables userThinkingEnables[] = {\r
7786   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7787   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7788   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7789   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7790   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7791   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7792   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7793   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7794   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7795   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7796   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7797   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7798   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7799   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7800   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7801   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7802   { -1, -1 }\r
7803 };\r
7804 \r
7805 /*---------------------------------------------------------------------------*\\r
7806  *\r
7807  *  Front-end interface functions exported by XBoard.\r
7808  *  Functions appear in same order as prototypes in frontend.h.\r
7809  * \r
7810 \*---------------------------------------------------------------------------*/\r
7811 VOID\r
7812 ModeHighlight()\r
7813 {\r
7814   static UINT prevChecked = 0;\r
7815   static int prevPausing = 0;\r
7816   UINT nowChecked;\r
7817 \r
7818   if (pausing != prevPausing) {\r
7819     prevPausing = pausing;\r
7820     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7821                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7822     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7823   }\r
7824 \r
7825   switch (gameMode) {\r
7826   case BeginningOfGame:\r
7827     if (appData.icsActive)\r
7828       nowChecked = IDM_IcsClient;\r
7829     else if (appData.noChessProgram)\r
7830       nowChecked = IDM_EditGame;\r
7831     else\r
7832       nowChecked = IDM_MachineBlack;\r
7833     break;\r
7834   case MachinePlaysBlack:\r
7835     nowChecked = IDM_MachineBlack;\r
7836     break;\r
7837   case MachinePlaysWhite:\r
7838     nowChecked = IDM_MachineWhite;\r
7839     break;\r
7840   case TwoMachinesPlay:\r
7841     nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match\r
7842     break;\r
7843   case AnalyzeMode:\r
7844     nowChecked = IDM_AnalysisMode;\r
7845     break;\r
7846   case AnalyzeFile:\r
7847     nowChecked = IDM_AnalyzeFile;\r
7848     break;\r
7849   case EditGame:\r
7850     nowChecked = IDM_EditGame;\r
7851     break;\r
7852   case PlayFromGameFile:\r
7853     nowChecked = IDM_LoadGame;\r
7854     break;\r
7855   case EditPosition:\r
7856     nowChecked = IDM_EditPosition;\r
7857     break;\r
7858   case Training:\r
7859     nowChecked = IDM_Training;\r
7860     break;\r
7861   case IcsPlayingWhite:\r
7862   case IcsPlayingBlack:\r
7863   case IcsObserving:\r
7864   case IcsIdle:\r
7865     nowChecked = IDM_IcsClient;\r
7866     break;\r
7867   default:\r
7868   case EndOfGame:\r
7869     nowChecked = 0;\r
7870     break;\r
7871   }\r
7872   if (prevChecked != 0)\r
7873     (void) CheckMenuItem(GetMenu(hwndMain),\r
7874                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7875   if (nowChecked != 0)\r
7876     (void) CheckMenuItem(GetMenu(hwndMain),\r
7877                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
7878 \r
7879   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7880     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7881                           MF_BYCOMMAND|MF_ENABLED);\r
7882   } else {\r
7883     (void) EnableMenuItem(GetMenu(hwndMain), \r
7884                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7885   }\r
7886 \r
7887   prevChecked = nowChecked;\r
7888 \r
7889   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
7890   if (appData.icsActive) {\r
7891        if (appData.icsEngineAnalyze) {\r
7892                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7893                        MF_BYCOMMAND|MF_CHECKED);\r
7894        } else {\r
7895                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7896                        MF_BYCOMMAND|MF_UNCHECKED);\r
7897        }\r
7898   }\r
7899 }\r
7900 \r
7901 VOID\r
7902 SetICSMode()\r
7903 {\r
7904   HMENU hmenu = GetMenu(hwndMain);\r
7905   SetMenuEnables(hmenu, icsEnables);\r
7906   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
7907     MF_BYPOSITION|MF_ENABLED);\r
7908 #if ZIPPY\r
7909   if (appData.zippyPlay) {\r
7910     SetMenuEnables(hmenu, zippyEnables);\r
7911     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
7912          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7913           MF_BYCOMMAND|MF_ENABLED);\r
7914   }\r
7915 #endif\r
7916 }\r
7917 \r
7918 VOID\r
7919 SetGNUMode()\r
7920 {\r
7921   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
7922 }\r
7923 \r
7924 VOID\r
7925 SetNCPMode()\r
7926 {\r
7927   HMENU hmenu = GetMenu(hwndMain);\r
7928   SetMenuEnables(hmenu, ncpEnables);\r
7929   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
7930     MF_BYPOSITION|MF_GRAYED);\r
7931     DrawMenuBar(hwndMain);\r
7932 }\r
7933 \r
7934 VOID\r
7935 SetCmailMode()\r
7936 {\r
7937   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
7938 }\r
7939 \r
7940 VOID \r
7941 SetTrainingModeOn()\r
7942 {\r
7943   int i;\r
7944   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
7945   for (i = 0; i < N_BUTTONS; i++) {\r
7946     if (buttonDesc[i].hwnd != NULL)\r
7947       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
7948   }\r
7949   CommentPopDown();\r
7950 }\r
7951 \r
7952 VOID SetTrainingModeOff()\r
7953 {\r
7954   int i;\r
7955   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
7956   for (i = 0; i < N_BUTTONS; i++) {\r
7957     if (buttonDesc[i].hwnd != NULL)\r
7958       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
7959   }\r
7960 }\r
7961 \r
7962 \r
7963 VOID\r
7964 SetUserThinkingEnables()\r
7965 {\r
7966   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
7967 }\r
7968 \r
7969 VOID\r
7970 SetMachineThinkingEnables()\r
7971 {\r
7972   HMENU hMenu = GetMenu(hwndMain);\r
7973   int flags = MF_BYCOMMAND|MF_ENABLED;\r
7974 \r
7975   SetMenuEnables(hMenu, machineThinkingEnables);\r
7976 \r
7977   if (gameMode == MachinePlaysBlack) {\r
7978     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
7979   } else if (gameMode == MachinePlaysWhite) {\r
7980     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
7981   } else if (gameMode == TwoMachinesPlay) {\r
7982     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
7983   }\r
7984 }\r
7985 \r
7986 \r
7987 VOID\r
7988 DisplayTitle(char *str)\r
7989 {\r
7990   char title[MSG_SIZ], *host;\r
7991   if (str[0] != NULLCHAR) {\r
7992     strcpy(title, str);\r
7993   } else if (appData.icsActive) {\r
7994     if (appData.icsCommPort[0] != NULLCHAR)\r
7995       host = "ICS";\r
7996     else \r
7997       host = appData.icsHost;\r
7998     sprintf(title, "%s: %s", szTitle, host);\r
7999   } else if (appData.noChessProgram) {\r
8000     strcpy(title, szTitle);\r
8001   } else {\r
8002     strcpy(title, szTitle);\r
8003     strcat(title, ": ");\r
8004     strcat(title, first.tidy);\r
8005   }\r
8006   SetWindowText(hwndMain, title);\r
8007 }\r
8008 \r
8009 \r
8010 VOID\r
8011 DisplayMessage(char *str1, char *str2)\r
8012 {\r
8013   HDC hdc;\r
8014   HFONT oldFont;\r
8015   int remain = MESSAGE_TEXT_MAX - 1;\r
8016   int len;\r
8017 \r
8018   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8019   messageText[0] = NULLCHAR;\r
8020   if (*str1) {\r
8021     len = strlen(str1);\r
8022     if (len > remain) len = remain;\r
8023     strncpy(messageText, str1, len);\r
8024     messageText[len] = NULLCHAR;\r
8025     remain -= len;\r
8026   }\r
8027   if (*str2 && remain >= 2) {\r
8028     if (*str1) {\r
8029       strcat(messageText, "  ");\r
8030       remain -= 2;\r
8031     }\r
8032     len = strlen(str2);\r
8033     if (len > remain) len = remain;\r
8034     strncat(messageText, str2, len);\r
8035   }\r
8036   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8037 \r
8038   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8039 \r
8040   SAYMACHINEMOVE();\r
8041 \r
8042   hdc = GetDC(hwndMain);\r
8043   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8044   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8045              &messageRect, messageText, strlen(messageText), NULL);\r
8046   (void) SelectObject(hdc, oldFont);\r
8047   (void) ReleaseDC(hwndMain, hdc);\r
8048 }\r
8049 \r
8050 VOID\r
8051 DisplayError(char *str, int error)\r
8052 {\r
8053   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8054   int len;\r
8055 \r
8056   if (error == 0) {\r
8057     strcpy(buf, str);\r
8058   } else {\r
8059     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8060                         NULL, error, LANG_NEUTRAL,\r
8061                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8062     if (len > 0) {\r
8063       sprintf(buf, "%s:\n%s", str, buf2);\r
8064     } else {\r
8065       ErrorMap *em = errmap;\r
8066       while (em->err != 0 && em->err != error) em++;\r
8067       if (em->err != 0) {\r
8068         sprintf(buf, "%s:\n%s", str, em->msg);\r
8069       } else {\r
8070         sprintf(buf, "%s:\nError code %d", str, error);\r
8071       }\r
8072     }\r
8073   }\r
8074   \r
8075   ErrorPopUp(_("Error"), buf);\r
8076 }\r
8077 \r
8078 \r
8079 VOID\r
8080 DisplayMoveError(char *str)\r
8081 {\r
8082   fromX = fromY = -1;\r
8083   ClearHighlights();\r
8084   DrawPosition(FALSE, NULL);\r
8085   if (appData.popupMoveErrors) {\r
8086     ErrorPopUp(_("Error"), str);\r
8087   } else {\r
8088     DisplayMessage(str, "");\r
8089     moveErrorMessageUp = TRUE;\r
8090   }\r
8091 }\r
8092 \r
8093 VOID\r
8094 DisplayFatalError(char *str, int error, int exitStatus)\r
8095 {\r
8096   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8097   int len;\r
8098   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8099 \r
8100   if (error != 0) {\r
8101     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8102                         NULL, error, LANG_NEUTRAL,\r
8103                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8104     if (len > 0) {\r
8105       sprintf(buf, "%s:\n%s", str, buf2);\r
8106     } else {\r
8107       ErrorMap *em = errmap;\r
8108       while (em->err != 0 && em->err != error) em++;\r
8109       if (em->err != 0) {\r
8110         sprintf(buf, "%s:\n%s", str, em->msg);\r
8111       } else {\r
8112         sprintf(buf, "%s:\nError code %d", str, error);\r
8113       }\r
8114     }\r
8115     str = buf;\r
8116   }\r
8117   if (appData.debugMode) {\r
8118     fprintf(debugFP, "%s: %s\n", label, str);\r
8119   }\r
8120   if (appData.popupExitMessage) {\r
8121     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8122                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8123   }\r
8124   ExitEvent(exitStatus);\r
8125 }\r
8126 \r
8127 \r
8128 VOID\r
8129 DisplayInformation(char *str)\r
8130 {\r
8131   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8132 }\r
8133 \r
8134 \r
8135 VOID\r
8136 DisplayNote(char *str)\r
8137 {\r
8138   ErrorPopUp(_("Note"), str);\r
8139 }\r
8140 \r
8141 \r
8142 typedef struct {\r
8143   char *title, *question, *replyPrefix;\r
8144   ProcRef pr;\r
8145 } QuestionParams;\r
8146 \r
8147 LRESULT CALLBACK\r
8148 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8149 {\r
8150   static QuestionParams *qp;\r
8151   char reply[MSG_SIZ];\r
8152   int len, err;\r
8153 \r
8154   switch (message) {\r
8155   case WM_INITDIALOG:\r
8156     qp = (QuestionParams *) lParam;\r
8157     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8158     Translate(hDlg, DLG_Question);\r
8159     SetWindowText(hDlg, qp->title);\r
8160     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8161     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8162     return FALSE;\r
8163 \r
8164   case WM_COMMAND:\r
8165     switch (LOWORD(wParam)) {\r
8166     case IDOK:\r
8167       strcpy(reply, qp->replyPrefix);\r
8168       if (*reply) strcat(reply, " ");\r
8169       len = strlen(reply);\r
8170       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8171       strcat(reply, "\n");\r
8172       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8173       EndDialog(hDlg, TRUE);\r
8174       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8175       return TRUE;\r
8176     case IDCANCEL:\r
8177       EndDialog(hDlg, FALSE);\r
8178       return TRUE;\r
8179     default:\r
8180       break;\r
8181     }\r
8182     break;\r
8183   }\r
8184   return FALSE;\r
8185 }\r
8186 \r
8187 VOID\r
8188 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8189 {\r
8190     QuestionParams qp;\r
8191     FARPROC lpProc;\r
8192     \r
8193     qp.title = title;\r
8194     qp.question = question;\r
8195     qp.replyPrefix = replyPrefix;\r
8196     qp.pr = pr;\r
8197     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8198     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8199       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8200     FreeProcInstance(lpProc);\r
8201 }\r
8202 \r
8203 /* [AS] Pick FRC position */\r
8204 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8205 {\r
8206     static int * lpIndexFRC;\r
8207     BOOL index_is_ok;\r
8208     char buf[16];\r
8209 \r
8210     switch( message )\r
8211     {\r
8212     case WM_INITDIALOG:\r
8213         lpIndexFRC = (int *) lParam;\r
8214 \r
8215         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8216         Translate(hDlg, DLG_NewGameFRC);\r
8217 \r
8218         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8219         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8220         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8221         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8222 \r
8223         break;\r
8224 \r
8225     case WM_COMMAND:\r
8226         switch( LOWORD(wParam) ) {\r
8227         case IDOK:\r
8228             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8229             EndDialog( hDlg, 0 );\r
8230             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8231             return TRUE;\r
8232         case IDCANCEL:\r
8233             EndDialog( hDlg, 1 );   \r
8234             return TRUE;\r
8235         case IDC_NFG_Edit:\r
8236             if( HIWORD(wParam) == EN_CHANGE ) {\r
8237                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8238 \r
8239                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8240             }\r
8241             return TRUE;\r
8242         case IDC_NFG_Random:\r
8243             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8244             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8245             return TRUE;\r
8246         }\r
8247 \r
8248         break;\r
8249     }\r
8250 \r
8251     return FALSE;\r
8252 }\r
8253 \r
8254 int NewGameFRC()\r
8255 {\r
8256     int result;\r
8257     int index = appData.defaultFrcPosition;\r
8258     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8259 \r
8260     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8261 \r
8262     if( result == 0 ) {\r
8263         appData.defaultFrcPosition = index;\r
8264     }\r
8265 \r
8266     return result;\r
8267 }\r
8268 \r
8269 /* [AS] Game list options. Refactored by HGM */\r
8270 \r
8271 HWND gameListOptionsDialog;\r
8272 \r
8273 // low-level front-end: clear text edit / list widget\r
8274 void\r
8275 GLT_ClearList()\r
8276 {\r
8277     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8278 }\r
8279 \r
8280 // low-level front-end: clear text edit / list widget\r
8281 void\r
8282 GLT_DeSelectList()\r
8283 {\r
8284     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8285 }\r
8286 \r
8287 // low-level front-end: append line to text edit / list widget\r
8288 void\r
8289 GLT_AddToList( char *name )\r
8290 {\r
8291     if( name != 0 ) {\r
8292             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8293     }\r
8294 }\r
8295 \r
8296 // low-level front-end: get line from text edit / list widget\r
8297 Boolean\r
8298 GLT_GetFromList( int index, char *name )\r
8299 {\r
8300     if( name != 0 ) {\r
8301             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8302                 return TRUE;\r
8303     }\r
8304     return FALSE;\r
8305 }\r
8306 \r
8307 void GLT_MoveSelection( HWND hDlg, int delta )\r
8308 {\r
8309     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8310     int idx2 = idx1 + delta;\r
8311     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8312 \r
8313     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8314         char buf[128];\r
8315 \r
8316         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8317         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8318         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8319         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8320     }\r
8321 }\r
8322 \r
8323 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8324 {\r
8325     switch( message )\r
8326     {\r
8327     case WM_INITDIALOG:\r
8328         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8329         \r
8330         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8331         Translate(hDlg, DLG_GameListOptions);\r
8332 \r
8333         /* Initialize list */\r
8334         GLT_TagsToList( lpUserGLT );\r
8335 \r
8336         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8337 \r
8338         break;\r
8339 \r
8340     case WM_COMMAND:\r
8341         switch( LOWORD(wParam) ) {\r
8342         case IDOK:\r
8343             GLT_ParseList();\r
8344             EndDialog( hDlg, 0 );\r
8345             return TRUE;\r
8346         case IDCANCEL:\r
8347             EndDialog( hDlg, 1 );\r
8348             return TRUE;\r
8349 \r
8350         case IDC_GLT_Default:\r
8351             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8352             return TRUE;\r
8353 \r
8354         case IDC_GLT_Restore:\r
8355             GLT_TagsToList( appData.gameListTags );\r
8356             return TRUE;\r
8357 \r
8358         case IDC_GLT_Up:\r
8359             GLT_MoveSelection( hDlg, -1 );\r
8360             return TRUE;\r
8361 \r
8362         case IDC_GLT_Down:\r
8363             GLT_MoveSelection( hDlg, +1 );\r
8364             return TRUE;\r
8365         }\r
8366 \r
8367         break;\r
8368     }\r
8369 \r
8370     return FALSE;\r
8371 }\r
8372 \r
8373 int GameListOptions()\r
8374 {\r
8375     int result;\r
8376     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8377 \r
8378     strcpy( lpUserGLT, appData.gameListTags );\r
8379 \r
8380     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8381 \r
8382     if( result == 0 ) {\r
8383         /* [AS] Memory leak here! */\r
8384         appData.gameListTags = strdup( lpUserGLT ); \r
8385     }\r
8386 \r
8387     return result;\r
8388 }\r
8389 \r
8390 VOID\r
8391 DisplayIcsInteractionTitle(char *str)\r
8392 {\r
8393   char consoleTitle[MSG_SIZ];\r
8394 \r
8395   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
8396   SetWindowText(hwndConsole, consoleTitle);\r
8397 }\r
8398 \r
8399 void\r
8400 DrawPosition(int fullRedraw, Board board)\r
8401 {\r
8402   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8403 }\r
8404 \r
8405 void NotifyFrontendLogin()\r
8406 {\r
8407         if (hwndConsole)\r
8408                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8409 }\r
8410 \r
8411 VOID\r
8412 ResetFrontEnd()\r
8413 {\r
8414   fromX = fromY = -1;\r
8415   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8416     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8417     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8418     dragInfo.lastpos = dragInfo.pos;\r
8419     dragInfo.start.x = dragInfo.start.y = -1;\r
8420     dragInfo.from = dragInfo.start;\r
8421     ReleaseCapture();\r
8422     DrawPosition(TRUE, NULL);\r
8423   }\r
8424   TagsPopDown();\r
8425 }\r
8426 \r
8427 \r
8428 VOID\r
8429 CommentPopUp(char *title, char *str)\r
8430 {\r
8431   HWND hwnd = GetActiveWindow();\r
8432   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8433   SAY(str);\r
8434   SetActiveWindow(hwnd);\r
8435 }\r
8436 \r
8437 VOID\r
8438 CommentPopDown(void)\r
8439 {\r
8440   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
8441   if (commentDialog) {\r
8442     ShowWindow(commentDialog, SW_HIDE);\r
8443   }\r
8444   commentUp = FALSE;\r
8445 }\r
8446 \r
8447 VOID\r
8448 EditCommentPopUp(int index, char *title, char *str)\r
8449 {\r
8450   EitherCommentPopUp(index, title, str, TRUE);\r
8451 }\r
8452 \r
8453 \r
8454 VOID\r
8455 RingBell()\r
8456 {\r
8457   MyPlaySound(&sounds[(int)SoundMove]);\r
8458 }\r
8459 \r
8460 VOID PlayIcsWinSound()\r
8461 {\r
8462   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8463 }\r
8464 \r
8465 VOID PlayIcsLossSound()\r
8466 {\r
8467   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8468 }\r
8469 \r
8470 VOID PlayIcsDrawSound()\r
8471 {\r
8472   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8473 }\r
8474 \r
8475 VOID PlayIcsUnfinishedSound()\r
8476 {\r
8477   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8478 }\r
8479 \r
8480 VOID\r
8481 PlayAlarmSound()\r
8482 {\r
8483   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8484 }\r
8485 \r
8486 \r
8487 VOID\r
8488 EchoOn()\r
8489 {\r
8490   HWND hInput;\r
8491   consoleEcho = TRUE;\r
8492   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8493   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8494   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8495 }\r
8496 \r
8497 \r
8498 VOID\r
8499 EchoOff()\r
8500 {\r
8501   CHARFORMAT cf;\r
8502   HWND hInput;\r
8503   consoleEcho = FALSE;\r
8504   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8505   /* This works OK: set text and background both to the same color */\r
8506   cf = consoleCF;\r
8507   cf.crTextColor = COLOR_ECHOOFF;\r
8508   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8509   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8510 }\r
8511 \r
8512 /* No Raw()...? */\r
8513 \r
8514 void Colorize(ColorClass cc, int continuation)\r
8515 {\r
8516   currentColorClass = cc;\r
8517   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8518   consoleCF.crTextColor = textAttribs[cc].color;\r
8519   consoleCF.dwEffects = textAttribs[cc].effects;\r
8520   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8521 }\r
8522 \r
8523 char *\r
8524 UserName()\r
8525 {\r
8526   static char buf[MSG_SIZ];\r
8527   DWORD bufsiz = MSG_SIZ;\r
8528 \r
8529   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8530         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8531   }\r
8532   if (!GetUserName(buf, &bufsiz)) {\r
8533     /*DisplayError("Error getting user name", GetLastError());*/\r
8534     strcpy(buf, _("User"));\r
8535   }\r
8536   return buf;\r
8537 }\r
8538 \r
8539 char *\r
8540 HostName()\r
8541 {\r
8542   static char buf[MSG_SIZ];\r
8543   DWORD bufsiz = MSG_SIZ;\r
8544 \r
8545   if (!GetComputerName(buf, &bufsiz)) {\r
8546     /*DisplayError("Error getting host name", GetLastError());*/\r
8547     strcpy(buf, _("Unknown"));\r
8548   }\r
8549   return buf;\r
8550 }\r
8551 \r
8552 \r
8553 int\r
8554 ClockTimerRunning()\r
8555 {\r
8556   return clockTimerEvent != 0;\r
8557 }\r
8558 \r
8559 int\r
8560 StopClockTimer()\r
8561 {\r
8562   if (clockTimerEvent == 0) return FALSE;\r
8563   KillTimer(hwndMain, clockTimerEvent);\r
8564   clockTimerEvent = 0;\r
8565   return TRUE;\r
8566 }\r
8567 \r
8568 void\r
8569 StartClockTimer(long millisec)\r
8570 {\r
8571   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8572                              (UINT) millisec, NULL);\r
8573 }\r
8574 \r
8575 void\r
8576 DisplayWhiteClock(long timeRemaining, int highlight)\r
8577 {\r
8578   HDC hdc;\r
8579   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8580 \r
8581   if(appData.noGUI) return;\r
8582   hdc = GetDC(hwndMain);\r
8583   if (!IsIconic(hwndMain)) {\r
8584     DisplayAClock(hdc, timeRemaining, highlight, \r
8585                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8586   }\r
8587   if (highlight && iconCurrent == iconBlack) {\r
8588     iconCurrent = iconWhite;\r
8589     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8590     if (IsIconic(hwndMain)) {\r
8591       DrawIcon(hdc, 2, 2, iconCurrent);\r
8592     }\r
8593   }\r
8594   (void) ReleaseDC(hwndMain, hdc);\r
8595   if (hwndConsole)\r
8596     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8597 }\r
8598 \r
8599 void\r
8600 DisplayBlackClock(long timeRemaining, int highlight)\r
8601 {\r
8602   HDC hdc;\r
8603   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8604 \r
8605   if(appData.noGUI) return;\r
8606   hdc = GetDC(hwndMain);\r
8607   if (!IsIconic(hwndMain)) {\r
8608     DisplayAClock(hdc, timeRemaining, highlight, \r
8609                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8610   }\r
8611   if (highlight && iconCurrent == iconWhite) {\r
8612     iconCurrent = iconBlack;\r
8613     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8614     if (IsIconic(hwndMain)) {\r
8615       DrawIcon(hdc, 2, 2, iconCurrent);\r
8616     }\r
8617   }\r
8618   (void) ReleaseDC(hwndMain, hdc);\r
8619   if (hwndConsole)\r
8620     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8621 }\r
8622 \r
8623 \r
8624 int\r
8625 LoadGameTimerRunning()\r
8626 {\r
8627   return loadGameTimerEvent != 0;\r
8628 }\r
8629 \r
8630 int\r
8631 StopLoadGameTimer()\r
8632 {\r
8633   if (loadGameTimerEvent == 0) return FALSE;\r
8634   KillTimer(hwndMain, loadGameTimerEvent);\r
8635   loadGameTimerEvent = 0;\r
8636   return TRUE;\r
8637 }\r
8638 \r
8639 void\r
8640 StartLoadGameTimer(long millisec)\r
8641 {\r
8642   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8643                                 (UINT) millisec, NULL);\r
8644 }\r
8645 \r
8646 void\r
8647 AutoSaveGame()\r
8648 {\r
8649   char *defName;\r
8650   FILE *f;\r
8651   char fileTitle[MSG_SIZ];\r
8652 \r
8653   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8654   f = OpenFileDialog(hwndMain, "a", defName,\r
8655                      appData.oldSaveStyle ? "gam" : "pgn",\r
8656                      GAME_FILT, \r
8657                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8658   if (f != NULL) {\r
8659     SaveGame(f, 0, "");\r
8660     fclose(f);\r
8661   }\r
8662 }\r
8663 \r
8664 \r
8665 void\r
8666 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8667 {\r
8668   if (delayedTimerEvent != 0) {\r
8669     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8670       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8671     }\r
8672     KillTimer(hwndMain, delayedTimerEvent);\r
8673     delayedTimerEvent = 0;\r
8674     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8675     delayedTimerCallback();\r
8676   }\r
8677   delayedTimerCallback = cb;\r
8678   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8679                                 (UINT) millisec, NULL);\r
8680 }\r
8681 \r
8682 DelayedEventCallback\r
8683 GetDelayedEvent()\r
8684 {\r
8685   if (delayedTimerEvent) {\r
8686     return delayedTimerCallback;\r
8687   } else {\r
8688     return NULL;\r
8689   }\r
8690 }\r
8691 \r
8692 void\r
8693 CancelDelayedEvent()\r
8694 {\r
8695   if (delayedTimerEvent) {\r
8696     KillTimer(hwndMain, delayedTimerEvent);\r
8697     delayedTimerEvent = 0;\r
8698   }\r
8699 }\r
8700 \r
8701 DWORD GetWin32Priority(int nice)\r
8702 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8703 /*\r
8704 REALTIME_PRIORITY_CLASS     0x00000100\r
8705 HIGH_PRIORITY_CLASS         0x00000080\r
8706 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8707 NORMAL_PRIORITY_CLASS       0x00000020\r
8708 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8709 IDLE_PRIORITY_CLASS         0x00000040\r
8710 */\r
8711         if (nice < -15) return 0x00000080;\r
8712         if (nice < 0)   return 0x00008000;\r
8713         if (nice == 0)  return 0x00000020;\r
8714         if (nice < 15)  return 0x00004000;\r
8715         return 0x00000040;\r
8716 }\r
8717 \r
8718 /* Start a child process running the given program.\r
8719    The process's standard output can be read from "from", and its\r
8720    standard input can be written to "to".\r
8721    Exit with fatal error if anything goes wrong.\r
8722    Returns an opaque pointer that can be used to destroy the process\r
8723    later.\r
8724 */\r
8725 int\r
8726 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8727 {\r
8728 #define BUFSIZE 4096\r
8729 \r
8730   HANDLE hChildStdinRd, hChildStdinWr,\r
8731     hChildStdoutRd, hChildStdoutWr;\r
8732   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8733   SECURITY_ATTRIBUTES saAttr;\r
8734   BOOL fSuccess;\r
8735   PROCESS_INFORMATION piProcInfo;\r
8736   STARTUPINFO siStartInfo;\r
8737   ChildProc *cp;\r
8738   char buf[MSG_SIZ];\r
8739   DWORD err;\r
8740 \r
8741   if (appData.debugMode) {\r
8742     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8743   }\r
8744 \r
8745   *pr = NoProc;\r
8746 \r
8747   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8748   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8749   saAttr.bInheritHandle = TRUE;\r
8750   saAttr.lpSecurityDescriptor = NULL;\r
8751 \r
8752   /*\r
8753    * The steps for redirecting child's STDOUT:\r
8754    *     1. Create anonymous pipe to be STDOUT for child.\r
8755    *     2. Create a noninheritable duplicate of read handle,\r
8756    *         and close the inheritable read handle.\r
8757    */\r
8758 \r
8759   /* Create a pipe for the child's STDOUT. */\r
8760   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8761     return GetLastError();\r
8762   }\r
8763 \r
8764   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8765   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8766                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8767                              FALSE,     /* not inherited */\r
8768                              DUPLICATE_SAME_ACCESS);\r
8769   if (! fSuccess) {\r
8770     return GetLastError();\r
8771   }\r
8772   CloseHandle(hChildStdoutRd);\r
8773 \r
8774   /*\r
8775    * The steps for redirecting child's STDIN:\r
8776    *     1. Create anonymous pipe to be STDIN for child.\r
8777    *     2. Create a noninheritable duplicate of write handle,\r
8778    *         and close the inheritable write handle.\r
8779    */\r
8780 \r
8781   /* Create a pipe for the child's STDIN. */\r
8782   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8783     return GetLastError();\r
8784   }\r
8785 \r
8786   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8787   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8788                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8789                              FALSE,     /* not inherited */\r
8790                              DUPLICATE_SAME_ACCESS);\r
8791   if (! fSuccess) {\r
8792     return GetLastError();\r
8793   }\r
8794   CloseHandle(hChildStdinWr);\r
8795 \r
8796   /* Arrange to (1) look in dir for the child .exe file, and\r
8797    * (2) have dir be the child's working directory.  Interpret\r
8798    * dir relative to the directory WinBoard loaded from. */\r
8799   GetCurrentDirectory(MSG_SIZ, buf);\r
8800   SetCurrentDirectory(installDir);\r
8801   SetCurrentDirectory(dir);\r
8802 \r
8803   /* Now create the child process. */\r
8804 \r
8805   siStartInfo.cb = sizeof(STARTUPINFO);\r
8806   siStartInfo.lpReserved = NULL;\r
8807   siStartInfo.lpDesktop = NULL;\r
8808   siStartInfo.lpTitle = NULL;\r
8809   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8810   siStartInfo.cbReserved2 = 0;\r
8811   siStartInfo.lpReserved2 = NULL;\r
8812   siStartInfo.hStdInput = hChildStdinRd;\r
8813   siStartInfo.hStdOutput = hChildStdoutWr;\r
8814   siStartInfo.hStdError = hChildStdoutWr;\r
8815 \r
8816   fSuccess = CreateProcess(NULL,\r
8817                            cmdLine,        /* command line */\r
8818                            NULL,           /* process security attributes */\r
8819                            NULL,           /* primary thread security attrs */\r
8820                            TRUE,           /* handles are inherited */\r
8821                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8822                            NULL,           /* use parent's environment */\r
8823                            NULL,\r
8824                            &siStartInfo, /* STARTUPINFO pointer */\r
8825                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8826 \r
8827   err = GetLastError();\r
8828   SetCurrentDirectory(buf); /* return to prev directory */\r
8829   if (! fSuccess) {\r
8830     return err;\r
8831   }\r
8832 \r
8833   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8834     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8835     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8836   }\r
8837 \r
8838   /* Close the handles we don't need in the parent */\r
8839   CloseHandle(piProcInfo.hThread);\r
8840   CloseHandle(hChildStdinRd);\r
8841   CloseHandle(hChildStdoutWr);\r
8842 \r
8843   /* Prepare return value */\r
8844   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8845   cp->kind = CPReal;\r
8846   cp->hProcess = piProcInfo.hProcess;\r
8847   cp->pid = piProcInfo.dwProcessId;\r
8848   cp->hFrom = hChildStdoutRdDup;\r
8849   cp->hTo = hChildStdinWrDup;\r
8850 \r
8851   *pr = (void *) cp;\r
8852 \r
8853   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8854      2000 where engines sometimes don't see the initial command(s)\r
8855      from WinBoard and hang.  I don't understand how that can happen,\r
8856      but the Sleep is harmless, so I've put it in.  Others have also\r
8857      reported what may be the same problem, so hopefully this will fix\r
8858      it for them too.  */\r
8859   Sleep(500);\r
8860 \r
8861   return NO_ERROR;\r
8862 }\r
8863 \r
8864 \r
8865 void\r
8866 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8867 {\r
8868   ChildProc *cp; int result;\r
8869 \r
8870   cp = (ChildProc *) pr;\r
8871   if (cp == NULL) return;\r
8872 \r
8873   switch (cp->kind) {\r
8874   case CPReal:\r
8875     /* TerminateProcess is considered harmful, so... */\r
8876     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8877     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8878     /* The following doesn't work because the chess program\r
8879        doesn't "have the same console" as WinBoard.  Maybe\r
8880        we could arrange for this even though neither WinBoard\r
8881        nor the chess program uses a console for stdio? */\r
8882     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8883 \r
8884     /* [AS] Special termination modes for misbehaving programs... */\r
8885     if( signal == 9 ) { \r
8886         result = TerminateProcess( cp->hProcess, 0 );\r
8887 \r
8888         if ( appData.debugMode) {\r
8889             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
8890         }\r
8891     }\r
8892     else if( signal == 10 ) {\r
8893         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
8894 \r
8895         if( dw != WAIT_OBJECT_0 ) {\r
8896             result = TerminateProcess( cp->hProcess, 0 );\r
8897 \r
8898             if ( appData.debugMode) {\r
8899                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
8900             }\r
8901 \r
8902         }\r
8903     }\r
8904 \r
8905     CloseHandle(cp->hProcess);\r
8906     break;\r
8907 \r
8908   case CPComm:\r
8909     if (cp->hFrom) CloseHandle(cp->hFrom);\r
8910     break;\r
8911 \r
8912   case CPSock:\r
8913     closesocket(cp->sock);\r
8914     WSACleanup();\r
8915     break;\r
8916 \r
8917   case CPRcmd:\r
8918     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
8919     closesocket(cp->sock);\r
8920     closesocket(cp->sock2);\r
8921     WSACleanup();\r
8922     break;\r
8923   }\r
8924   free(cp);\r
8925 }\r
8926 \r
8927 void\r
8928 InterruptChildProcess(ProcRef pr)\r
8929 {\r
8930   ChildProc *cp;\r
8931 \r
8932   cp = (ChildProc *) pr;\r
8933   if (cp == NULL) return;\r
8934   switch (cp->kind) {\r
8935   case CPReal:\r
8936     /* The following doesn't work because the chess program\r
8937        doesn't "have the same console" as WinBoard.  Maybe\r
8938        we could arrange for this even though neither WinBoard\r
8939        nor the chess program uses a console for stdio */\r
8940     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
8941     break;\r
8942 \r
8943   case CPComm:\r
8944   case CPSock:\r
8945     /* Can't interrupt */\r
8946     break;\r
8947 \r
8948   case CPRcmd:\r
8949     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
8950     break;\r
8951   }\r
8952 }\r
8953 \r
8954 \r
8955 int\r
8956 OpenTelnet(char *host, char *port, ProcRef *pr)\r
8957 {\r
8958   char cmdLine[MSG_SIZ];\r
8959 \r
8960   if (port[0] == NULLCHAR) {\r
8961     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
8962   } else {\r
8963     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
8964   }\r
8965   return StartChildProcess(cmdLine, "", pr);\r
8966 }\r
8967 \r
8968 \r
8969 /* Code to open TCP sockets */\r
8970 \r
8971 int\r
8972 OpenTCP(char *host, char *port, ProcRef *pr)\r
8973 {\r
8974   ChildProc *cp;\r
8975   int err;\r
8976   SOCKET s;\r
8977   struct sockaddr_in sa, mysa;\r
8978   struct hostent FAR *hp;\r
8979   unsigned short uport;\r
8980   WORD wVersionRequested;\r
8981   WSADATA wsaData;\r
8982 \r
8983   /* Initialize socket DLL */\r
8984   wVersionRequested = MAKEWORD(1, 1);\r
8985   err = WSAStartup(wVersionRequested, &wsaData);\r
8986   if (err != 0) return err;\r
8987 \r
8988   /* Make socket */\r
8989   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
8990     err = WSAGetLastError();\r
8991     WSACleanup();\r
8992     return err;\r
8993   }\r
8994 \r
8995   /* Bind local address using (mostly) don't-care values.\r
8996    */\r
8997   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
8998   mysa.sin_family = AF_INET;\r
8999   mysa.sin_addr.s_addr = INADDR_ANY;\r
9000   uport = (unsigned short) 0;\r
9001   mysa.sin_port = htons(uport);\r
9002   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9003       == SOCKET_ERROR) {\r
9004     err = WSAGetLastError();\r
9005     WSACleanup();\r
9006     return err;\r
9007   }\r
9008 \r
9009   /* Resolve remote host name */\r
9010   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9011   if (!(hp = gethostbyname(host))) {\r
9012     unsigned int b0, b1, b2, b3;\r
9013 \r
9014     err = WSAGetLastError();\r
9015 \r
9016     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9017       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9018       hp->h_addrtype = AF_INET;\r
9019       hp->h_length = 4;\r
9020       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9021       hp->h_addr_list[0] = (char *) malloc(4);\r
9022       hp->h_addr_list[0][0] = (char) b0;\r
9023       hp->h_addr_list[0][1] = (char) b1;\r
9024       hp->h_addr_list[0][2] = (char) b2;\r
9025       hp->h_addr_list[0][3] = (char) b3;\r
9026     } else {\r
9027       WSACleanup();\r
9028       return err;\r
9029     }\r
9030   }\r
9031   sa.sin_family = hp->h_addrtype;\r
9032   uport = (unsigned short) atoi(port);\r
9033   sa.sin_port = htons(uport);\r
9034   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9035 \r
9036   /* Make connection */\r
9037   if (connect(s, (struct sockaddr *) &sa,\r
9038               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9039     err = WSAGetLastError();\r
9040     WSACleanup();\r
9041     return err;\r
9042   }\r
9043 \r
9044   /* Prepare return value */\r
9045   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9046   cp->kind = CPSock;\r
9047   cp->sock = s;\r
9048   *pr = (ProcRef *) cp;\r
9049 \r
9050   return NO_ERROR;\r
9051 }\r
9052 \r
9053 int\r
9054 OpenCommPort(char *name, ProcRef *pr)\r
9055 {\r
9056   HANDLE h;\r
9057   COMMTIMEOUTS ct;\r
9058   ChildProc *cp;\r
9059   char fullname[MSG_SIZ];\r
9060 \r
9061   if (*name != '\\')\r
9062     sprintf(fullname, "\\\\.\\%s", name);\r
9063   else\r
9064     strcpy(fullname, name);\r
9065 \r
9066   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9067                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9068   if (h == (HANDLE) -1) {\r
9069     return GetLastError();\r
9070   }\r
9071   hCommPort = h;\r
9072 \r
9073   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9074 \r
9075   /* Accumulate characters until a 100ms pause, then parse */\r
9076   ct.ReadIntervalTimeout = 100;\r
9077   ct.ReadTotalTimeoutMultiplier = 0;\r
9078   ct.ReadTotalTimeoutConstant = 0;\r
9079   ct.WriteTotalTimeoutMultiplier = 0;\r
9080   ct.WriteTotalTimeoutConstant = 0;\r
9081   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9082 \r
9083   /* Prepare return value */\r
9084   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9085   cp->kind = CPComm;\r
9086   cp->hFrom = h;\r
9087   cp->hTo = h;\r
9088   *pr = (ProcRef *) cp;\r
9089 \r
9090   return NO_ERROR;\r
9091 }\r
9092 \r
9093 int\r
9094 OpenLoopback(ProcRef *pr)\r
9095 {\r
9096   DisplayFatalError(_("Not implemented"), 0, 1);\r
9097   return NO_ERROR;\r
9098 }\r
9099 \r
9100 \r
9101 int\r
9102 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9103 {\r
9104   ChildProc *cp;\r
9105   int err;\r
9106   SOCKET s, s2, s3;\r
9107   struct sockaddr_in sa, mysa;\r
9108   struct hostent FAR *hp;\r
9109   unsigned short uport;\r
9110   WORD wVersionRequested;\r
9111   WSADATA wsaData;\r
9112   int fromPort;\r
9113   char stderrPortStr[MSG_SIZ];\r
9114 \r
9115   /* Initialize socket DLL */\r
9116   wVersionRequested = MAKEWORD(1, 1);\r
9117   err = WSAStartup(wVersionRequested, &wsaData);\r
9118   if (err != 0) return err;\r
9119 \r
9120   /* Resolve remote host name */\r
9121   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9122   if (!(hp = gethostbyname(host))) {\r
9123     unsigned int b0, b1, b2, b3;\r
9124 \r
9125     err = WSAGetLastError();\r
9126 \r
9127     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9128       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9129       hp->h_addrtype = AF_INET;\r
9130       hp->h_length = 4;\r
9131       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9132       hp->h_addr_list[0] = (char *) malloc(4);\r
9133       hp->h_addr_list[0][0] = (char) b0;\r
9134       hp->h_addr_list[0][1] = (char) b1;\r
9135       hp->h_addr_list[0][2] = (char) b2;\r
9136       hp->h_addr_list[0][3] = (char) b3;\r
9137     } else {\r
9138       WSACleanup();\r
9139       return err;\r
9140     }\r
9141   }\r
9142   sa.sin_family = hp->h_addrtype;\r
9143   uport = (unsigned short) 514;\r
9144   sa.sin_port = htons(uport);\r
9145   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9146 \r
9147   /* Bind local socket to unused "privileged" port address\r
9148    */\r
9149   s = INVALID_SOCKET;\r
9150   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9151   mysa.sin_family = AF_INET;\r
9152   mysa.sin_addr.s_addr = INADDR_ANY;\r
9153   for (fromPort = 1023;; fromPort--) {\r
9154     if (fromPort < 0) {\r
9155       WSACleanup();\r
9156       return WSAEADDRINUSE;\r
9157     }\r
9158     if (s == INVALID_SOCKET) {\r
9159       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9160         err = WSAGetLastError();\r
9161         WSACleanup();\r
9162         return err;\r
9163       }\r
9164     }\r
9165     uport = (unsigned short) fromPort;\r
9166     mysa.sin_port = htons(uport);\r
9167     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9168         == SOCKET_ERROR) {\r
9169       err = WSAGetLastError();\r
9170       if (err == WSAEADDRINUSE) continue;\r
9171       WSACleanup();\r
9172       return err;\r
9173     }\r
9174     if (connect(s, (struct sockaddr *) &sa,\r
9175       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9176       err = WSAGetLastError();\r
9177       if (err == WSAEADDRINUSE) {\r
9178         closesocket(s);\r
9179         s = -1;\r
9180         continue;\r
9181       }\r
9182       WSACleanup();\r
9183       return err;\r
9184     }\r
9185     break;\r
9186   }\r
9187 \r
9188   /* Bind stderr local socket to unused "privileged" port address\r
9189    */\r
9190   s2 = INVALID_SOCKET;\r
9191   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9192   mysa.sin_family = AF_INET;\r
9193   mysa.sin_addr.s_addr = INADDR_ANY;\r
9194   for (fromPort = 1023;; fromPort--) {\r
9195     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9196     if (fromPort < 0) {\r
9197       (void) closesocket(s);\r
9198       WSACleanup();\r
9199       return WSAEADDRINUSE;\r
9200     }\r
9201     if (s2 == INVALID_SOCKET) {\r
9202       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9203         err = WSAGetLastError();\r
9204         closesocket(s);\r
9205         WSACleanup();\r
9206         return err;\r
9207       }\r
9208     }\r
9209     uport = (unsigned short) fromPort;\r
9210     mysa.sin_port = htons(uport);\r
9211     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9212         == SOCKET_ERROR) {\r
9213       err = WSAGetLastError();\r
9214       if (err == WSAEADDRINUSE) continue;\r
9215       (void) closesocket(s);\r
9216       WSACleanup();\r
9217       return err;\r
9218     }\r
9219     if (listen(s2, 1) == SOCKET_ERROR) {\r
9220       err = WSAGetLastError();\r
9221       if (err == WSAEADDRINUSE) {\r
9222         closesocket(s2);\r
9223         s2 = INVALID_SOCKET;\r
9224         continue;\r
9225       }\r
9226       (void) closesocket(s);\r
9227       (void) closesocket(s2);\r
9228       WSACleanup();\r
9229       return err;\r
9230     }\r
9231     break;\r
9232   }\r
9233   prevStderrPort = fromPort; // remember port used\r
9234   sprintf(stderrPortStr, "%d", fromPort);\r
9235 \r
9236   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9237     err = WSAGetLastError();\r
9238     (void) closesocket(s);\r
9239     (void) closesocket(s2);\r
9240     WSACleanup();\r
9241     return err;\r
9242   }\r
9243 \r
9244   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9245     err = WSAGetLastError();\r
9246     (void) closesocket(s);\r
9247     (void) closesocket(s2);\r
9248     WSACleanup();\r
9249     return err;\r
9250   }\r
9251   if (*user == NULLCHAR) user = UserName();\r
9252   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9253     err = WSAGetLastError();\r
9254     (void) closesocket(s);\r
9255     (void) closesocket(s2);\r
9256     WSACleanup();\r
9257     return err;\r
9258   }\r
9259   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9260     err = WSAGetLastError();\r
9261     (void) closesocket(s);\r
9262     (void) closesocket(s2);\r
9263     WSACleanup();\r
9264     return err;\r
9265   }\r
9266 \r
9267   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9268     err = WSAGetLastError();\r
9269     (void) closesocket(s);\r
9270     (void) closesocket(s2);\r
9271     WSACleanup();\r
9272     return err;\r
9273   }\r
9274   (void) closesocket(s2);  /* Stop listening */\r
9275 \r
9276   /* Prepare return value */\r
9277   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9278   cp->kind = CPRcmd;\r
9279   cp->sock = s;\r
9280   cp->sock2 = s3;\r
9281   *pr = (ProcRef *) cp;\r
9282 \r
9283   return NO_ERROR;\r
9284 }\r
9285 \r
9286 \r
9287 InputSourceRef\r
9288 AddInputSource(ProcRef pr, int lineByLine,\r
9289                InputCallback func, VOIDSTAR closure)\r
9290 {\r
9291   InputSource *is, *is2 = NULL;\r
9292   ChildProc *cp = (ChildProc *) pr;\r
9293 \r
9294   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9295   is->lineByLine = lineByLine;\r
9296   is->func = func;\r
9297   is->closure = closure;\r
9298   is->second = NULL;\r
9299   is->next = is->buf;\r
9300   if (pr == NoProc) {\r
9301     is->kind = CPReal;\r
9302     consoleInputSource = is;\r
9303   } else {\r
9304     is->kind = cp->kind;\r
9305     /* \r
9306         [AS] Try to avoid a race condition if the thread is given control too early:\r
9307         we create all threads suspended so that the is->hThread variable can be\r
9308         safely assigned, then let the threads start with ResumeThread.\r
9309     */\r
9310     switch (cp->kind) {\r
9311     case CPReal:\r
9312       is->hFile = cp->hFrom;\r
9313       cp->hFrom = NULL; /* now owned by InputThread */\r
9314       is->hThread =\r
9315         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9316                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9317       break;\r
9318 \r
9319     case CPComm:\r
9320       is->hFile = cp->hFrom;\r
9321       cp->hFrom = NULL; /* now owned by InputThread */\r
9322       is->hThread =\r
9323         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9324                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9325       break;\r
9326 \r
9327     case CPSock:\r
9328       is->sock = cp->sock;\r
9329       is->hThread =\r
9330         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9331                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9332       break;\r
9333 \r
9334     case CPRcmd:\r
9335       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9336       *is2 = *is;\r
9337       is->sock = cp->sock;\r
9338       is->second = is2;\r
9339       is2->sock = cp->sock2;\r
9340       is2->second = is2;\r
9341       is->hThread =\r
9342         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9343                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9344       is2->hThread =\r
9345         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9346                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9347       break;\r
9348     }\r
9349 \r
9350     if( is->hThread != NULL ) {\r
9351         ResumeThread( is->hThread );\r
9352     }\r
9353 \r
9354     if( is2 != NULL && is2->hThread != NULL ) {\r
9355         ResumeThread( is2->hThread );\r
9356     }\r
9357   }\r
9358 \r
9359   return (InputSourceRef) is;\r
9360 }\r
9361 \r
9362 void\r
9363 RemoveInputSource(InputSourceRef isr)\r
9364 {\r
9365   InputSource *is;\r
9366 \r
9367   is = (InputSource *) isr;\r
9368   is->hThread = NULL;  /* tell thread to stop */\r
9369   CloseHandle(is->hThread);\r
9370   if (is->second != NULL) {\r
9371     is->second->hThread = NULL;\r
9372     CloseHandle(is->second->hThread);\r
9373   }\r
9374 }\r
9375 \r
9376 int no_wrap(char *message, int count)\r
9377 {\r
9378     ConsoleOutput(message, count, FALSE);\r
9379     return count;\r
9380 }\r
9381 \r
9382 int\r
9383 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9384 {\r
9385   DWORD dOutCount;\r
9386   int outCount = SOCKET_ERROR;\r
9387   ChildProc *cp = (ChildProc *) pr;\r
9388   static OVERLAPPED ovl;\r
9389   static int line = 0;\r
9390 \r
9391   if (pr == NoProc)\r
9392   {\r
9393     if (appData.noJoin || !appData.useInternalWrap)\r
9394       return no_wrap(message, count);\r
9395     else\r
9396     {\r
9397       int width = get_term_width();\r
9398       int len = wrap(NULL, message, count, width, &line);\r
9399       char *msg = malloc(len);\r
9400       int dbgchk;\r
9401 \r
9402       if (!msg)\r
9403         return no_wrap(message, count);\r
9404       else\r
9405       {\r
9406         dbgchk = wrap(msg, message, count, width, &line);\r
9407         if (dbgchk != len && appData.debugMode)\r
9408             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9409         ConsoleOutput(msg, len, FALSE);\r
9410         free(msg);\r
9411         return len;\r
9412       }\r
9413     }\r
9414   }\r
9415 \r
9416   if (ovl.hEvent == NULL) {\r
9417     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9418   }\r
9419   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9420 \r
9421   switch (cp->kind) {\r
9422   case CPSock:\r
9423   case CPRcmd:\r
9424     outCount = send(cp->sock, message, count, 0);\r
9425     if (outCount == SOCKET_ERROR) {\r
9426       *outError = WSAGetLastError();\r
9427     } else {\r
9428       *outError = NO_ERROR;\r
9429     }\r
9430     break;\r
9431 \r
9432   case CPReal:\r
9433     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9434                   &dOutCount, NULL)) {\r
9435       *outError = NO_ERROR;\r
9436       outCount = (int) dOutCount;\r
9437     } else {\r
9438       *outError = GetLastError();\r
9439     }\r
9440     break;\r
9441 \r
9442   case CPComm:\r
9443     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9444                             &dOutCount, &ovl);\r
9445     if (*outError == NO_ERROR) {\r
9446       outCount = (int) dOutCount;\r
9447     }\r
9448     break;\r
9449   }\r
9450   return outCount;\r
9451 }\r
9452 \r
9453 int\r
9454 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9455                        long msdelay)\r
9456 {\r
9457   /* Ignore delay, not implemented for WinBoard */\r
9458   return OutputToProcess(pr, message, count, outError);\r
9459 }\r
9460 \r
9461 \r
9462 void\r
9463 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9464                         char *buf, int count, int error)\r
9465 {\r
9466   DisplayFatalError(_("Not implemented"), 0, 1);\r
9467 }\r
9468 \r
9469 /* see wgamelist.c for Game List functions */\r
9470 /* see wedittags.c for Edit Tags functions */\r
9471 \r
9472 \r
9473 VOID\r
9474 ICSInitScript()\r
9475 {\r
9476   FILE *f;\r
9477   char buf[MSG_SIZ];\r
9478   char *dummy;\r
9479 \r
9480   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9481     f = fopen(buf, "r");\r
9482     if (f != NULL) {\r
9483       ProcessICSInitScript(f);\r
9484       fclose(f);\r
9485     }\r
9486   }\r
9487 }\r
9488 \r
9489 \r
9490 VOID\r
9491 StartAnalysisClock()\r
9492 {\r
9493   if (analysisTimerEvent) return;\r
9494   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9495                                         (UINT) 2000, NULL);\r
9496 }\r
9497 \r
9498 VOID\r
9499 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9500 {\r
9501   highlightInfo.sq[0].x = fromX;\r
9502   highlightInfo.sq[0].y = fromY;\r
9503   highlightInfo.sq[1].x = toX;\r
9504   highlightInfo.sq[1].y = toY;\r
9505 }\r
9506 \r
9507 VOID\r
9508 ClearHighlights()\r
9509 {\r
9510   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9511     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9512 }\r
9513 \r
9514 VOID\r
9515 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9516 {\r
9517   premoveHighlightInfo.sq[0].x = fromX;\r
9518   premoveHighlightInfo.sq[0].y = fromY;\r
9519   premoveHighlightInfo.sq[1].x = toX;\r
9520   premoveHighlightInfo.sq[1].y = toY;\r
9521 }\r
9522 \r
9523 VOID\r
9524 ClearPremoveHighlights()\r
9525 {\r
9526   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9527     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9528 }\r
9529 \r
9530 VOID\r
9531 ShutDownFrontEnd()\r
9532 {\r
9533   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9534   DeleteClipboardTempFiles();\r
9535 }\r
9536 \r
9537 void\r
9538 BoardToTop()\r
9539 {\r
9540     if (IsIconic(hwndMain))\r
9541       ShowWindow(hwndMain, SW_RESTORE);\r
9542 \r
9543     SetActiveWindow(hwndMain);\r
9544 }\r
9545 \r
9546 /*\r
9547  * Prototypes for animation support routines\r
9548  */\r
9549 static void ScreenSquare(int column, int row, POINT * pt);\r
9550 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9551      POINT frames[], int * nFrames);\r
9552 \r
9553 \r
9554 void\r
9555 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
9556 {       // [HGM] atomic: animate blast wave\r
9557         int i;\r
9558 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
9559         explodeInfo.fromX = fromX;\r
9560         explodeInfo.fromY = fromY;\r
9561         explodeInfo.toX = toX;\r
9562         explodeInfo.toY = toY;\r
9563         for(i=1; i<nFrames; i++) {\r
9564             explodeInfo.radius = (i*180)/(nFrames-1);\r
9565             DrawPosition(FALSE, NULL);\r
9566             Sleep(appData.animSpeed);\r
9567         }\r
9568         explodeInfo.radius = 0;\r
9569         DrawPosition(TRUE, NULL);\r
9570 }\r
9571 \r
9572 #define kFactor 4\r
9573 \r
9574 void\r
9575 AnimateMove(board, fromX, fromY, toX, toY)\r
9576      Board board;\r
9577      int fromX;\r
9578      int fromY;\r
9579      int toX;\r
9580      int toY;\r
9581 {\r
9582   ChessSquare piece;\r
9583   POINT start, finish, mid;\r
9584   POINT frames[kFactor * 2 + 1];\r
9585   int nFrames, n;\r
9586 \r
9587   if (!appData.animate) return;\r
9588   if (doingSizing) return;\r
9589   if (fromY < 0 || fromX < 0) return;\r
9590   piece = board[fromY][fromX];\r
9591   if (piece >= EmptySquare) return;\r
9592 \r
9593   ScreenSquare(fromX, fromY, &start);\r
9594   ScreenSquare(toX, toY, &finish);\r
9595 \r
9596   /* All pieces except knights move in straight line */\r
9597   if (piece != WhiteKnight && piece != BlackKnight) {\r
9598     mid.x = start.x + (finish.x - start.x) / 2;\r
9599     mid.y = start.y + (finish.y - start.y) / 2;\r
9600   } else {\r
9601     /* Knight: make diagonal movement then straight */\r
9602     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9603        mid.x = start.x + (finish.x - start.x) / 2;\r
9604        mid.y = finish.y;\r
9605      } else {\r
9606        mid.x = finish.x;\r
9607        mid.y = start.y + (finish.y - start.y) / 2;\r
9608      }\r
9609   }\r
9610   \r
9611   /* Don't use as many frames for very short moves */\r
9612   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9613     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9614   else\r
9615     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9616 \r
9617   animInfo.from.x = fromX;\r
9618   animInfo.from.y = fromY;\r
9619   animInfo.to.x = toX;\r
9620   animInfo.to.y = toY;\r
9621   animInfo.lastpos = start;\r
9622   animInfo.piece = piece;\r
9623   for (n = 0; n < nFrames; n++) {\r
9624     animInfo.pos = frames[n];\r
9625     DrawPosition(FALSE, NULL);\r
9626     animInfo.lastpos = animInfo.pos;\r
9627     Sleep(appData.animSpeed);\r
9628   }\r
9629   animInfo.pos = finish;\r
9630   DrawPosition(FALSE, NULL);\r
9631   animInfo.piece = EmptySquare;\r
9632   if(gameInfo.variant == VariantAtomic && \r
9633      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
9634         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
9635 }\r
9636 \r
9637 /*      Convert board position to corner of screen rect and color       */\r
9638 \r
9639 static void\r
9640 ScreenSquare(column, row, pt)\r
9641      int column; int row; POINT * pt;\r
9642 {\r
9643   if (flipView) {\r
9644     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9645     pt->y = lineGap + row * (squareSize + lineGap);\r
9646   } else {\r
9647     pt->x = lineGap + column * (squareSize + lineGap);\r
9648     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9649   }\r
9650 }\r
9651 \r
9652 /*      Generate a series of frame coords from start->mid->finish.\r
9653         The movement rate doubles until the half way point is\r
9654         reached, then halves back down to the final destination,\r
9655         which gives a nice slow in/out effect. The algorithmn\r
9656         may seem to generate too many intermediates for short\r
9657         moves, but remember that the purpose is to attract the\r
9658         viewers attention to the piece about to be moved and\r
9659         then to where it ends up. Too few frames would be less\r
9660         noticeable.                                             */\r
9661 \r
9662 static void\r
9663 Tween(start, mid, finish, factor, frames, nFrames)\r
9664      POINT * start; POINT * mid;\r
9665      POINT * finish; int factor;\r
9666      POINT frames[]; int * nFrames;\r
9667 {\r
9668   int n, fraction = 1, count = 0;\r
9669 \r
9670   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9671   for (n = 0; n < factor; n++)\r
9672     fraction *= 2;\r
9673   for (n = 0; n < factor; n++) {\r
9674     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9675     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9676     count ++;\r
9677     fraction = fraction / 2;\r
9678   }\r
9679   \r
9680   /* Midpoint */\r
9681   frames[count] = *mid;\r
9682   count ++;\r
9683   \r
9684   /* Slow out, stepping 1/2, then 1/4, ... */\r
9685   fraction = 2;\r
9686   for (n = 0; n < factor; n++) {\r
9687     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9688     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9689     count ++;\r
9690     fraction = fraction * 2;\r
9691   }\r
9692   *nFrames = count;\r
9693 }\r
9694 \r
9695 void\r
9696 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9697 {\r
9698     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9699 \r
9700     EvalGraphSet( first, last, current, pvInfoList );\r
9701 }\r