Add some forgotten translation hooks
[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,\r
281   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
282   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
283   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
284   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
285   OPT_HighlightMoveArrow, OPT_AutoLogo }, \r
286 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
287   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
288   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
289   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
290   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
291   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
292   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
293   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
294   GPB_General, GPB_Alarm }, \r
295 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
296   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
297   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
298   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
299   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
300   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
301   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
302   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size }, \r
303 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
304   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
305   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
306   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
307   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
308   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
309   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat,\r
310   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
311   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
312 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
313   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
314   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,\r
315   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
316 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
317 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
318   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
319 { DLG_MoveHistory }, \r
320 { DLG_EvalGraph }, \r
321 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
322 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
323 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
324   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
325   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
326   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
327 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
328   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
329   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
330 { 0 }\r
331 };\r
332 \r
333 char languageBuf[40000], *foreign[1000], *english[1000];\r
334 \r
335 void\r
336 LoadLanguageFile(char *name)\r
337 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
338     FILE *f;\r
339     int i=0, j=0, n=0, k;\r
340     static char oldLanguage[MSG_SIZ];\r
341     if(!strcmp(name, oldLanguage)) return;\r
342     if(!name || name[0] == NULLCHAR) return;\r
343     if((f = fopen(name, "r")) == NULL) return;\r
344     while((k = fgetc(f)) != EOF) {\r
345         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
346         languageBuf[i] = k;\r
347         if(k == '\n') {\r
348             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
349                 char *p;\r
350                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
351                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
352                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
353                         english[j] = languageBuf + n + 1; *p = 0;\r
354                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
355 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
356                     }\r
357                 }\r
358             }\r
359             n = i + 1;\r
360         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
361             switch(k) {\r
362               case 'n': k = '\n'; break;\r
363               case 'r': k = '\r'; break;\r
364               case 't': k = '\t'; break;\r
365             }\r
366             languageBuf[--i] = k;\r
367         }\r
368         i++;\r
369     }\r
370     fclose(f);\r
371     barbaric = (j != 0);\r
372     strcpy(oldLanguage, buf);\r
373 }\r
374 \r
375 char *\r
376 T_(char *s)\r
377 {   // return the translation of the given string\r
378     // efficiency can be improved a lot...\r
379     int i=0;\r
380 if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
381     if(!barbaric) return s;\r
382     if(!s) return ""; // sanity\r
383     while(english[i]) {\r
384         if(!strcmp(s, english[i])) return foreign[i];\r
385         i++;\r
386     }\r
387     return s;\r
388 }\r
389 \r
390 void\r
391 Translate(HWND hDlg, int dialogID)\r
392 {   // translate all text items in the given dialog\r
393     int i=0, j, k;\r
394     char buf[MSG_SIZ], *s;\r
395 //if(appData.debugMode) fprintf(debugFP, "Translate(%d)\n", dialogID);\r
396     if(!barbaric) return;\r
397     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
398     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
399     GetWindowText( hDlg, buf, MSG_SIZ );\r
400     s = T_(buf);\r
401 if(appData.debugMode) fprintf(debugFP, "WindowText '%s' -> '%s'\n", buf, s);\r
402     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
403     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
404         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
405         if(strlen(buf) == 0) continue;\r
406         s = T_(buf);\r
407         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
408     }\r
409 }\r
410 \r
411 void\r
412 TranslateMenus()\r
413 {\r
414     int i, j;\r
415     if(barbaric) {\r
416         HMENU mainMenu = GetMenu(hwndMain);\r
417         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
418           HMENU subMenu = GetSubMenu(mainMenu, i);\r
419           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
420             char buf[MSG_SIZ];\r
421             UINT k = GetMenuItemID(subMenu, j);\r
422             GetMenuString(subMenu, j, buf, MSG_SIZ, MF_BYPOSITION);\r
423             if(buf[0] == NULLCHAR) continue;\r
424 //fprintf(debugFP, "menu(%d,%d) = %s (%08x, %08x) %d\n", i, j, buf, mainMenu, subMenu, k);\r
425             ModifyMenu(subMenu, j, MF_STRING|MF_BYPOSITION, \r
426             k, T_(buf));\r
427           }\r
428         }\r
429     \r
430     }\r
431 }\r
432 \r
433 #endif\r
434 \r
435 typedef struct {\r
436   char *name;\r
437   int squareSize;\r
438   int lineGap;\r
439   int smallLayout;\r
440   int tinyLayout;\r
441   int cliWidth, cliHeight;\r
442 } SizeInfo;\r
443 \r
444 SizeInfo sizeInfo[] = \r
445 {\r
446   { "tiny",     21, 0, 1, 1, 0, 0 },\r
447   { "teeny",    25, 1, 1, 1, 0, 0 },\r
448   { "dinky",    29, 1, 1, 1, 0, 0 },\r
449   { "petite",   33, 1, 1, 1, 0, 0 },\r
450   { "slim",     37, 2, 1, 0, 0, 0 },\r
451   { "small",    40, 2, 1, 0, 0, 0 },\r
452   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
453   { "middling", 49, 2, 0, 0, 0, 0 },\r
454   { "average",  54, 2, 0, 0, 0, 0 },\r
455   { "moderate", 58, 3, 0, 0, 0, 0 },\r
456   { "medium",   64, 3, 0, 0, 0, 0 },\r
457   { "bulky",    72, 3, 0, 0, 0, 0 },\r
458   { "large",    80, 3, 0, 0, 0, 0 },\r
459   { "big",      87, 3, 0, 0, 0, 0 },\r
460   { "huge",     95, 3, 0, 0, 0, 0 },\r
461   { "giant",    108, 3, 0, 0, 0, 0 },\r
462   { "colossal", 116, 4, 0, 0, 0, 0 },\r
463   { "titanic",  129, 4, 0, 0, 0, 0 },\r
464   { NULL, 0, 0, 0, 0, 0, 0 }\r
465 };\r
466 \r
467 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
468 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
469 {\r
470   { 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
471   { 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
472   { 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
473   { 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
474   { 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
475   { 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
476   { 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
477   { 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
478   { 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
479   { 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
480   { 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
481   { 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
482   { 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
483   { 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
484   { 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
485   { 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
486   { 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
487   { 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
488 };\r
489 \r
490 MyFont *font[NUM_SIZES][NUM_FONTS];\r
491 \r
492 typedef struct {\r
493   char *label;\r
494   int id;\r
495   HWND hwnd;\r
496   WNDPROC wndproc;\r
497 } MyButtonDesc;\r
498 \r
499 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
500 #define N_BUTTONS 5\r
501 \r
502 MyButtonDesc buttonDesc[N_BUTTONS] =\r
503 {\r
504   {"<<", IDM_ToStart, NULL, NULL},\r
505   {"<", IDM_Backward, NULL, NULL},\r
506   {"P", IDM_Pause, NULL, NULL},\r
507   {">", IDM_Forward, NULL, NULL},\r
508   {">>", IDM_ToEnd, NULL, NULL},\r
509 };\r
510 \r
511 int tinyLayout = 0, smallLayout = 0;\r
512 #define MENU_BAR_ITEMS 7\r
513 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
514   { N_("&File"), N_("&Mode"), N_("&Action"), N_("&Step"), N_("&Options"), N_("&Help"), NULL },\r
515   { N_("&F"), N_("&M"), N_("&A"), N_("&S"), N_("&O"), N_("&H"), NULL },\r
516 };\r
517 \r
518 \r
519 MySound sounds[(int)NSoundClasses];\r
520 MyTextAttribs textAttribs[(int)NColorClasses];\r
521 \r
522 MyColorizeAttribs colorizeAttribs[] = {\r
523   { (COLORREF)0, 0, N_("Shout Text") },\r
524   { (COLORREF)0, 0, N_("SShout/CShout") },\r
525   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
526   { (COLORREF)0, 0, N_("Channel Text") },\r
527   { (COLORREF)0, 0, N_("Kibitz Text") },\r
528   { (COLORREF)0, 0, N_("Tell Text") },\r
529   { (COLORREF)0, 0, N_("Challenge Text") },\r
530   { (COLORREF)0, 0, N_("Request Text") },\r
531   { (COLORREF)0, 0, N_("Seek Text") },\r
532   { (COLORREF)0, 0, N_("Normal Text") },\r
533   { (COLORREF)0, 0, N_("None") }\r
534 };\r
535 \r
536 \r
537 \r
538 static char *commentTitle;\r
539 static char *commentText;\r
540 static int commentIndex;\r
541 static Boolean editComment = FALSE;\r
542 \r
543 \r
544 char errorTitle[MSG_SIZ];\r
545 char errorMessage[2*MSG_SIZ];\r
546 HWND errorDialog = NULL;\r
547 BOOLEAN moveErrorMessageUp = FALSE;\r
548 BOOLEAN consoleEcho = TRUE;\r
549 CHARFORMAT consoleCF;\r
550 COLORREF consoleBackgroundColor;\r
551 \r
552 char *programVersion;\r
553 \r
554 #define CPReal 1\r
555 #define CPComm 2\r
556 #define CPSock 3\r
557 #define CPRcmd 4\r
558 typedef int CPKind;\r
559 \r
560 typedef struct {\r
561   CPKind kind;\r
562   HANDLE hProcess;\r
563   DWORD pid;\r
564   HANDLE hTo;\r
565   HANDLE hFrom;\r
566   SOCKET sock;\r
567   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
568 } ChildProc;\r
569 \r
570 #define INPUT_SOURCE_BUF_SIZE 4096\r
571 \r
572 typedef struct _InputSource {\r
573   CPKind kind;\r
574   HANDLE hFile;\r
575   SOCKET sock;\r
576   int lineByLine;\r
577   HANDLE hThread;\r
578   DWORD id;\r
579   char buf[INPUT_SOURCE_BUF_SIZE];\r
580   char *next;\r
581   DWORD count;\r
582   int error;\r
583   InputCallback func;\r
584   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
585   VOIDSTAR closure;\r
586 } InputSource;\r
587 \r
588 InputSource *consoleInputSource;\r
589 \r
590 DCB dcb;\r
591 \r
592 /* forward */\r
593 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
594 VOID ConsoleCreate();\r
595 LRESULT CALLBACK\r
596   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
597 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
598 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
599 VOID ParseCommSettings(char *arg, DCB *dcb);\r
600 LRESULT CALLBACK\r
601   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
602 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
603 void ParseIcsTextMenu(char *icsTextMenuString);\r
604 VOID PopUpMoveDialog(char firstchar);\r
605 VOID PopUpNameDialog(char firstchar);\r
606 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
607 \r
608 /* [AS] */\r
609 int NewGameFRC();\r
610 int GameListOptions();\r
611 \r
612 int dummy; // [HGM] for obsolete args\r
613 \r
614 HWND hwndMain = NULL;        /* root window*/\r
615 HWND hwndConsole = NULL;\r
616 HWND commentDialog = NULL;\r
617 HWND moveHistoryDialog = NULL;\r
618 HWND evalGraphDialog = NULL;\r
619 HWND engineOutputDialog = NULL;\r
620 HWND gameListDialog = NULL;\r
621 HWND editTagsDialog = NULL;\r
622 \r
623 int commentUp = FALSE;\r
624 \r
625 WindowPlacement wpMain;\r
626 WindowPlacement wpConsole;\r
627 WindowPlacement wpComment;\r
628 WindowPlacement wpMoveHistory;\r
629 WindowPlacement wpEvalGraph;\r
630 WindowPlacement wpEngineOutput;\r
631 WindowPlacement wpGameList;\r
632 WindowPlacement wpTags;\r
633 \r
634 VOID EngineOptionsPopup(); // [HGM] settings\r
635 \r
636 VOID GothicPopUp(char *title, VariantClass variant);\r
637 /*\r
638  * Setting "frozen" should disable all user input other than deleting\r
639  * the window.  We do this while engines are initializing themselves.\r
640  */\r
641 static int frozen = 0;\r
642 static int oldMenuItemState[MENU_BAR_ITEMS];\r
643 void FreezeUI()\r
644 {\r
645   HMENU hmenu;\r
646   int i;\r
647 \r
648   if (frozen) return;\r
649   frozen = 1;\r
650   hmenu = GetMenu(hwndMain);\r
651   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
652     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
653   }\r
654   DrawMenuBar(hwndMain);\r
655 }\r
656 \r
657 /* Undo a FreezeUI */\r
658 void ThawUI()\r
659 {\r
660   HMENU hmenu;\r
661   int i;\r
662 \r
663   if (!frozen) return;\r
664   frozen = 0;\r
665   hmenu = GetMenu(hwndMain);\r
666   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
667     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
668   }\r
669   DrawMenuBar(hwndMain);\r
670 }\r
671 \r
672 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
673 \r
674 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
675 #ifdef JAWS\r
676 #include "jaws.c"\r
677 #else\r
678 #define JAWS_INIT\r
679 #define JAWS_ARGS\r
680 #define JAWS_ALT_INTERCEPT\r
681 #define JAWS_KB_NAVIGATION\r
682 #define JAWS_MENU_ITEMS\r
683 #define JAWS_SILENCE\r
684 #define JAWS_REPLAY\r
685 #define JAWS_ACCEL\r
686 #define JAWS_COPYRIGHT\r
687 #define JAWS_DELETE(X) X\r
688 #define SAYMACHINEMOVE()\r
689 #define SAY(X)\r
690 #endif\r
691 \r
692 /*---------------------------------------------------------------------------*\\r
693  *\r
694  * WinMain\r
695  *\r
696 \*---------------------------------------------------------------------------*/\r
697 \r
698 int APIENTRY\r
699 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
700         LPSTR lpCmdLine, int nCmdShow)\r
701 {\r
702   MSG msg;\r
703   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
704 //  INITCOMMONCONTROLSEX ex;\r
705 \r
706   debugFP = stderr;\r
707 \r
708   LoadLibrary("RICHED32.DLL");\r
709   consoleCF.cbSize = sizeof(CHARFORMAT);\r
710 \r
711   if (!InitApplication(hInstance)) {\r
712     return (FALSE);\r
713   }\r
714   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
715     return (FALSE);\r
716   }\r
717 \r
718   JAWS_INIT\r
719 \r
720 //  InitCommonControlsEx(&ex);\r
721   InitCommonControls();\r
722 \r
723   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
724   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
725   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
726 \r
727   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
728 \r
729   while (GetMessage(&msg, /* message structure */\r
730                     NULL, /* handle of window receiving the message */\r
731                     0,    /* lowest message to examine */\r
732                     0))   /* highest message to examine */\r
733     {\r
734 \r
735       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
736         // [HGM] navigate: switch between all windows with tab\r
737         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
738         int i, currentElement = 0;\r
739 \r
740         // first determine what element of the chain we come from (if any)\r
741         if(appData.icsActive) {\r
742             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
743             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
744         }\r
745         if(engineOutputDialog && EngineOutputIsUp()) {\r
746             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
747             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
748         }\r
749         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
750             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
751         }\r
752         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
753         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
754         if(msg.hwnd == e1)                 currentElement = 2; else\r
755         if(msg.hwnd == e2)                 currentElement = 3; else\r
756         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
757         if(msg.hwnd == mh)                currentElement = 4; else\r
758         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
759         if(msg.hwnd == hText)  currentElement = 5; else\r
760         if(msg.hwnd == hInput) currentElement = 6; else\r
761         for (i = 0; i < N_BUTTONS; i++) {\r
762             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
763         }\r
764 \r
765         // determine where to go to\r
766         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
767           do {\r
768             currentElement = (currentElement + direction) % 7;\r
769             switch(currentElement) {\r
770                 case 0:\r
771                   h = hwndMain; break; // passing this case always makes the loop exit\r
772                 case 1:\r
773                   h = buttonDesc[0].hwnd; break; // could be NULL\r
774                 case 2:\r
775                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
776                   h = e1; break;\r
777                 case 3:\r
778                   if(!EngineOutputIsUp()) continue;\r
779                   h = e2; break;\r
780                 case 4:\r
781                   if(!MoveHistoryIsUp()) continue;\r
782                   h = mh; break;\r
783 //              case 6: // input to eval graph does not seem to get here!\r
784 //                if(!EvalGraphIsUp()) continue;\r
785 //                h = evalGraphDialog; break;\r
786                 case 5:\r
787                   if(!appData.icsActive) continue;\r
788                   SAY("display");\r
789                   h = hText; break;\r
790                 case 6:\r
791                   if(!appData.icsActive) continue;\r
792                   SAY("input");\r
793                   h = hInput; break;\r
794             }\r
795           } while(h == 0);\r
796 \r
797           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
798           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
799           SetFocus(h);\r
800 \r
801           continue; // this message now has been processed\r
802         }\r
803       }\r
804 \r
805       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
806           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
807           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
808           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
809           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
810           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
811           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
812           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
813           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
814           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
815         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
816         for(i=0; i<MAX_CHAT; i++) \r
817             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
818                 done = 1; break;\r
819         }\r
820         if(done) continue; // [HGM] chat: end patch\r
821         TranslateMessage(&msg); /* Translates virtual key codes */\r
822         DispatchMessage(&msg);  /* Dispatches message to window */\r
823       }\r
824     }\r
825 \r
826 \r
827   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
828 }\r
829 \r
830 /*---------------------------------------------------------------------------*\\r
831  *\r
832  * Initialization functions\r
833  *\r
834 \*---------------------------------------------------------------------------*/\r
835 \r
836 void\r
837 SetUserLogo()\r
838 {   // update user logo if necessary\r
839     static char oldUserName[MSG_SIZ], *curName;\r
840 \r
841     if(appData.autoLogo) {\r
842           curName = UserName();\r
843           if(strcmp(curName, oldUserName)) {\r
844                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
845                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
846                 strcpy(oldUserName, curName);\r
847           }\r
848     }\r
849 }\r
850 \r
851 BOOL\r
852 InitApplication(HINSTANCE hInstance)\r
853 {\r
854   WNDCLASS wc;\r
855 \r
856   /* Fill in window class structure with parameters that describe the */\r
857   /* main window. */\r
858 \r
859   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
860   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
861   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
862   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
863   wc.hInstance     = hInstance;         /* Owner of this class */\r
864   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
865   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
866   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
867   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
868   wc.lpszClassName = szAppName;                 /* Name to register as */\r
869 \r
870   /* Register the window class and return success/failure code. */\r
871   if (!RegisterClass(&wc)) return FALSE;\r
872 \r
873   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
874   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
875   wc.cbClsExtra    = 0;\r
876   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
877   wc.hInstance     = hInstance;\r
878   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
879   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
880   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
881   wc.lpszMenuName  = NULL;\r
882   wc.lpszClassName = szConsoleName;\r
883 \r
884   if (!RegisterClass(&wc)) return FALSE;\r
885   return TRUE;\r
886 }\r
887 \r
888 \r
889 /* Set by InitInstance, used by EnsureOnScreen */\r
890 int screenHeight, screenWidth;\r
891 \r
892 void\r
893 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
894 {\r
895 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
896   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
897   if (*x > screenWidth - 32) *x = 0;\r
898   if (*y > screenHeight - 32) *y = 0;\r
899   if (*x < minX) *x = minX;\r
900   if (*y < minY) *y = minY;\r
901 }\r
902 \r
903 BOOL\r
904 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
905 {\r
906   HWND hwnd; /* Main window handle. */\r
907   int ibs;\r
908   WINDOWPLACEMENT wp;\r
909   char *filepart;\r
910 \r
911   hInst = hInstance;    /* Store instance handle in our global variable */\r
912   programName = szAppName;\r
913 \r
914   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
915     *filepart = NULLCHAR;\r
916   } else {\r
917     GetCurrentDirectory(MSG_SIZ, installDir);\r
918   }\r
919   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
920   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
921   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
922   /* xboard, and older WinBoards, controlled the move sound with the\r
923      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
924      always turn the option on (so that the backend will call us),\r
925      then let the user turn the sound off by setting it to silence if\r
926      desired.  To accommodate old winboard.ini files saved by old\r
927      versions of WinBoard, we also turn off the sound if the option\r
928      was initially set to false. [HGM] taken out of InitAppData */\r
929   if (!appData.ringBellAfterMoves) {\r
930     sounds[(int)SoundMove].name = strdup("");\r
931     appData.ringBellAfterMoves = TRUE;\r
932   }\r
933   if (appData.debugMode) {\r
934     debugFP = fopen(appData.nameOfDebugFile, "w");\r
935     setbuf(debugFP, NULL);\r
936   }\r
937 \r
938   LoadLanguageFile(appData.language);\r
939 \r
940   InitBackEnd1();\r
941 \r
942 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
943 //  InitEngineUCI( installDir, &second );\r
944 \r
945   /* Create a main window for this application instance. */\r
946   hwnd = CreateWindow(szAppName, szTitle,\r
947                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
948                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
949                       NULL, NULL, hInstance, NULL);\r
950   hwndMain = hwnd;\r
951 \r
952   /* If window could not be created, return "failure" */\r
953   if (!hwnd) {\r
954     return (FALSE);\r
955   }\r
956 \r
957   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
958   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
959       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
960 \r
961       if (first.programLogo == NULL && appData.debugMode) {\r
962           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
963       }\r
964   } else if(appData.autoLogo) {\r
965       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
966         char buf[MSG_SIZ];\r
967         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
968         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
969       }\r
970   }\r
971 \r
972   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
973       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
974 \r
975       if (second.programLogo == NULL && appData.debugMode) {\r
976           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
977       }\r
978   } else if(appData.autoLogo) {\r
979       char buf[MSG_SIZ];\r
980       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
981         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
982         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
983       } else\r
984       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
985         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
986         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
987       }\r
988   }\r
989 \r
990   SetUserLogo();\r
991 \r
992   iconWhite = LoadIcon(hInstance, "icon_white");\r
993   iconBlack = LoadIcon(hInstance, "icon_black");\r
994   iconCurrent = iconWhite;\r
995   InitDrawingColors();\r
996   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
997   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
998   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
999     /* Compute window size for each board size, and use the largest\r
1000        size that fits on this screen as the default. */\r
1001     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1002     if (boardSize == (BoardSize)-1 &&\r
1003         winH <= screenHeight\r
1004            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1005         && winW <= screenWidth) {\r
1006       boardSize = (BoardSize)ibs;\r
1007     }\r
1008   }\r
1009 \r
1010   InitDrawingSizes(boardSize, 0);\r
1011   TranslateMenus();\r
1012   InitMenuChecks();\r
1013   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1014 \r
1015   /* [AS] Load textures if specified */\r
1016   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1017   \r
1018   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1019       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1020       liteBackTextureMode = appData.liteBackTextureMode;\r
1021 \r
1022       if (liteBackTexture == NULL && appData.debugMode) {\r
1023           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1024       }\r
1025   }\r
1026   \r
1027   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1028       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1029       darkBackTextureMode = appData.darkBackTextureMode;\r
1030 \r
1031       if (darkBackTexture == NULL && appData.debugMode) {\r
1032           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1033       }\r
1034   }\r
1035 \r
1036   mysrandom( (unsigned) time(NULL) );\r
1037 \r
1038   /* [AS] Restore layout */\r
1039   if( wpMoveHistory.visible ) {\r
1040       MoveHistoryPopUp();\r
1041   }\r
1042 \r
1043   if( wpEvalGraph.visible ) {\r
1044       EvalGraphPopUp();\r
1045   }\r
1046 \r
1047   if( wpEngineOutput.visible ) {\r
1048       EngineOutputPopUp();\r
1049   }\r
1050 \r
1051   /* Make the window visible; update its client area; and return "success" */\r
1052   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1053   wp.length = sizeof(WINDOWPLACEMENT);\r
1054   wp.flags = 0;\r
1055   wp.showCmd = nCmdShow;\r
1056   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1057   wp.rcNormalPosition.left = wpMain.x;\r
1058   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1059   wp.rcNormalPosition.top = wpMain.y;\r
1060   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1061   SetWindowPlacement(hwndMain, &wp);\r
1062 \r
1063   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1064 \r
1065   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1066                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1067 \r
1068   if (hwndConsole) {\r
1069 #if AOT_CONSOLE\r
1070     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1071                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1072 #endif\r
1073     ShowWindow(hwndConsole, nCmdShow);\r
1074     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
1075       char buf[MSG_SIZ], *p = buf, *q;\r
1076       strcpy(buf, appData.chatBoxes);\r
1077       do {\r
1078         q = strchr(p, ';');\r
1079         if(q) *q++ = 0;\r
1080         if(*p) ChatPopUp(p);\r
1081       } while(p=q);\r
1082     }\r
1083     SetActiveWindow(hwndConsole);\r
1084   }\r
1085   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1086   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1087 \r
1088   return TRUE;\r
1089 \r
1090 }\r
1091 \r
1092 VOID\r
1093 InitMenuChecks()\r
1094 {\r
1095   HMENU hmenu = GetMenu(hwndMain);\r
1096 \r
1097   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1098                         MF_BYCOMMAND|((appData.icsActive &&\r
1099                                        *appData.icsCommPort != NULLCHAR) ?\r
1100                                       MF_ENABLED : MF_GRAYED));\r
1101   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1102                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1103                                      MF_CHECKED : MF_UNCHECKED));\r
1104 }\r
1105 \r
1106 //---------------------------------------------------------------------------------------------------------\r
1107 \r
1108 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1109 #define XBOARD FALSE\r
1110 \r
1111 #define OPTCHAR "/"\r
1112 #define SEPCHAR "="\r
1113 \r
1114 #include "args.h"\r
1115 \r
1116 // front-end part of option handling\r
1117 \r
1118 VOID\r
1119 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1120 {\r
1121   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1122   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1123   DeleteDC(hdc);\r
1124   lf->lfWidth = 0;\r
1125   lf->lfEscapement = 0;\r
1126   lf->lfOrientation = 0;\r
1127   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1128   lf->lfItalic = mfp->italic;\r
1129   lf->lfUnderline = mfp->underline;\r
1130   lf->lfStrikeOut = mfp->strikeout;\r
1131   lf->lfCharSet = mfp->charset;\r
1132   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1133   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1134   lf->lfQuality = DEFAULT_QUALITY;\r
1135   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1136   strcpy(lf->lfFaceName, mfp->faceName);\r
1137 }\r
1138 \r
1139 void\r
1140 CreateFontInMF(MyFont *mf)\r
1141\r
1142   LFfromMFP(&mf->lf, &mf->mfp);\r
1143   if (mf->hf) DeleteObject(mf->hf);\r
1144   mf->hf = CreateFontIndirect(&mf->lf);\r
1145 }\r
1146 \r
1147 // [HGM] This platform-dependent table provides the location for storing the color info\r
1148 void *\r
1149 colorVariable[] = {\r
1150   &whitePieceColor, \r
1151   &blackPieceColor, \r
1152   &lightSquareColor,\r
1153   &darkSquareColor, \r
1154   &highlightSquareColor,\r
1155   &premoveHighlightColor,\r
1156   NULL,\r
1157   &consoleBackgroundColor,\r
1158   &appData.fontForeColorWhite,\r
1159   &appData.fontBackColorWhite,\r
1160   &appData.fontForeColorBlack,\r
1161   &appData.fontBackColorBlack,\r
1162   &appData.evalHistColorWhite,\r
1163   &appData.evalHistColorBlack,\r
1164   &appData.highlightArrowColor,\r
1165 };\r
1166 \r
1167 /* Command line font name parser.  NULL name means do nothing.\r
1168    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1169    For backward compatibility, syntax without the colon is also\r
1170    accepted, but font names with digits in them won't work in that case.\r
1171 */\r
1172 VOID\r
1173 ParseFontName(char *name, MyFontParams *mfp)\r
1174 {\r
1175   char *p, *q;\r
1176   if (name == NULL) return;\r
1177   p = name;\r
1178   q = strchr(p, ':');\r
1179   if (q) {\r
1180     if (q - p >= sizeof(mfp->faceName))\r
1181       ExitArgError(_("Font name too long:"), name);\r
1182     memcpy(mfp->faceName, p, q - p);\r
1183     mfp->faceName[q - p] = NULLCHAR;\r
1184     p = q + 1;\r
1185   } else {\r
1186     q = mfp->faceName;\r
1187     while (*p && !isdigit(*p)) {\r
1188       *q++ = *p++;\r
1189       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1190         ExitArgError(_("Font name too long:"), name);\r
1191     }\r
1192     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1193     *q = NULLCHAR;\r
1194   }\r
1195   if (!*p) ExitArgError(_("Font point size missing:"), name);\r
1196   mfp->pointSize = (float) atof(p);\r
1197   mfp->bold = (strchr(p, 'b') != NULL);\r
1198   mfp->italic = (strchr(p, 'i') != NULL);\r
1199   mfp->underline = (strchr(p, 'u') != NULL);\r
1200   mfp->strikeout = (strchr(p, 's') != NULL);\r
1201   mfp->charset = DEFAULT_CHARSET;\r
1202   q = strchr(p, 'c');\r
1203   if (q)\r
1204     mfp->charset = (BYTE) atoi(q+1);\r
1205 }\r
1206 \r
1207 void\r
1208 ParseFont(char *name, int number)\r
1209 { // wrapper to shield back-end from 'font'\r
1210   ParseFontName(name, &font[boardSize][number]->mfp);\r
1211 }\r
1212 \r
1213 void\r
1214 SetFontDefaults()\r
1215 { // in WB  we have a 2D array of fonts; this initializes their description\r
1216   int i, j;\r
1217   /* Point font array elements to structures and\r
1218      parse default font names */\r
1219   for (i=0; i<NUM_FONTS; i++) {\r
1220     for (j=0; j<NUM_SIZES; j++) {\r
1221       font[j][i] = &fontRec[j][i];\r
1222       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1223     }\r
1224   }\r
1225 }\r
1226 \r
1227 void\r
1228 CreateFonts()\r
1229 { // here we create the actual fonts from the selected descriptions\r
1230   int i, j;\r
1231   for (i=0; i<NUM_FONTS; i++) {\r
1232     for (j=0; j<NUM_SIZES; j++) {\r
1233       CreateFontInMF(font[j][i]);\r
1234     }\r
1235   }\r
1236 }\r
1237 /* Color name parser.\r
1238    X version accepts X color names, but this one\r
1239    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1240 COLORREF\r
1241 ParseColorName(char *name)\r
1242 {\r
1243   int red, green, blue, count;\r
1244   char buf[MSG_SIZ];\r
1245 \r
1246   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1247   if (count != 3) {\r
1248     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1249       &red, &green, &blue);\r
1250   }\r
1251   if (count != 3) {\r
1252     sprintf(buf, _("Can't parse color name %s"), name);\r
1253     DisplayError(buf, 0);\r
1254     return RGB(0, 0, 0);\r
1255   }\r
1256   return PALETTERGB(red, green, blue);\r
1257 }\r
1258 \r
1259 void\r
1260 ParseColor(int n, char *name)\r
1261 { // for WinBoard the color is an int, which needs to be derived from the string\r
1262   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1263 }\r
1264 \r
1265 void\r
1266 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1267 {\r
1268   char *e = argValue;\r
1269   int eff = 0;\r
1270 \r
1271   while (*e) {\r
1272     if (*e == 'b')      eff |= CFE_BOLD;\r
1273     else if (*e == 'i') eff |= CFE_ITALIC;\r
1274     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1275     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1276     else if (*e == '#' || isdigit(*e)) break;\r
1277     e++;\r
1278   }\r
1279   *effects = eff;\r
1280   *color   = ParseColorName(e);\r
1281 }\r
1282 \r
1283 void\r
1284 ParseTextAttribs(ColorClass cc, char *s)\r
1285 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1286     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1287     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1288 }\r
1289 \r
1290 void\r
1291 ParseBoardSize(void *addr, char *name)\r
1292 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1293   BoardSize bs = SizeTiny;\r
1294   while (sizeInfo[bs].name != NULL) {\r
1295     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1296         *(BoardSize *)addr = bs;\r
1297         return;\r
1298     }\r
1299     bs++;\r
1300   }\r
1301   ExitArgError(_("Unrecognized board size value"), name);\r
1302 }\r
1303 \r
1304 void\r
1305 LoadAllSounds()\r
1306 { // [HGM] import name from appData first\r
1307   ColorClass cc;\r
1308   SoundClass sc;\r
1309   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1310     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1311     textAttribs[cc].sound.data = NULL;\r
1312     MyLoadSound(&textAttribs[cc].sound);\r
1313   }\r
1314   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1315     textAttribs[cc].sound.name = strdup("");\r
1316     textAttribs[cc].sound.data = NULL;\r
1317   }\r
1318   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1319     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1320     sounds[sc].data = NULL;\r
1321     MyLoadSound(&sounds[sc]);\r
1322   }\r
1323 }\r
1324 \r
1325 void\r
1326 SetCommPortDefaults()\r
1327 {\r
1328    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1329   dcb.DCBlength = sizeof(DCB);\r
1330   dcb.BaudRate = 9600;\r
1331   dcb.fBinary = TRUE;\r
1332   dcb.fParity = FALSE;\r
1333   dcb.fOutxCtsFlow = FALSE;\r
1334   dcb.fOutxDsrFlow = FALSE;\r
1335   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1336   dcb.fDsrSensitivity = FALSE;\r
1337   dcb.fTXContinueOnXoff = TRUE;\r
1338   dcb.fOutX = FALSE;\r
1339   dcb.fInX = FALSE;\r
1340   dcb.fNull = FALSE;\r
1341   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1342   dcb.fAbortOnError = FALSE;\r
1343   dcb.ByteSize = 7;\r
1344   dcb.Parity = SPACEPARITY;\r
1345   dcb.StopBits = ONESTOPBIT;\r
1346 }\r
1347 \r
1348 // [HGM] args: these three cases taken out to stay in front-end\r
1349 void\r
1350 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1351 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1352         // while the curent board size determines the element. This system should be ported to XBoard.\r
1353         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1354         int bs;\r
1355         for (bs=0; bs<NUM_SIZES; bs++) {\r
1356           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1357           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1358           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1359             ad->argName, mfp->faceName, mfp->pointSize,\r
1360             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1361             mfp->bold ? "b" : "",\r
1362             mfp->italic ? "i" : "",\r
1363             mfp->underline ? "u" : "",\r
1364             mfp->strikeout ? "s" : "",\r
1365             (int)mfp->charset);\r
1366         }\r
1367       }\r
1368 \r
1369 void\r
1370 ExportSounds()\r
1371 { // [HGM] copy the names from the internal WB variables to appData\r
1372   ColorClass cc;\r
1373   SoundClass sc;\r
1374   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1375     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1376   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1377     (&appData.soundMove)[sc] = sounds[sc].name;\r
1378 }\r
1379 \r
1380 void\r
1381 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1382 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1383         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1384         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1385           (ta->effects & CFE_BOLD) ? "b" : "",\r
1386           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1387           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1388           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1389           (ta->effects) ? " " : "",\r
1390           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1391       }\r
1392 \r
1393 void\r
1394 SaveColor(FILE *f, ArgDescriptor *ad)\r
1395 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1396         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1397         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1398           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1399 }\r
1400 \r
1401 void\r
1402 SaveBoardSize(FILE *f, char *name, void *addr)\r
1403 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1404   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1405 }\r
1406 \r
1407 void\r
1408 ParseCommPortSettings(char *s)\r
1409 { // wrapper to keep dcb from back-end\r
1410   ParseCommSettings(s, &dcb);\r
1411 }\r
1412 \r
1413 void\r
1414 GetWindowCoords()\r
1415 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1416   GetActualPlacement(hwndMain, &wpMain);\r
1417   GetActualPlacement(hwndConsole, &wpConsole);\r
1418   GetActualPlacement(commentDialog, &wpComment);\r
1419   GetActualPlacement(editTagsDialog, &wpTags);\r
1420   GetActualPlacement(gameListDialog, &wpGameList);\r
1421   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1422   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1423   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1424 }\r
1425 \r
1426 void\r
1427 PrintCommPortSettings(FILE *f, char *name)\r
1428 { // wrapper to shield back-end from DCB\r
1429       PrintCommSettings(f, name, &dcb);\r
1430 }\r
1431 \r
1432 int\r
1433 MySearchPath(char *installDir, char *name, char *fullname)\r
1434 {\r
1435   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1436   if(name[0]== '%') {\r
1437     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1438     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1439       strcpy(buf, p+1);\r
1440       *strchr(buf, '%') = 0;\r
1441       strcat(fullname, getenv(buf));\r
1442       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1443     }\r
1444     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1445     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1446     return (int) strlen(fullname);\r
1447   }\r
1448   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1449 }\r
1450 \r
1451 int\r
1452 MyGetFullPathName(char *name, char *fullname)\r
1453 {\r
1454   char *dummy;\r
1455   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1456 }\r
1457 \r
1458 int\r
1459 MainWindowUp()\r
1460 { // [HGM] args: allows testing if main window is realized from back-end\r
1461   return hwndMain != NULL;\r
1462 }\r
1463 \r
1464 void\r
1465 PopUpStartupDialog()\r
1466 {\r
1467     FARPROC lpProc;\r
1468     \r
1469     LoadLanguageFile(appData.language);\r
1470     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1471     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1472     FreeProcInstance(lpProc);\r
1473 }\r
1474 \r
1475 /*---------------------------------------------------------------------------*\\r
1476  *\r
1477  * GDI board drawing routines\r
1478  *\r
1479 \*---------------------------------------------------------------------------*/\r
1480 \r
1481 /* [AS] Draw square using background texture */\r
1482 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1483 {\r
1484     XFORM   x;\r
1485 \r
1486     if( mode == 0 ) {\r
1487         return; /* Should never happen! */\r
1488     }\r
1489 \r
1490     SetGraphicsMode( dst, GM_ADVANCED );\r
1491 \r
1492     switch( mode ) {\r
1493     case 1:\r
1494         /* Identity */\r
1495         break;\r
1496     case 2:\r
1497         /* X reflection */\r
1498         x.eM11 = -1.0;\r
1499         x.eM12 = 0;\r
1500         x.eM21 = 0;\r
1501         x.eM22 = 1.0;\r
1502         x.eDx = (FLOAT) dw + dx - 1;\r
1503         x.eDy = 0;\r
1504         dx = 0;\r
1505         SetWorldTransform( dst, &x );\r
1506         break;\r
1507     case 3:\r
1508         /* Y reflection */\r
1509         x.eM11 = 1.0;\r
1510         x.eM12 = 0;\r
1511         x.eM21 = 0;\r
1512         x.eM22 = -1.0;\r
1513         x.eDx = 0;\r
1514         x.eDy = (FLOAT) dh + dy - 1;\r
1515         dy = 0;\r
1516         SetWorldTransform( dst, &x );\r
1517         break;\r
1518     case 4:\r
1519         /* X/Y flip */\r
1520         x.eM11 = 0;\r
1521         x.eM12 = 1.0;\r
1522         x.eM21 = 1.0;\r
1523         x.eM22 = 0;\r
1524         x.eDx = (FLOAT) dx;\r
1525         x.eDy = (FLOAT) dy;\r
1526         dx = 0;\r
1527         dy = 0;\r
1528         SetWorldTransform( dst, &x );\r
1529         break;\r
1530     }\r
1531 \r
1532     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1533 \r
1534     x.eM11 = 1.0;\r
1535     x.eM12 = 0;\r
1536     x.eM21 = 0;\r
1537     x.eM22 = 1.0;\r
1538     x.eDx = 0;\r
1539     x.eDy = 0;\r
1540     SetWorldTransform( dst, &x );\r
1541 \r
1542     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1543 }\r
1544 \r
1545 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1546 enum {\r
1547     PM_WP = (int) WhitePawn, \r
1548     PM_WN = (int) WhiteKnight, \r
1549     PM_WB = (int) WhiteBishop, \r
1550     PM_WR = (int) WhiteRook, \r
1551     PM_WQ = (int) WhiteQueen, \r
1552     PM_WF = (int) WhiteFerz, \r
1553     PM_WW = (int) WhiteWazir, \r
1554     PM_WE = (int) WhiteAlfil, \r
1555     PM_WM = (int) WhiteMan, \r
1556     PM_WO = (int) WhiteCannon, \r
1557     PM_WU = (int) WhiteUnicorn, \r
1558     PM_WH = (int) WhiteNightrider, \r
1559     PM_WA = (int) WhiteAngel, \r
1560     PM_WC = (int) WhiteMarshall, \r
1561     PM_WAB = (int) WhiteCardinal, \r
1562     PM_WD = (int) WhiteDragon, \r
1563     PM_WL = (int) WhiteLance, \r
1564     PM_WS = (int) WhiteCobra, \r
1565     PM_WV = (int) WhiteFalcon, \r
1566     PM_WSG = (int) WhiteSilver, \r
1567     PM_WG = (int) WhiteGrasshopper, \r
1568     PM_WK = (int) WhiteKing,\r
1569     PM_BP = (int) BlackPawn, \r
1570     PM_BN = (int) BlackKnight, \r
1571     PM_BB = (int) BlackBishop, \r
1572     PM_BR = (int) BlackRook, \r
1573     PM_BQ = (int) BlackQueen, \r
1574     PM_BF = (int) BlackFerz, \r
1575     PM_BW = (int) BlackWazir, \r
1576     PM_BE = (int) BlackAlfil, \r
1577     PM_BM = (int) BlackMan,\r
1578     PM_BO = (int) BlackCannon, \r
1579     PM_BU = (int) BlackUnicorn, \r
1580     PM_BH = (int) BlackNightrider, \r
1581     PM_BA = (int) BlackAngel, \r
1582     PM_BC = (int) BlackMarshall, \r
1583     PM_BG = (int) BlackGrasshopper, \r
1584     PM_BAB = (int) BlackCardinal,\r
1585     PM_BD = (int) BlackDragon,\r
1586     PM_BL = (int) BlackLance,\r
1587     PM_BS = (int) BlackCobra,\r
1588     PM_BV = (int) BlackFalcon,\r
1589     PM_BSG = (int) BlackSilver,\r
1590     PM_BK = (int) BlackKing\r
1591 };\r
1592 \r
1593 static HFONT hPieceFont = NULL;\r
1594 static HBITMAP hPieceMask[(int) EmptySquare];\r
1595 static HBITMAP hPieceFace[(int) EmptySquare];\r
1596 static int fontBitmapSquareSize = 0;\r
1597 static char pieceToFontChar[(int) EmptySquare] =\r
1598                               { 'p', 'n', 'b', 'r', 'q', \r
1599                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1600                       'k', 'o', 'm', 'v', 't', 'w', \r
1601                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1602                                                               'l' };\r
1603 \r
1604 extern BOOL SetCharTable( char *table, const char * map );\r
1605 /* [HGM] moved to backend.c */\r
1606 \r
1607 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1608 {\r
1609     HBRUSH hbrush;\r
1610     BYTE r1 = GetRValue( color );\r
1611     BYTE g1 = GetGValue( color );\r
1612     BYTE b1 = GetBValue( color );\r
1613     BYTE r2 = r1 / 2;\r
1614     BYTE g2 = g1 / 2;\r
1615     BYTE b2 = b1 / 2;\r
1616     RECT rc;\r
1617 \r
1618     /* Create a uniform background first */\r
1619     hbrush = CreateSolidBrush( color );\r
1620     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1621     FillRect( hdc, &rc, hbrush );\r
1622     DeleteObject( hbrush );\r
1623     \r
1624     if( mode == 1 ) {\r
1625         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1626         int steps = squareSize / 2;\r
1627         int i;\r
1628 \r
1629         for( i=0; i<steps; i++ ) {\r
1630             BYTE r = r1 - (r1-r2) * i / steps;\r
1631             BYTE g = g1 - (g1-g2) * i / steps;\r
1632             BYTE b = b1 - (b1-b2) * i / steps;\r
1633 \r
1634             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1635             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1636             FillRect( hdc, &rc, hbrush );\r
1637             DeleteObject(hbrush);\r
1638         }\r
1639     }\r
1640     else if( mode == 2 ) {\r
1641         /* Diagonal gradient, good more or less for every piece */\r
1642         POINT triangle[3];\r
1643         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1644         HBRUSH hbrush_old;\r
1645         int steps = squareSize;\r
1646         int i;\r
1647 \r
1648         triangle[0].x = squareSize - steps;\r
1649         triangle[0].y = squareSize;\r
1650         triangle[1].x = squareSize;\r
1651         triangle[1].y = squareSize;\r
1652         triangle[2].x = squareSize;\r
1653         triangle[2].y = squareSize - steps;\r
1654 \r
1655         for( i=0; i<steps; i++ ) {\r
1656             BYTE r = r1 - (r1-r2) * i / steps;\r
1657             BYTE g = g1 - (g1-g2) * i / steps;\r
1658             BYTE b = b1 - (b1-b2) * i / steps;\r
1659 \r
1660             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1661             hbrush_old = SelectObject( hdc, hbrush );\r
1662             Polygon( hdc, triangle, 3 );\r
1663             SelectObject( hdc, hbrush_old );\r
1664             DeleteObject(hbrush);\r
1665             triangle[0].x++;\r
1666             triangle[2].y++;\r
1667         }\r
1668 \r
1669         SelectObject( hdc, hpen );\r
1670     }\r
1671 }\r
1672 \r
1673 /*\r
1674     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1675     seems to work ok. The main problem here is to find the "inside" of a chess\r
1676     piece: follow the steps as explained below.\r
1677 */\r
1678 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1679 {\r
1680     HBITMAP hbm;\r
1681     HBITMAP hbm_old;\r
1682     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1683     RECT rc;\r
1684     SIZE sz;\r
1685     POINT pt;\r
1686     int backColor = whitePieceColor; \r
1687     int foreColor = blackPieceColor;\r
1688     \r
1689     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1690         backColor = appData.fontBackColorWhite;\r
1691         foreColor = appData.fontForeColorWhite;\r
1692     }\r
1693     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1694         backColor = appData.fontBackColorBlack;\r
1695         foreColor = appData.fontForeColorBlack;\r
1696     }\r
1697 \r
1698     /* Mask */\r
1699     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1700 \r
1701     hbm_old = SelectObject( hdc, hbm );\r
1702 \r
1703     rc.left = 0;\r
1704     rc.top = 0;\r
1705     rc.right = squareSize;\r
1706     rc.bottom = squareSize;\r
1707 \r
1708     /* Step 1: background is now black */\r
1709     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1710 \r
1711     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1712 \r
1713     pt.x = (squareSize - sz.cx) / 2;\r
1714     pt.y = (squareSize - sz.cy) / 2;\r
1715 \r
1716     SetBkMode( hdc, TRANSPARENT );\r
1717     SetTextColor( hdc, chroma );\r
1718     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1719     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1720 \r
1721     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1722     /* Step 3: the area outside the piece is filled with white */\r
1723 //    FloodFill( hdc, 0, 0, chroma );\r
1724     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1725     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1726     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1727     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1728     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1729     /* \r
1730         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1731         but if the start point is not inside the piece we're lost!\r
1732         There should be a better way to do this... if we could create a region or path\r
1733         from the fill operation we would be fine for example.\r
1734     */\r
1735 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1736     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1737 \r
1738     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1739         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1740         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1741 \r
1742         SelectObject( dc2, bm2 );\r
1743         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1744         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1745         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1746         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1747         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1748 \r
1749         DeleteDC( dc2 );\r
1750         DeleteObject( bm2 );\r
1751     }\r
1752 \r
1753     SetTextColor( hdc, 0 );\r
1754     /* \r
1755         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1756         draw the piece again in black for safety.\r
1757     */\r
1758     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1759 \r
1760     SelectObject( hdc, hbm_old );\r
1761 \r
1762     if( hPieceMask[index] != NULL ) {\r
1763         DeleteObject( hPieceMask[index] );\r
1764     }\r
1765 \r
1766     hPieceMask[index] = hbm;\r
1767 \r
1768     /* Face */\r
1769     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1770 \r
1771     SelectObject( hdc, hbm );\r
1772 \r
1773     {\r
1774         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1775         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1776         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1777 \r
1778         SelectObject( dc1, hPieceMask[index] );\r
1779         SelectObject( dc2, bm2 );\r
1780         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1781         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1782         \r
1783         /* \r
1784             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1785             the piece background and deletes (makes transparent) the rest.\r
1786             Thanks to that mask, we are free to paint the background with the greates\r
1787             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1788             We use this, to make gradients and give the pieces a "roundish" look.\r
1789         */\r
1790         SetPieceBackground( hdc, backColor, 2 );\r
1791         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1792 \r
1793         DeleteDC( dc2 );\r
1794         DeleteDC( dc1 );\r
1795         DeleteObject( bm2 );\r
1796     }\r
1797 \r
1798     SetTextColor( hdc, foreColor );\r
1799     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1800 \r
1801     SelectObject( hdc, hbm_old );\r
1802 \r
1803     if( hPieceFace[index] != NULL ) {\r
1804         DeleteObject( hPieceFace[index] );\r
1805     }\r
1806 \r
1807     hPieceFace[index] = hbm;\r
1808 }\r
1809 \r
1810 static int TranslatePieceToFontPiece( int piece )\r
1811 {\r
1812     switch( piece ) {\r
1813     case BlackPawn:\r
1814         return PM_BP;\r
1815     case BlackKnight:\r
1816         return PM_BN;\r
1817     case BlackBishop:\r
1818         return PM_BB;\r
1819     case BlackRook:\r
1820         return PM_BR;\r
1821     case BlackQueen:\r
1822         return PM_BQ;\r
1823     case BlackKing:\r
1824         return PM_BK;\r
1825     case WhitePawn:\r
1826         return PM_WP;\r
1827     case WhiteKnight:\r
1828         return PM_WN;\r
1829     case WhiteBishop:\r
1830         return PM_WB;\r
1831     case WhiteRook:\r
1832         return PM_WR;\r
1833     case WhiteQueen:\r
1834         return PM_WQ;\r
1835     case WhiteKing:\r
1836         return PM_WK;\r
1837 \r
1838     case BlackAngel:\r
1839         return PM_BA;\r
1840     case BlackMarshall:\r
1841         return PM_BC;\r
1842     case BlackFerz:\r
1843         return PM_BF;\r
1844     case BlackNightrider:\r
1845         return PM_BH;\r
1846     case BlackAlfil:\r
1847         return PM_BE;\r
1848     case BlackWazir:\r
1849         return PM_BW;\r
1850     case BlackUnicorn:\r
1851         return PM_BU;\r
1852     case BlackCannon:\r
1853         return PM_BO;\r
1854     case BlackGrasshopper:\r
1855         return PM_BG;\r
1856     case BlackMan:\r
1857         return PM_BM;\r
1858     case BlackSilver:\r
1859         return PM_BSG;\r
1860     case BlackLance:\r
1861         return PM_BL;\r
1862     case BlackFalcon:\r
1863         return PM_BV;\r
1864     case BlackCobra:\r
1865         return PM_BS;\r
1866     case BlackCardinal:\r
1867         return PM_BAB;\r
1868     case BlackDragon:\r
1869         return PM_BD;\r
1870 \r
1871     case WhiteAngel:\r
1872         return PM_WA;\r
1873     case WhiteMarshall:\r
1874         return PM_WC;\r
1875     case WhiteFerz:\r
1876         return PM_WF;\r
1877     case WhiteNightrider:\r
1878         return PM_WH;\r
1879     case WhiteAlfil:\r
1880         return PM_WE;\r
1881     case WhiteWazir:\r
1882         return PM_WW;\r
1883     case WhiteUnicorn:\r
1884         return PM_WU;\r
1885     case WhiteCannon:\r
1886         return PM_WO;\r
1887     case WhiteGrasshopper:\r
1888         return PM_WG;\r
1889     case WhiteMan:\r
1890         return PM_WM;\r
1891     case WhiteSilver:\r
1892         return PM_WSG;\r
1893     case WhiteLance:\r
1894         return PM_WL;\r
1895     case WhiteFalcon:\r
1896         return PM_WV;\r
1897     case WhiteCobra:\r
1898         return PM_WS;\r
1899     case WhiteCardinal:\r
1900         return PM_WAB;\r
1901     case WhiteDragon:\r
1902         return PM_WD;\r
1903     }\r
1904 \r
1905     return 0;\r
1906 }\r
1907 \r
1908 void CreatePiecesFromFont()\r
1909 {\r
1910     LOGFONT lf;\r
1911     HDC hdc_window = NULL;\r
1912     HDC hdc = NULL;\r
1913     HFONT hfont_old;\r
1914     int fontHeight;\r
1915     int i;\r
1916 \r
1917     if( fontBitmapSquareSize < 0 ) {\r
1918         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1919         return;\r
1920     }\r
1921 \r
1922     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1923         fontBitmapSquareSize = -1;\r
1924         return;\r
1925     }\r
1926 \r
1927     if( fontBitmapSquareSize != squareSize ) {\r
1928         hdc_window = GetDC( hwndMain );\r
1929         hdc = CreateCompatibleDC( hdc_window );\r
1930 \r
1931         if( hPieceFont != NULL ) {\r
1932             DeleteObject( hPieceFont );\r
1933         }\r
1934         else {\r
1935             for( i=0; i<=(int)BlackKing; i++ ) {\r
1936                 hPieceMask[i] = NULL;\r
1937                 hPieceFace[i] = NULL;\r
1938             }\r
1939         }\r
1940 \r
1941         fontHeight = 75;\r
1942 \r
1943         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
1944             fontHeight = appData.fontPieceSize;\r
1945         }\r
1946 \r
1947         fontHeight = (fontHeight * squareSize) / 100;\r
1948 \r
1949         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
1950         lf.lfWidth = 0;\r
1951         lf.lfEscapement = 0;\r
1952         lf.lfOrientation = 0;\r
1953         lf.lfWeight = FW_NORMAL;\r
1954         lf.lfItalic = 0;\r
1955         lf.lfUnderline = 0;\r
1956         lf.lfStrikeOut = 0;\r
1957         lf.lfCharSet = DEFAULT_CHARSET;\r
1958         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1959         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1960         lf.lfQuality = PROOF_QUALITY;\r
1961         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
1962         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
1963         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
1964 \r
1965         hPieceFont = CreateFontIndirect( &lf );\r
1966 \r
1967         if( hPieceFont == NULL ) {\r
1968             fontBitmapSquareSize = -2;\r
1969         }\r
1970         else {\r
1971             /* Setup font-to-piece character table */\r
1972             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
1973                 /* No (or wrong) global settings, try to detect the font */\r
1974                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
1975                     /* Alpha */\r
1976                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
1977                 }\r
1978                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
1979                     /* DiagramTT* family */\r
1980                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
1981                 }\r
1982                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
1983                     /* Fairy symbols */\r
1984                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
1985                 }\r
1986                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
1987                     /* Good Companion (Some characters get warped as literal :-( */\r
1988                     char s[] = "1cmWG0??S??oYI23wgQU";\r
1989                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
1990                     SetCharTable(pieceToFontChar, s);\r
1991                 }\r
1992                 else {\r
1993                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
1994                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
1995                 }\r
1996             }\r
1997 \r
1998             /* Create bitmaps */\r
1999             hfont_old = SelectObject( hdc, hPieceFont );\r
2000             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2001                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2002                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2003 \r
2004             SelectObject( hdc, hfont_old );\r
2005 \r
2006             fontBitmapSquareSize = squareSize;\r
2007         }\r
2008     }\r
2009 \r
2010     if( hdc != NULL ) {\r
2011         DeleteDC( hdc );\r
2012     }\r
2013 \r
2014     if( hdc_window != NULL ) {\r
2015         ReleaseDC( hwndMain, hdc_window );\r
2016     }\r
2017 }\r
2018 \r
2019 HBITMAP\r
2020 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2021 {\r
2022   char name[128];\r
2023 \r
2024   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2025   if (gameInfo.event &&\r
2026       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2027       strcmp(name, "k80s") == 0) {\r
2028     strcpy(name, "tim");\r
2029   }\r
2030   return LoadBitmap(hinst, name);\r
2031 }\r
2032 \r
2033 \r
2034 /* Insert a color into the program's logical palette\r
2035    structure.  This code assumes the given color is\r
2036    the result of the RGB or PALETTERGB macro, and it\r
2037    knows how those macros work (which is documented).\r
2038 */\r
2039 VOID\r
2040 InsertInPalette(COLORREF color)\r
2041 {\r
2042   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2043 \r
2044   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2045     DisplayFatalError(_("Too many colors"), 0, 1);\r
2046     pLogPal->palNumEntries--;\r
2047     return;\r
2048   }\r
2049 \r
2050   pe->peFlags = (char) 0;\r
2051   pe->peRed = (char) (0xFF & color);\r
2052   pe->peGreen = (char) (0xFF & (color >> 8));\r
2053   pe->peBlue = (char) (0xFF & (color >> 16));\r
2054   return;\r
2055 }\r
2056 \r
2057 \r
2058 VOID\r
2059 InitDrawingColors()\r
2060 {\r
2061   if (pLogPal == NULL) {\r
2062     /* Allocate enough memory for a logical palette with\r
2063      * PALETTESIZE entries and set the size and version fields\r
2064      * of the logical palette structure.\r
2065      */\r
2066     pLogPal = (NPLOGPALETTE)\r
2067       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2068                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2069     pLogPal->palVersion    = 0x300;\r
2070   }\r
2071   pLogPal->palNumEntries = 0;\r
2072 \r
2073   InsertInPalette(lightSquareColor);\r
2074   InsertInPalette(darkSquareColor);\r
2075   InsertInPalette(whitePieceColor);\r
2076   InsertInPalette(blackPieceColor);\r
2077   InsertInPalette(highlightSquareColor);\r
2078   InsertInPalette(premoveHighlightColor);\r
2079 \r
2080   /*  create a logical color palette according the information\r
2081    *  in the LOGPALETTE structure.\r
2082    */\r
2083   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2084 \r
2085   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2086   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2087   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2088   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2089   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2090   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2091   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2092   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2093   /* [AS] Force rendering of the font-based pieces */\r
2094   if( fontBitmapSquareSize > 0 ) {\r
2095     fontBitmapSquareSize = 0;\r
2096   }\r
2097 }\r
2098 \r
2099 \r
2100 int\r
2101 BoardWidth(int boardSize, int n)\r
2102 { /* [HGM] argument n added to allow different width and height */\r
2103   int lineGap = sizeInfo[boardSize].lineGap;\r
2104 \r
2105   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2106       lineGap = appData.overrideLineGap;\r
2107   }\r
2108 \r
2109   return (n + 1) * lineGap +\r
2110           n * sizeInfo[boardSize].squareSize;\r
2111 }\r
2112 \r
2113 /* Respond to board resize by dragging edge */\r
2114 VOID\r
2115 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2116 {\r
2117   BoardSize newSize = NUM_SIZES - 1;\r
2118   static int recurse = 0;\r
2119   if (IsIconic(hwndMain)) return;\r
2120   if (recurse > 0) return;\r
2121   recurse++;\r
2122   while (newSize > 0) {\r
2123         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2124         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2125            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2126     newSize--;\r
2127   } \r
2128   boardSize = newSize;\r
2129   InitDrawingSizes(boardSize, flags);\r
2130   recurse--;\r
2131 }\r
2132 \r
2133 \r
2134 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2135 \r
2136 VOID\r
2137 InitDrawingSizes(BoardSize boardSize, int flags)\r
2138 {\r
2139   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2140   ChessSquare piece;\r
2141   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2142   HDC hdc;\r
2143   SIZE clockSize, messageSize;\r
2144   HFONT oldFont;\r
2145   char buf[MSG_SIZ];\r
2146   char *str;\r
2147   HMENU hmenu = GetMenu(hwndMain);\r
2148   RECT crect, wrect, oldRect;\r
2149   int offby;\r
2150   LOGBRUSH logbrush;\r
2151 \r
2152   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2153   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2154 \r
2155   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2156   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2157 \r
2158   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2159   oldRect.top = wpMain.y;\r
2160   oldRect.right = wpMain.x + wpMain.width;\r
2161   oldRect.bottom = wpMain.y + wpMain.height;\r
2162 \r
2163   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2164   smallLayout = sizeInfo[boardSize].smallLayout;\r
2165   squareSize = sizeInfo[boardSize].squareSize;\r
2166   lineGap = sizeInfo[boardSize].lineGap;\r
2167   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2168 \r
2169   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2170       lineGap = appData.overrideLineGap;\r
2171   }\r
2172 \r
2173   if (tinyLayout != oldTinyLayout) {\r
2174     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
2175     if (tinyLayout) {\r
2176       style &= ~WS_SYSMENU;\r
2177       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2178                  "&Minimize\tCtrl+F4");\r
2179     } else {\r
2180       style |= WS_SYSMENU;\r
2181       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2182     }\r
2183     SetWindowLong(hwndMain, GWL_STYLE, style);\r
2184 \r
2185     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2186       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2187         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2188     }\r
2189     DrawMenuBar(hwndMain);\r
2190   }\r
2191 \r
2192   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2193   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2194 \r
2195   /* Get text area sizes */\r
2196   hdc = GetDC(hwndMain);\r
2197   if (appData.clockMode) {\r
2198     sprintf(buf, _("White: %s"), TimeString(23*60*60*1000L));\r
2199   } else {\r
2200     sprintf(buf, _("White"));\r
2201   }\r
2202   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2203   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2204   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2205   str = _("We only care about the height here");\r
2206   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2207   SelectObject(hdc, oldFont);\r
2208   ReleaseDC(hwndMain, hdc);\r
2209 \r
2210   /* Compute where everything goes */\r
2211   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2212         /* [HGM] logo: if either logo is on, reserve space for it */\r
2213         logoHeight =  2*clockSize.cy;\r
2214         leftLogoRect.left   = OUTER_MARGIN;\r
2215         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2216         leftLogoRect.top    = OUTER_MARGIN;\r
2217         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2218 \r
2219         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2220         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2221         rightLogoRect.top    = OUTER_MARGIN;\r
2222         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2223 \r
2224 \r
2225     whiteRect.left = leftLogoRect.right;\r
2226     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2227     whiteRect.top = OUTER_MARGIN;\r
2228     whiteRect.bottom = whiteRect.top + logoHeight;\r
2229 \r
2230     blackRect.right = rightLogoRect.left;\r
2231     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2232     blackRect.top = whiteRect.top;\r
2233     blackRect.bottom = whiteRect.bottom;\r
2234   } else {\r
2235     whiteRect.left = OUTER_MARGIN;\r
2236     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2237     whiteRect.top = OUTER_MARGIN;\r
2238     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2239 \r
2240     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2241     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2242     blackRect.top = whiteRect.top;\r
2243     blackRect.bottom = whiteRect.bottom;\r
2244 \r
2245     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2246   }\r
2247 \r
2248   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2249   if (appData.showButtonBar) {\r
2250     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2251       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2252   } else {\r
2253     messageRect.right = OUTER_MARGIN + boardWidth;\r
2254   }\r
2255   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2256   messageRect.bottom = messageRect.top + messageSize.cy;\r
2257 \r
2258   boardRect.left = OUTER_MARGIN;\r
2259   boardRect.right = boardRect.left + boardWidth;\r
2260   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2261   boardRect.bottom = boardRect.top + boardHeight;\r
2262 \r
2263   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2264   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2265   oldBoardSize = boardSize;\r
2266   oldTinyLayout = tinyLayout;\r
2267   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2268   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2269     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2270   winW *= 1 + twoBoards;\r
2271   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2272   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2273   wpMain.height = winH; //       without disturbing window attachments\r
2274   GetWindowRect(hwndMain, &wrect);\r
2275   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2276                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2277 \r
2278   // [HGM] placement: let attached windows follow size change.\r
2279   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2280   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2281   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2282   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2283   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2284 \r
2285   /* compensate if menu bar wrapped */\r
2286   GetClientRect(hwndMain, &crect);\r
2287   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2288   wpMain.height += offby;\r
2289   switch (flags) {\r
2290   case WMSZ_TOPLEFT:\r
2291     SetWindowPos(hwndMain, NULL, \r
2292                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2293                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2294     break;\r
2295 \r
2296   case WMSZ_TOPRIGHT:\r
2297   case WMSZ_TOP:\r
2298     SetWindowPos(hwndMain, NULL, \r
2299                  wrect.left, wrect.bottom - wpMain.height, \r
2300                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2301     break;\r
2302 \r
2303   case WMSZ_BOTTOMLEFT:\r
2304   case WMSZ_LEFT:\r
2305     SetWindowPos(hwndMain, NULL, \r
2306                  wrect.right - wpMain.width, wrect.top, \r
2307                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2308     break;\r
2309 \r
2310   case WMSZ_BOTTOMRIGHT:\r
2311   case WMSZ_BOTTOM:\r
2312   case WMSZ_RIGHT:\r
2313   default:\r
2314     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2315                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2316     break;\r
2317   }\r
2318 \r
2319   hwndPause = NULL;\r
2320   for (i = 0; i < N_BUTTONS; i++) {\r
2321     if (buttonDesc[i].hwnd != NULL) {\r
2322       DestroyWindow(buttonDesc[i].hwnd);\r
2323       buttonDesc[i].hwnd = NULL;\r
2324     }\r
2325     if (appData.showButtonBar) {\r
2326       buttonDesc[i].hwnd =\r
2327         CreateWindow("BUTTON", buttonDesc[i].label,\r
2328                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2329                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2330                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2331                      (HMENU) buttonDesc[i].id,\r
2332                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
2333       if (tinyLayout) {\r
2334         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2335                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2336                     MAKELPARAM(FALSE, 0));\r
2337       }\r
2338       if (buttonDesc[i].id == IDM_Pause)\r
2339         hwndPause = buttonDesc[i].hwnd;\r
2340       buttonDesc[i].wndproc = (WNDPROC)\r
2341         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
2342     }\r
2343   }\r
2344   if (gridPen != NULL) DeleteObject(gridPen);\r
2345   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2346   if (premovePen != NULL) DeleteObject(premovePen);\r
2347   if (lineGap != 0) {\r
2348     logbrush.lbStyle = BS_SOLID;\r
2349     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2350     gridPen =\r
2351       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2352                    lineGap, &logbrush, 0, NULL);\r
2353     logbrush.lbColor = highlightSquareColor;\r
2354     highlightPen =\r
2355       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2356                    lineGap, &logbrush, 0, NULL);\r
2357 \r
2358     logbrush.lbColor = premoveHighlightColor; \r
2359     premovePen =\r
2360       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2361                    lineGap, &logbrush, 0, NULL);\r
2362 \r
2363     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2364     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2365       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2366       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2367         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2368       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2369         BOARD_WIDTH * (squareSize + lineGap);\r
2370       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2371     }\r
2372     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2373       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2374       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2375         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2376         lineGap / 2 + (i * (squareSize + lineGap));\r
2377       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2378         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2379       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2380     }\r
2381   }\r
2382 \r
2383   /* [HGM] Licensing requirement */\r
2384 #ifdef GOTHIC\r
2385   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2386 #endif\r
2387 #ifdef FALCON\r
2388   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2389 #endif\r
2390   GothicPopUp( "", VariantNormal);\r
2391 \r
2392 \r
2393 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2394 \r
2395   /* Load piece bitmaps for this board size */\r
2396   for (i=0; i<=2; i++) {\r
2397     for (piece = WhitePawn;\r
2398          (int) piece < (int) BlackPawn;\r
2399          piece = (ChessSquare) ((int) piece + 1)) {\r
2400       if (pieceBitmap[i][piece] != NULL)\r
2401         DeleteObject(pieceBitmap[i][piece]);\r
2402     }\r
2403   }\r
2404 \r
2405   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2406   // Orthodox Chess pieces\r
2407   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2408   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2409   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2410   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2411   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2412   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2413   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2414   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2415   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2416   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2417   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2418   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2419   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2420   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2421   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2422   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
2423     // in Shogi, Hijack the unused Queen for Lance\r
2424     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2425     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2426     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2427   } else {\r
2428     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2429     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2430     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2431   }\r
2432 \r
2433   if(squareSize <= 72 && squareSize >= 33) { \r
2434     /* A & C are available in most sizes now */\r
2435     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2436       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2437       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2438       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2439       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2440       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2441       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2442       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2443       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2444       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2445       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2446       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2447       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2448     } else { // Smirf-like\r
2449       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2450       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2451       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2452     }\r
2453     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2454       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2455       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2456       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2457     } else { // WinBoard standard\r
2458       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2459       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2460       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2461     }\r
2462   }\r
2463 \r
2464 \r
2465   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2466     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2467     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2468     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2469     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2470     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2471     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2472     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2473     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2474     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2475     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2476     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2477     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2478     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2479     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2480     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2481     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2482     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2483     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2484     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2485     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2486     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2487     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2488     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2489     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2490     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2491     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2492     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2493     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2494     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2495     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2496 \r
2497     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2498       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2499       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2500       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2501       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2502       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2503       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2504       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2505       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2506       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2507       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2508       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2509       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2510     } else {\r
2511       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2512       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2513       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2514       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2515       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2516       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2517       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2518       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2519       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2520       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2521       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2522       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2523     }\r
2524 \r
2525   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2526     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2527     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2528     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2529     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2530     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2531     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2532     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2533     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2534     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2535     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2536     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2537     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2538     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2539     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2540   }\r
2541 \r
2542 \r
2543   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2544   /* special Shogi support in this size */\r
2545   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2546       for (piece = WhitePawn;\r
2547            (int) piece < (int) BlackPawn;\r
2548            piece = (ChessSquare) ((int) piece + 1)) {\r
2549         if (pieceBitmap[i][piece] != NULL)\r
2550           DeleteObject(pieceBitmap[i][piece]);\r
2551       }\r
2552     }\r
2553   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2554   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2555   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2556   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2557   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2558   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2559   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2560   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2561   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2562   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2563   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2564   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2565   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2566   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2567   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2568   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2569   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2570   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2571   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2572   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2573   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2574   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2575   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2576   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2577   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2578   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2579   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2580   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2581   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2582   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2583   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2584   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2585   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2586   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2587   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2588   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2589   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2590   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2591   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2592   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2593   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2594   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2595   minorSize = 0;\r
2596   }\r
2597 }\r
2598 \r
2599 HBITMAP\r
2600 PieceBitmap(ChessSquare p, int kind)\r
2601 {\r
2602   if ((int) p >= (int) BlackPawn)\r
2603     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2604 \r
2605   return pieceBitmap[kind][(int) p];\r
2606 }\r
2607 \r
2608 /***************************************************************/\r
2609 \r
2610 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2611 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2612 /*\r
2613 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2614 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2615 */\r
2616 \r
2617 VOID\r
2618 SquareToPos(int row, int column, int * x, int * y)\r
2619 {\r
2620   if (flipView) {\r
2621     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2622     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2623   } else {\r
2624     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2625     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2626   }\r
2627 }\r
2628 \r
2629 VOID\r
2630 DrawCoordsOnDC(HDC hdc)\r
2631 {\r
2632   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
2633   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
2634   char str[2] = { NULLCHAR, NULLCHAR };\r
2635   int oldMode, oldAlign, x, y, start, i;\r
2636   HFONT oldFont;\r
2637   HBRUSH oldBrush;\r
2638 \r
2639   if (!appData.showCoords)\r
2640     return;\r
2641 \r
2642   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2643 \r
2644   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2645   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2646   oldAlign = GetTextAlign(hdc);\r
2647   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2648 \r
2649   y = boardRect.top + lineGap;\r
2650   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2651 \r
2652   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2653   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2654     str[0] = files[start + i];\r
2655     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2656     y += squareSize + lineGap;\r
2657   }\r
2658 \r
2659   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2660 \r
2661   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2662   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2663     str[0] = ranks[start + i];\r
2664     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2665     x += squareSize + lineGap;\r
2666   }    \r
2667 \r
2668   SelectObject(hdc, oldBrush);\r
2669   SetBkMode(hdc, oldMode);\r
2670   SetTextAlign(hdc, oldAlign);\r
2671   SelectObject(hdc, oldFont);\r
2672 }\r
2673 \r
2674 VOID\r
2675 DrawGridOnDC(HDC hdc)\r
2676 {\r
2677   HPEN oldPen;\r
2678  \r
2679   if (lineGap != 0) {\r
2680     oldPen = SelectObject(hdc, gridPen);\r
2681     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2682     SelectObject(hdc, oldPen);\r
2683   }\r
2684 }\r
2685 \r
2686 #define HIGHLIGHT_PEN 0\r
2687 #define PREMOVE_PEN   1\r
2688 \r
2689 VOID\r
2690 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2691 {\r
2692   int x1, y1;\r
2693   HPEN oldPen, hPen;\r
2694   if (lineGap == 0) return;\r
2695   if (flipView) {\r
2696     x1 = boardRect.left +\r
2697       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2698     y1 = boardRect.top +\r
2699       lineGap/2 + y * (squareSize + lineGap);\r
2700   } else {\r
2701     x1 = boardRect.left +\r
2702       lineGap/2 + x * (squareSize + lineGap);\r
2703     y1 = boardRect.top +\r
2704       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2705   }\r
2706   hPen = pen ? premovePen : highlightPen;\r
2707   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2708   MoveToEx(hdc, x1, y1, NULL);\r
2709   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2710   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2711   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2712   LineTo(hdc, x1, y1);\r
2713   SelectObject(hdc, oldPen);\r
2714 }\r
2715 \r
2716 VOID\r
2717 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2718 {\r
2719   int i;\r
2720   for (i=0; i<2; i++) {\r
2721     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2722       DrawHighlightOnDC(hdc, TRUE,\r
2723                         h->sq[i].x, h->sq[i].y,\r
2724                         pen);\r
2725   }\r
2726 }\r
2727 \r
2728 /* Note: sqcolor is used only in monoMode */\r
2729 /* Note that this code is largely duplicated in woptions.c,\r
2730    function DrawSampleSquare, so that needs to be updated too */\r
2731 VOID\r
2732 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2733 {\r
2734   HBITMAP oldBitmap;\r
2735   HBRUSH oldBrush;\r
2736   int tmpSize;\r
2737 \r
2738   if (appData.blindfold) return;\r
2739 \r
2740   /* [AS] Use font-based pieces if needed */\r
2741   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2742     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2743     CreatePiecesFromFont();\r
2744 \r
2745     if( fontBitmapSquareSize == squareSize ) {\r
2746         int index = TranslatePieceToFontPiece(piece);\r
2747 \r
2748         SelectObject( tmphdc, hPieceMask[ index ] );\r
2749 \r
2750       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2751         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2752       else\r
2753         BitBlt( hdc,\r
2754             x, y,\r
2755             squareSize, squareSize,\r
2756             tmphdc,\r
2757             0, 0,\r
2758             SRCAND );\r
2759 \r
2760         SelectObject( tmphdc, hPieceFace[ index ] );\r
2761 \r
2762       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2763         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2764       else\r
2765         BitBlt( hdc,\r
2766             x, y,\r
2767             squareSize, squareSize,\r
2768             tmphdc,\r
2769             0, 0,\r
2770             SRCPAINT );\r
2771 \r
2772         return;\r
2773     }\r
2774   }\r
2775 \r
2776   if (appData.monoMode) {\r
2777     SelectObject(tmphdc, PieceBitmap(piece, \r
2778       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2779     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2780            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2781   } else {\r
2782     tmpSize = squareSize;\r
2783     if(minorSize &&\r
2784         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2785          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2786       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2787       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2788       x += (squareSize - minorSize)>>1;\r
2789       y += squareSize - minorSize - 2;\r
2790       tmpSize = minorSize;\r
2791     }\r
2792     if (color || appData.allWhite ) {\r
2793       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2794       if( color )\r
2795               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2796       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2797       if(appData.upsideDown && color==flipView)\r
2798         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2799       else\r
2800         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2801       /* Use black for outline of white pieces */\r
2802       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2803       if(appData.upsideDown && color==flipView)\r
2804         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2805       else\r
2806         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2807     } else {\r
2808       /* Use square color for details of black pieces */\r
2809       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2810       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2811       if(appData.upsideDown && !flipView)\r
2812         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2813       else\r
2814         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2815     }\r
2816     SelectObject(hdc, oldBrush);\r
2817     SelectObject(tmphdc, oldBitmap);\r
2818   }\r
2819 }\r
2820 \r
2821 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2822 int GetBackTextureMode( int algo )\r
2823 {\r
2824     int result = BACK_TEXTURE_MODE_DISABLED;\r
2825 \r
2826     switch( algo ) \r
2827     {\r
2828         case BACK_TEXTURE_MODE_PLAIN:\r
2829             result = 1; /* Always use identity map */\r
2830             break;\r
2831         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2832             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2833             break;\r
2834     }\r
2835 \r
2836     return result;\r
2837 }\r
2838 \r
2839 /* \r
2840     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2841     to handle redraws cleanly (as random numbers would always be different).\r
2842 */\r
2843 VOID RebuildTextureSquareInfo()\r
2844 {\r
2845     BITMAP bi;\r
2846     int lite_w = 0;\r
2847     int lite_h = 0;\r
2848     int dark_w = 0;\r
2849     int dark_h = 0;\r
2850     int row;\r
2851     int col;\r
2852 \r
2853     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2854 \r
2855     if( liteBackTexture != NULL ) {\r
2856         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2857             lite_w = bi.bmWidth;\r
2858             lite_h = bi.bmHeight;\r
2859         }\r
2860     }\r
2861 \r
2862     if( darkBackTexture != NULL ) {\r
2863         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2864             dark_w = bi.bmWidth;\r
2865             dark_h = bi.bmHeight;\r
2866         }\r
2867     }\r
2868 \r
2869     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2870         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2871             if( (col + row) & 1 ) {\r
2872                 /* Lite square */\r
2873                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2874                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2875                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2876                   else\r
2877                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2878                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2879                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2880                   else\r
2881                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2882                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2883                 }\r
2884             }\r
2885             else {\r
2886                 /* Dark square */\r
2887                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2888                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2889                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2890                   else\r
2891                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2892                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2893                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2894                   else\r
2895                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2896                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2897                 }\r
2898             }\r
2899         }\r
2900     }\r
2901 }\r
2902 \r
2903 /* [AS] Arrow highlighting support */\r
2904 \r
2905 static int A_WIDTH = 5; /* Width of arrow body */\r
2906 \r
2907 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2908 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2909 \r
2910 static double Sqr( double x )\r
2911 {\r
2912     return x*x;\r
2913 }\r
2914 \r
2915 static int Round( double x )\r
2916 {\r
2917     return (int) (x + 0.5);\r
2918 }\r
2919 \r
2920 /* Draw an arrow between two points using current settings */\r
2921 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2922 {\r
2923     POINT arrow[7];\r
2924     double dx, dy, j, k, x, y;\r
2925 \r
2926     if( d_x == s_x ) {\r
2927         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2928 \r
2929         arrow[0].x = s_x + A_WIDTH;\r
2930         arrow[0].y = s_y;\r
2931 \r
2932         arrow[1].x = s_x + A_WIDTH;\r
2933         arrow[1].y = d_y - h;\r
2934 \r
2935         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
2936         arrow[2].y = d_y - h;\r
2937 \r
2938         arrow[3].x = d_x;\r
2939         arrow[3].y = d_y;\r
2940 \r
2941         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
2942         arrow[4].y = d_y - h;\r
2943 \r
2944         arrow[5].x = s_x - A_WIDTH;\r
2945         arrow[5].y = d_y - h;\r
2946 \r
2947         arrow[6].x = s_x - A_WIDTH;\r
2948         arrow[6].y = s_y;\r
2949     }\r
2950     else if( d_y == s_y ) {\r
2951         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2952 \r
2953         arrow[0].x = s_x;\r
2954         arrow[0].y = s_y + A_WIDTH;\r
2955 \r
2956         arrow[1].x = d_x - w;\r
2957         arrow[1].y = s_y + A_WIDTH;\r
2958 \r
2959         arrow[2].x = d_x - w;\r
2960         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
2961 \r
2962         arrow[3].x = d_x;\r
2963         arrow[3].y = d_y;\r
2964 \r
2965         arrow[4].x = d_x - w;\r
2966         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
2967 \r
2968         arrow[5].x = d_x - w;\r
2969         arrow[5].y = s_y - A_WIDTH;\r
2970 \r
2971         arrow[6].x = s_x;\r
2972         arrow[6].y = s_y - A_WIDTH;\r
2973     }\r
2974     else {\r
2975         /* [AS] Needed a lot of paper for this! :-) */\r
2976         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
2977         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
2978   \r
2979         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
2980 \r
2981         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
2982 \r
2983         x = s_x;\r
2984         y = s_y;\r
2985 \r
2986         arrow[0].x = Round(x - j);\r
2987         arrow[0].y = Round(y + j*dx);\r
2988 \r
2989         arrow[1].x = Round(x + j);\r
2990         arrow[1].y = Round(y - j*dx);\r
2991 \r
2992         if( d_x > s_x ) {\r
2993             x = (double) d_x - k;\r
2994             y = (double) d_y - k*dy;\r
2995         }\r
2996         else {\r
2997             x = (double) d_x + k;\r
2998             y = (double) d_y + k*dy;\r
2999         }\r
3000 \r
3001         arrow[2].x = Round(x + j);\r
3002         arrow[2].y = Round(y - j*dx);\r
3003 \r
3004         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3005         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3006 \r
3007         arrow[4].x = d_x;\r
3008         arrow[4].y = d_y;\r
3009 \r
3010         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3011         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3012 \r
3013         arrow[6].x = Round(x - j);\r
3014         arrow[6].y = Round(y + j*dx);\r
3015     }\r
3016 \r
3017     Polygon( hdc, arrow, 7 );\r
3018 }\r
3019 \r
3020 /* [AS] Draw an arrow between two squares */\r
3021 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3022 {\r
3023     int s_x, s_y, d_x, d_y;\r
3024     HPEN hpen;\r
3025     HPEN holdpen;\r
3026     HBRUSH hbrush;\r
3027     HBRUSH holdbrush;\r
3028     LOGBRUSH stLB;\r
3029 \r
3030     if( s_col == d_col && s_row == d_row ) {\r
3031         return;\r
3032     }\r
3033 \r
3034     /* Get source and destination points */\r
3035     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3036     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3037 \r
3038     if( d_y > s_y ) {\r
3039         d_y += squareSize / 4;\r
3040     }\r
3041     else if( d_y < s_y ) {\r
3042         d_y += 3 * squareSize / 4;\r
3043     }\r
3044     else {\r
3045         d_y += squareSize / 2;\r
3046     }\r
3047 \r
3048     if( d_x > s_x ) {\r
3049         d_x += squareSize / 4;\r
3050     }\r
3051     else if( d_x < s_x ) {\r
3052         d_x += 3 * squareSize / 4;\r
3053     }\r
3054     else {\r
3055         d_x += squareSize / 2;\r
3056     }\r
3057 \r
3058     s_x += squareSize / 2;\r
3059     s_y += squareSize / 2;\r
3060 \r
3061     /* Adjust width */\r
3062     A_WIDTH = squareSize / 14;\r
3063 \r
3064     /* Draw */\r
3065     stLB.lbStyle = BS_SOLID;\r
3066     stLB.lbColor = appData.highlightArrowColor;\r
3067     stLB.lbHatch = 0;\r
3068 \r
3069     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3070     holdpen = SelectObject( hdc, hpen );\r
3071     hbrush = CreateBrushIndirect( &stLB );\r
3072     holdbrush = SelectObject( hdc, hbrush );\r
3073 \r
3074     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3075 \r
3076     SelectObject( hdc, holdpen );\r
3077     SelectObject( hdc, holdbrush );\r
3078     DeleteObject( hpen );\r
3079     DeleteObject( hbrush );\r
3080 }\r
3081 \r
3082 BOOL HasHighlightInfo()\r
3083 {\r
3084     BOOL result = FALSE;\r
3085 \r
3086     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3087         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3088     {\r
3089         result = TRUE;\r
3090     }\r
3091 \r
3092     return result;\r
3093 }\r
3094 \r
3095 BOOL IsDrawArrowEnabled()\r
3096 {\r
3097     BOOL result = FALSE;\r
3098 \r
3099     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3100         result = TRUE;\r
3101     }\r
3102 \r
3103     return result;\r
3104 }\r
3105 \r
3106 VOID DrawArrowHighlight( HDC hdc )\r
3107 {\r
3108     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3109         DrawArrowBetweenSquares( hdc,\r
3110             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3111             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3112     }\r
3113 }\r
3114 \r
3115 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3116 {\r
3117     HRGN result = NULL;\r
3118 \r
3119     if( HasHighlightInfo() ) {\r
3120         int x1, y1, x2, y2;\r
3121         int sx, sy, dx, dy;\r
3122 \r
3123         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3124         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3125 \r
3126         sx = MIN( x1, x2 );\r
3127         sy = MIN( y1, y2 );\r
3128         dx = MAX( x1, x2 ) + squareSize;\r
3129         dy = MAX( y1, y2 ) + squareSize;\r
3130 \r
3131         result = CreateRectRgn( sx, sy, dx, dy );\r
3132     }\r
3133 \r
3134     return result;\r
3135 }\r
3136 \r
3137 /*\r
3138     Warning: this function modifies the behavior of several other functions. \r
3139     \r
3140     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3141     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3142     repaint is scattered all over the place, which is not good for features such as\r
3143     "arrow highlighting" that require a full repaint of the board.\r
3144 \r
3145     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3146     user interaction, when speed is not so important) but especially to avoid errors\r
3147     in the displayed graphics.\r
3148 \r
3149     In such patched places, I always try refer to this function so there is a single\r
3150     place to maintain knowledge.\r
3151     \r
3152     To restore the original behavior, just return FALSE unconditionally.\r
3153 */\r
3154 BOOL IsFullRepaintPreferrable()\r
3155 {\r
3156     BOOL result = FALSE;\r
3157 \r
3158     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3159         /* Arrow may appear on the board */\r
3160         result = TRUE;\r
3161     }\r
3162 \r
3163     return result;\r
3164 }\r
3165 \r
3166 /* \r
3167     This function is called by DrawPosition to know whether a full repaint must\r
3168     be forced or not.\r
3169 \r
3170     Only DrawPosition may directly call this function, which makes use of \r
3171     some state information. Other function should call DrawPosition specifying \r
3172     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3173 */\r
3174 BOOL DrawPositionNeedsFullRepaint()\r
3175 {\r
3176     BOOL result = FALSE;\r
3177 \r
3178     /* \r
3179         Probably a slightly better policy would be to trigger a full repaint\r
3180         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3181         but animation is fast enough that it's difficult to notice.\r
3182     */\r
3183     if( animInfo.piece == EmptySquare ) {\r
3184         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3185             result = TRUE;\r
3186         }\r
3187     }\r
3188 \r
3189     return result;\r
3190 }\r
3191 \r
3192 VOID\r
3193 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3194 {\r
3195   int row, column, x, y, square_color, piece_color;\r
3196   ChessSquare piece;\r
3197   HBRUSH oldBrush;\r
3198   HDC texture_hdc = NULL;\r
3199 \r
3200   /* [AS] Initialize background textures if needed */\r
3201   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3202       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3203       if( backTextureSquareSize != squareSize \r
3204        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3205           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3206           backTextureSquareSize = squareSize;\r
3207           RebuildTextureSquareInfo();\r
3208       }\r
3209 \r
3210       texture_hdc = CreateCompatibleDC( hdc );\r
3211   }\r
3212 \r
3213   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3214     for (column = 0; column < BOARD_WIDTH; column++) {\r
3215   \r
3216       SquareToPos(row, column, &x, &y);\r
3217 \r
3218       piece = board[row][column];\r
3219 \r
3220       square_color = ((column + row) % 2) == 1;\r
3221       if( gameInfo.variant == VariantXiangqi ) {\r
3222           square_color = !InPalace(row, column);\r
3223           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3224           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3225       }\r
3226       piece_color = (int) piece < (int) BlackPawn;\r
3227 \r
3228 \r
3229       /* [HGM] holdings file: light square or black */\r
3230       if(column == BOARD_LEFT-2) {\r
3231             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3232                 square_color = 1;\r
3233             else {\r
3234                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3235                 continue;\r
3236             }\r
3237       } else\r
3238       if(column == BOARD_RGHT + 1 ) {\r
3239             if( row < gameInfo.holdingsSize )\r
3240                 square_color = 1;\r
3241             else {\r
3242                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3243                 continue;\r
3244             }\r
3245       }\r
3246       if(column == BOARD_LEFT-1 ) /* left align */\r
3247             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3248       else if( column == BOARD_RGHT) /* right align */\r
3249             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3250       else\r
3251       if (appData.monoMode) {\r
3252         if (piece == EmptySquare) {\r
3253           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3254                  square_color ? WHITENESS : BLACKNESS);\r
3255         } else {\r
3256           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3257         }\r
3258       } \r
3259       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3260           /* [AS] Draw the square using a texture bitmap */\r
3261           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3262           int r = row, c = column; // [HGM] do not flip board in flipView\r
3263           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3264 \r
3265           DrawTile( x, y, \r
3266               squareSize, squareSize, \r
3267               hdc, \r
3268               texture_hdc,\r
3269               backTextureSquareInfo[r][c].mode,\r
3270               backTextureSquareInfo[r][c].x,\r
3271               backTextureSquareInfo[r][c].y );\r
3272 \r
3273           SelectObject( texture_hdc, hbm );\r
3274 \r
3275           if (piece != EmptySquare) {\r
3276               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3277           }\r
3278       }\r
3279       else {\r
3280         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3281 \r
3282         oldBrush = SelectObject(hdc, brush );\r
3283         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3284         SelectObject(hdc, oldBrush);\r
3285         if (piece != EmptySquare)\r
3286           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3287       }\r
3288     }\r
3289   }\r
3290 \r
3291   if( texture_hdc != NULL ) {\r
3292     DeleteDC( texture_hdc );\r
3293   }\r
3294 }\r
3295 \r
3296 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3297 void fputDW(FILE *f, int x)\r
3298 {\r
3299         fputc(x     & 255, f);\r
3300         fputc(x>>8  & 255, f);\r
3301         fputc(x>>16 & 255, f);\r
3302         fputc(x>>24 & 255, f);\r
3303 }\r
3304 \r
3305 #define MAX_CLIPS 200   /* more than enough */\r
3306 \r
3307 VOID\r
3308 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3309 {\r
3310 //  HBITMAP bufferBitmap;\r
3311   BITMAP bi;\r
3312 //  RECT Rect;\r
3313   HDC tmphdc;\r
3314   HBITMAP hbm;\r
3315   int w = 100, h = 50;\r
3316 \r
3317   if(logo == NULL) return;\r
3318 //  GetClientRect(hwndMain, &Rect);\r
3319 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3320 //                                      Rect.bottom-Rect.top+1);\r
3321   tmphdc = CreateCompatibleDC(hdc);\r
3322   hbm = SelectObject(tmphdc, logo);\r
3323   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3324             w = bi.bmWidth;\r
3325             h = bi.bmHeight;\r
3326   }\r
3327   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3328                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3329   SelectObject(tmphdc, hbm);\r
3330   DeleteDC(tmphdc);\r
3331 }\r
3332 \r
3333 static HDC hdcSeek;\r
3334 \r
3335 // [HGM] seekgraph\r
3336 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3337 {\r
3338     POINT stPt;\r
3339     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3340     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3341     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3342     SelectObject( hdcSeek, hp );\r
3343 }\r
3344 \r
3345 // front-end wrapper for drawing functions to do rectangles\r
3346 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3347 {\r
3348     HPEN hp;\r
3349     RECT rc;\r
3350 \r
3351     if (hdcSeek == NULL) {\r
3352     hdcSeek = GetDC(hwndMain);\r
3353       if (!appData.monoMode) {\r
3354         SelectPalette(hdcSeek, hPal, FALSE);\r
3355         RealizePalette(hdcSeek);\r
3356       }\r
3357     }\r
3358     hp = SelectObject( hdcSeek, gridPen );\r
3359     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3360     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3361     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3362     SelectObject( hdcSeek, hp );\r
3363 }\r
3364 \r
3365 // front-end wrapper for putting text in graph\r
3366 void DrawSeekText(char *buf, int x, int y)\r
3367 {\r
3368         SIZE stSize;\r
3369         SetBkMode( hdcSeek, TRANSPARENT );\r
3370         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3371         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3372 }\r
3373 \r
3374 void DrawSeekDot(int x, int y, int color)\r
3375 {\r
3376         int square = color & 0x80;\r
3377         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3378                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3379         color &= 0x7F;\r
3380         if(square)\r
3381             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3382                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3383         else\r
3384             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3385                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3386             SelectObject(hdcSeek, oldBrush);\r
3387 }\r
3388 \r
3389 VOID\r
3390 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3391 {\r
3392   static Board lastReq[2], lastDrawn[2];\r
3393   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3394   static int lastDrawnFlipView = 0;\r
3395   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3396   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3397   HDC tmphdc;\r
3398   HDC hdcmem;\r
3399   HBITMAP bufferBitmap;\r
3400   HBITMAP oldBitmap;\r
3401   RECT Rect;\r
3402   HRGN clips[MAX_CLIPS];\r
3403   ChessSquare dragged_piece = EmptySquare;\r
3404   int nr = twoBoards*partnerUp;\r
3405 \r
3406   /* I'm undecided on this - this function figures out whether a full\r
3407    * repaint is necessary on its own, so there's no real reason to have the\r
3408    * caller tell it that.  I think this can safely be set to FALSE - but\r
3409    * if we trust the callers not to request full repaints unnessesarily, then\r
3410    * we could skip some clipping work.  In other words, only request a full\r
3411    * redraw when the majority of pieces have changed positions (ie. flip, \r
3412    * gamestart and similar)  --Hawk\r
3413    */\r
3414   Boolean fullrepaint = repaint;\r
3415 \r
3416   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3417 \r
3418   if( DrawPositionNeedsFullRepaint() ) {\r
3419       fullrepaint = TRUE;\r
3420   }\r
3421 \r
3422   if (board == NULL) {\r
3423     if (!lastReqValid[nr]) {\r
3424       return;\r
3425     }\r
3426     board = lastReq[nr];\r
3427   } else {\r
3428     CopyBoard(lastReq[nr], board);\r
3429     lastReqValid[nr] = 1;\r
3430   }\r
3431 \r
3432   if (doingSizing) {\r
3433     return;\r
3434   }\r
3435 \r
3436   if (IsIconic(hwndMain)) {\r
3437     return;\r
3438   }\r
3439 \r
3440   if (hdc == NULL) {\r
3441     hdc = GetDC(hwndMain);\r
3442     if (!appData.monoMode) {\r
3443       SelectPalette(hdc, hPal, FALSE);\r
3444       RealizePalette(hdc);\r
3445     }\r
3446     releaseDC = TRUE;\r
3447   } else {\r
3448     releaseDC = FALSE;\r
3449   }\r
3450 \r
3451   /* Create some work-DCs */\r
3452   hdcmem = CreateCompatibleDC(hdc);\r
3453   tmphdc = CreateCompatibleDC(hdc);\r
3454 \r
3455   /* If dragging is in progress, we temporarely remove the piece */\r
3456   /* [HGM] or temporarily decrease count if stacked              */\r
3457   /*       !! Moved to before board compare !!                   */\r
3458   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3459     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3460     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3461             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3462         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3463     } else \r
3464     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3465             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3466         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3467     } else \r
3468         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3469   }\r
3470 \r
3471   /* Figure out which squares need updating by comparing the \r
3472    * newest board with the last drawn board and checking if\r
3473    * flipping has changed.\r
3474    */\r
3475   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3476     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3477       for (column = 0; column < BOARD_WIDTH; column++) {\r
3478         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3479           SquareToPos(row, column, &x, &y);\r
3480           clips[num_clips++] =\r
3481             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3482         }\r
3483       }\r
3484     }\r
3485    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3486     for (i=0; i<2; i++) {\r
3487       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3488           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3489         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3490             lastDrawnHighlight.sq[i].y >= 0) {\r
3491           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3492                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3493           clips[num_clips++] =\r
3494             CreateRectRgn(x - lineGap, y - lineGap, \r
3495                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3496         }\r
3497         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3498           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3499           clips[num_clips++] =\r
3500             CreateRectRgn(x - lineGap, y - lineGap, \r
3501                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3502         }\r
3503       }\r
3504     }\r
3505     for (i=0; i<2; i++) {\r
3506       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3507           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3508         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3509             lastDrawnPremove.sq[i].y >= 0) {\r
3510           SquareToPos(lastDrawnPremove.sq[i].y,\r
3511                       lastDrawnPremove.sq[i].x, &x, &y);\r
3512           clips[num_clips++] =\r
3513             CreateRectRgn(x - lineGap, y - lineGap, \r
3514                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3515         }\r
3516         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3517             premoveHighlightInfo.sq[i].y >= 0) {\r
3518           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3519                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3520           clips[num_clips++] =\r
3521             CreateRectRgn(x - lineGap, y - lineGap, \r
3522                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3523         }\r
3524       }\r
3525     }\r
3526    } else { // nr == 1\r
3527         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3528         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3529         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3530         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3531       for (i=0; i<2; i++) {\r
3532         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3533             partnerHighlightInfo.sq[i].y >= 0) {\r
3534           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3535                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3536           clips[num_clips++] =\r
3537             CreateRectRgn(x - lineGap, y - lineGap, \r
3538                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3539         }\r
3540         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3541             oldPartnerHighlight.sq[i].y >= 0) {\r
3542           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3543                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3544           clips[num_clips++] =\r
3545             CreateRectRgn(x - lineGap, y - lineGap, \r
3546                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3547         }\r
3548       }\r
3549    }\r
3550   } else {\r
3551     fullrepaint = TRUE;\r
3552   }\r
3553 \r
3554   /* Create a buffer bitmap - this is the actual bitmap\r
3555    * being written to.  When all the work is done, we can\r
3556    * copy it to the real DC (the screen).  This avoids\r
3557    * the problems with flickering.\r
3558    */\r
3559   GetClientRect(hwndMain, &Rect);\r
3560   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3561                                         Rect.bottom-Rect.top+1);\r
3562   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3563   if (!appData.monoMode) {\r
3564     SelectPalette(hdcmem, hPal, FALSE);\r
3565   }\r
3566 \r
3567   /* Create clips for dragging */\r
3568   if (!fullrepaint) {\r
3569     if (dragInfo.from.x >= 0) {\r
3570       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3571       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3572     }\r
3573     if (dragInfo.start.x >= 0) {\r
3574       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3575       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3576     }\r
3577     if (dragInfo.pos.x >= 0) {\r
3578       x = dragInfo.pos.x - squareSize / 2;\r
3579       y = dragInfo.pos.y - squareSize / 2;\r
3580       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3581     }\r
3582     if (dragInfo.lastpos.x >= 0) {\r
3583       x = dragInfo.lastpos.x - squareSize / 2;\r
3584       y = dragInfo.lastpos.y - squareSize / 2;\r
3585       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3586     }\r
3587   }\r
3588 \r
3589   /* Are we animating a move?  \r
3590    * If so, \r
3591    *   - remove the piece from the board (temporarely)\r
3592    *   - calculate the clipping region\r
3593    */\r
3594   if (!fullrepaint) {\r
3595     if (animInfo.piece != EmptySquare) {\r
3596       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3597       x = boardRect.left + animInfo.lastpos.x;\r
3598       y = boardRect.top + animInfo.lastpos.y;\r
3599       x2 = boardRect.left + animInfo.pos.x;\r
3600       y2 = boardRect.top + animInfo.pos.y;\r
3601       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3602       /* Slight kludge.  The real problem is that after AnimateMove is\r
3603          done, the position on the screen does not match lastDrawn.\r
3604          This currently causes trouble only on e.p. captures in\r
3605          atomic, where the piece moves to an empty square and then\r
3606          explodes.  The old and new positions both had an empty square\r
3607          at the destination, but animation has drawn a piece there and\r
3608          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3609       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3610     }\r
3611   }\r
3612 \r
3613   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3614   if (num_clips == 0)\r
3615     fullrepaint = TRUE;\r
3616 \r
3617   /* Set clipping on the memory DC */\r
3618   if (!fullrepaint) {\r
3619     SelectClipRgn(hdcmem, clips[0]);\r
3620     for (x = 1; x < num_clips; x++) {\r
3621       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3622         abort();  // this should never ever happen!\r
3623     }\r
3624   }\r
3625 \r
3626   /* Do all the drawing to the memory DC */\r
3627   if(explodeInfo.radius) { // [HGM] atomic\r
3628         HBRUSH oldBrush;\r
3629         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3630         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3631         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3632         x += squareSize/2;\r
3633         y += squareSize/2;\r
3634         if(!fullrepaint) {\r
3635           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3636           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3637         }\r
3638         DrawGridOnDC(hdcmem);\r
3639         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3640         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3641         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3642         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3643         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3644         SelectObject(hdcmem, oldBrush);\r
3645   } else {\r
3646     DrawGridOnDC(hdcmem);\r
3647     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3648         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3649         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3650     } else {\r
3651         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3652         oldPartnerHighlight = partnerHighlightInfo;\r
3653     }\r
3654     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3655   }\r
3656   if(nr == 0) // [HGM] dual: markers only on left board\r
3657   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3658     for (column = 0; column < BOARD_WIDTH; column++) {\r
3659         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3660             HBRUSH oldBrush = SelectObject(hdcmem, \r
3661                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3662             SquareToPos(row, column, &x, &y);\r
3663             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3664                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3665             SelectObject(hdcmem, oldBrush);\r
3666         }\r
3667     }\r
3668   }\r
3669   if(logoHeight) {\r
3670         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3671         if(appData.autoLogo) {\r
3672           \r
3673           switch(gameMode) { // pick logos based on game mode\r
3674             case IcsObserving:\r
3675                 whiteLogo = second.programLogo; // ICS logo\r
3676                 blackLogo = second.programLogo;\r
3677             default:\r
3678                 break;\r
3679             case IcsPlayingWhite:\r
3680                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3681                 blackLogo = second.programLogo; // ICS logo\r
3682                 break;\r
3683             case IcsPlayingBlack:\r
3684                 whiteLogo = second.programLogo; // ICS logo\r
3685                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3686                 break;\r
3687             case TwoMachinesPlay:\r
3688                 if(first.twoMachinesColor[0] == 'b') {\r
3689                     whiteLogo = second.programLogo;\r
3690                     blackLogo = first.programLogo;\r
3691                 }\r
3692                 break;\r
3693             case MachinePlaysWhite:\r
3694                 blackLogo = userLogo;\r
3695                 break;\r
3696             case MachinePlaysBlack:\r
3697                 whiteLogo = userLogo;\r
3698                 blackLogo = first.programLogo;\r
3699           }\r
3700         }\r
3701         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3702         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3703   }\r
3704 \r
3705   if( appData.highlightMoveWithArrow ) {\r
3706     DrawArrowHighlight(hdcmem);\r
3707   }\r
3708 \r
3709   DrawCoordsOnDC(hdcmem);\r
3710 \r
3711   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3712                  /* to make sure lastDrawn contains what is actually drawn */\r
3713 \r
3714   /* Put the dragged piece back into place and draw it (out of place!) */\r
3715     if (dragged_piece != EmptySquare) {\r
3716     /* [HGM] or restack */\r
3717     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3718                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3719     else\r
3720     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3721                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3722     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3723     x = dragInfo.pos.x - squareSize / 2;\r
3724     y = dragInfo.pos.y - squareSize / 2;\r
3725     DrawPieceOnDC(hdcmem, dragged_piece,\r
3726                   ((int) dragged_piece < (int) BlackPawn), \r
3727                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3728   }   \r
3729   \r
3730   /* Put the animated piece back into place and draw it */\r
3731   if (animInfo.piece != EmptySquare) {\r
3732     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3733     x = boardRect.left + animInfo.pos.x;\r
3734     y = boardRect.top + animInfo.pos.y;\r
3735     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3736                   ((int) animInfo.piece < (int) BlackPawn),\r
3737                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3738   }\r
3739 \r
3740   /* Release the bufferBitmap by selecting in the old bitmap \r
3741    * and delete the memory DC\r
3742    */\r
3743   SelectObject(hdcmem, oldBitmap);\r
3744   DeleteDC(hdcmem);\r
3745 \r
3746   /* Set clipping on the target DC */\r
3747   if (!fullrepaint) {\r
3748     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3749         RECT rect;\r
3750         GetRgnBox(clips[x], &rect);\r
3751         DeleteObject(clips[x]);\r
3752         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3753                           rect.right + wpMain.width/2, rect.bottom);\r
3754     }\r
3755     SelectClipRgn(hdc, clips[0]);\r
3756     for (x = 1; x < num_clips; x++) {\r
3757       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3758         abort();   // this should never ever happen!\r
3759     } \r
3760   }\r
3761 \r
3762   /* Copy the new bitmap onto the screen in one go.\r
3763    * This way we avoid any flickering\r
3764    */\r
3765   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3766   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3767          boardRect.right - boardRect.left,\r
3768          boardRect.bottom - boardRect.top,\r
3769          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3770   if(saveDiagFlag) { \r
3771     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
3772     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3773 \r
3774     GetObject(bufferBitmap, sizeof(b), &b);\r
3775     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
3776         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3777         bih.biWidth = b.bmWidth;\r
3778         bih.biHeight = b.bmHeight;\r
3779         bih.biPlanes = 1;\r
3780         bih.biBitCount = b.bmBitsPixel;\r
3781         bih.biCompression = 0;\r
3782         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3783         bih.biXPelsPerMeter = 0;\r
3784         bih.biYPelsPerMeter = 0;\r
3785         bih.biClrUsed = 0;\r
3786         bih.biClrImportant = 0;\r
3787 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3788 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3789         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3790 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3791 \r
3792         wb = b.bmWidthBytes;\r
3793         // count colors\r
3794         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3795                 int k = ((int*) pData)[i];\r
3796                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3797                 if(j >= 16) break;\r
3798                 color[j] = k;\r
3799                 if(j >= nrColors) nrColors = j+1;\r
3800         }\r
3801         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3802                 INT p = 0;\r
3803                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3804                     for(w=0; w<(wb>>2); w+=2) {\r
3805                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3806                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3807                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3808                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3809                         pData[p++] = m | j<<4;\r
3810                     }\r
3811                     while(p&3) pData[p++] = 0;\r
3812                 }\r
3813                 fac = 3;\r
3814                 wb = ((wb+31)>>5)<<2;\r
3815         }\r
3816         // write BITMAPFILEHEADER\r
3817         fprintf(diagFile, "BM");\r
3818         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3819         fputDW(diagFile, 0);\r
3820         fputDW(diagFile, 0x36 + (fac?64:0));\r
3821         // write BITMAPINFOHEADER\r
3822         fputDW(diagFile, 40);\r
3823         fputDW(diagFile, b.bmWidth);\r
3824         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3825         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3826         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\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         fputDW(diagFile, 0);\r
3833         // write color table\r
3834         if(fac)\r
3835         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3836         // write bitmap data\r
3837         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3838                 fputc(pData[i], diagFile);\r
3839      }\r
3840   }\r
3841 \r
3842   SelectObject(tmphdc, oldBitmap);\r
3843 \r
3844   /* Massive cleanup */\r
3845   for (x = 0; x < num_clips; x++)\r
3846     DeleteObject(clips[x]);\r
3847 \r
3848   DeleteDC(tmphdc);\r
3849   DeleteObject(bufferBitmap);\r
3850 \r
3851   if (releaseDC) \r
3852     ReleaseDC(hwndMain, hdc);\r
3853   \r
3854   if (lastDrawnFlipView != flipView && nr == 0) {\r
3855     if (flipView)\r
3856       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3857     else\r
3858       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3859   }\r
3860 \r
3861 /*  CopyBoard(lastDrawn, board);*/\r
3862   lastDrawnHighlight = highlightInfo;\r
3863   lastDrawnPremove   = premoveHighlightInfo;\r
3864   lastDrawnFlipView = flipView;\r
3865   lastDrawnValid[nr] = 1;\r
3866 }\r
3867 \r
3868 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3869 int\r
3870 SaveDiagram(f)\r
3871      FILE *f;\r
3872 {\r
3873     saveDiagFlag = 1; diagFile = f;\r
3874     HDCDrawPosition(NULL, TRUE, NULL);\r
3875 \r
3876     saveDiagFlag = 0;\r
3877 \r
3878 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
3879     \r
3880     fclose(f);\r
3881     return TRUE;\r
3882 }\r
3883 \r
3884 \r
3885 /*---------------------------------------------------------------------------*\\r
3886 | CLIENT PAINT PROCEDURE\r
3887 |   This is the main event-handler for the WM_PAINT message.\r
3888 |\r
3889 \*---------------------------------------------------------------------------*/\r
3890 VOID\r
3891 PaintProc(HWND hwnd)\r
3892 {\r
3893   HDC         hdc;\r
3894   PAINTSTRUCT ps;\r
3895   HFONT       oldFont;\r
3896 \r
3897   if((hdc = BeginPaint(hwnd, &ps))) {\r
3898     if (IsIconic(hwnd)) {\r
3899       DrawIcon(hdc, 2, 2, iconCurrent);\r
3900     } else {\r
3901       if (!appData.monoMode) {\r
3902         SelectPalette(hdc, hPal, FALSE);\r
3903         RealizePalette(hdc);\r
3904       }\r
3905       HDCDrawPosition(hdc, 1, NULL);\r
3906       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3907         flipView = !flipView; partnerUp = !partnerUp;\r
3908         HDCDrawPosition(hdc, 1, NULL);\r
3909         flipView = !flipView; partnerUp = !partnerUp;\r
3910       }\r
3911       oldFont =\r
3912         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3913       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3914                  ETO_CLIPPED|ETO_OPAQUE,\r
3915                  &messageRect, messageText, strlen(messageText), NULL);\r
3916       SelectObject(hdc, oldFont);\r
3917       DisplayBothClocks();\r
3918     }\r
3919     EndPaint(hwnd,&ps);\r
3920   }\r
3921 \r
3922   return;\r
3923 }\r
3924 \r
3925 \r
3926 /*\r
3927  * If the user selects on a border boundary, return -1; if off the board,\r
3928  *   return -2.  Otherwise map the event coordinate to the square.\r
3929  * The offset boardRect.left or boardRect.top must already have been\r
3930  *   subtracted from x.\r
3931  */\r
3932 int EventToSquare(x, limit)\r
3933      int x, limit;\r
3934 {\r
3935   if (x <= 0)\r
3936     return -2;\r
3937   if (x < lineGap)\r
3938     return -1;\r
3939   x -= lineGap;\r
3940   if ((x % (squareSize + lineGap)) >= squareSize)\r
3941     return -1;\r
3942   x /= (squareSize + lineGap);\r
3943     if (x >= limit)\r
3944     return -2;\r
3945   return x;\r
3946 }\r
3947 \r
3948 typedef struct {\r
3949   char piece;\r
3950   int command;\r
3951   char* name;\r
3952 } DropEnable;\r
3953 \r
3954 DropEnable dropEnables[] = {\r
3955   { 'P', DP_Pawn, N_("Pawn") },\r
3956   { 'N', DP_Knight, N_("Knight") },\r
3957   { 'B', DP_Bishop, N_("Bishop") },\r
3958   { 'R', DP_Rook, N_("Rook") },\r
3959   { 'Q', DP_Queen, N_("Queen") },\r
3960 };\r
3961 \r
3962 VOID\r
3963 SetupDropMenu(HMENU hmenu)\r
3964 {\r
3965   int i, count, enable;\r
3966   char *p;\r
3967   extern char white_holding[], black_holding[];\r
3968   char item[MSG_SIZ];\r
3969 \r
3970   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
3971     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
3972                dropEnables[i].piece);\r
3973     count = 0;\r
3974     while (p && *p++ == dropEnables[i].piece) count++;\r
3975     sprintf(item, "%s  %d", T_(dropEnables[i].name), count);\r
3976     enable = count > 0 || !appData.testLegality\r
3977       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
3978                       && !appData.icsActive);\r
3979     ModifyMenu(hmenu, dropEnables[i].command,\r
3980                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
3981                dropEnables[i].command, item);\r
3982   }\r
3983 }\r
3984 \r
3985 void DragPieceBegin(int x, int y)\r
3986 {\r
3987       dragInfo.lastpos.x = boardRect.left + x;\r
3988       dragInfo.lastpos.y = boardRect.top + y;\r
3989       dragInfo.from.x = fromX;\r
3990       dragInfo.from.y = fromY;\r
3991       dragInfo.start = dragInfo.from;\r
3992       SetCapture(hwndMain);\r
3993 }\r
3994 \r
3995 void DragPieceEnd(int x, int y)\r
3996 {\r
3997     ReleaseCapture();\r
3998     dragInfo.start.x = dragInfo.start.y = -1;\r
3999     dragInfo.from = dragInfo.start;\r
4000     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4001 }\r
4002 \r
4003 /* Event handler for mouse messages */\r
4004 VOID\r
4005 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4006 {\r
4007   int x, y, menuNr;\r
4008   POINT pt;\r
4009   static int recursive = 0;\r
4010   HMENU hmenu;\r
4011   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4012 \r
4013   if (recursive) {\r
4014     if (message == WM_MBUTTONUP) {\r
4015       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4016          to the middle button: we simulate pressing the left button too!\r
4017          */\r
4018       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4019       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4020     }\r
4021     return;\r
4022   }\r
4023   recursive++;\r
4024   \r
4025   pt.x = LOWORD(lParam);\r
4026   pt.y = HIWORD(lParam);\r
4027   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4028   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4029   if (!flipView && y >= 0) {\r
4030     y = BOARD_HEIGHT - 1 - y;\r
4031   }\r
4032   if (flipView && x >= 0) {\r
4033     x = BOARD_WIDTH - 1 - x;\r
4034   }\r
4035 \r
4036   switch (message) {\r
4037   case WM_LBUTTONDOWN:\r
4038       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4039         if (gameMode == EditPosition) {\r
4040           SetWhiteToPlayEvent();\r
4041         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4042           AdjustClock(flipClock, -1);\r
4043         } else if (gameMode == IcsPlayingBlack ||\r
4044                    gameMode == MachinePlaysWhite) {\r
4045           CallFlagEvent();\r
4046         }\r
4047       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4048         if (gameMode == EditPosition) {\r
4049           SetBlackToPlayEvent();\r
4050         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4051           AdjustClock(!flipClock, -1);\r
4052         } else if (gameMode == IcsPlayingWhite ||\r
4053                    gameMode == MachinePlaysBlack) {\r
4054           CallFlagEvent();\r
4055         }\r
4056       }\r
4057       dragInfo.start.x = dragInfo.start.y = -1;\r
4058       dragInfo.from = dragInfo.start;\r
4059     if(fromX == -1 && frozen) { // not sure where this is for\r
4060                 fromX = fromY = -1; \r
4061       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4062       break;\r
4063     }\r
4064       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4065       DrawPosition(TRUE, NULL);\r
4066     break;\r
4067 \r
4068   case WM_LBUTTONUP:\r
4069       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4070       DrawPosition(TRUE, NULL);\r
4071     break;\r
4072 \r
4073   case WM_MOUSEMOVE:\r
4074     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4075     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4076     if ((appData.animateDragging || appData.highlightDragging)\r
4077         && (wParam & MK_LBUTTON)\r
4078         && dragInfo.from.x >= 0) \r
4079     {\r
4080       BOOL full_repaint = FALSE;\r
4081 \r
4082       if (appData.animateDragging) {\r
4083         dragInfo.pos = pt;\r
4084       }\r
4085       if (appData.highlightDragging) {\r
4086         SetHighlights(fromX, fromY, x, y);\r
4087         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4088             full_repaint = TRUE;\r
4089         }\r
4090       }\r
4091       \r
4092       DrawPosition( full_repaint, NULL);\r
4093       \r
4094       dragInfo.lastpos = dragInfo.pos;\r
4095     }\r
4096     break;\r
4097 \r
4098   case WM_MOUSEWHEEL: // [DM]\r
4099     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4100        /* Mouse Wheel is being rolled forward\r
4101         * Play moves forward\r
4102         */\r
4103        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4104                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4105        /* Mouse Wheel is being rolled backward\r
4106         * Play moves backward\r
4107         */\r
4108        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4109                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4110     }\r
4111     break;\r
4112 \r
4113   case WM_MBUTTONUP:\r
4114   case WM_RBUTTONUP:\r
4115     ReleaseCapture();\r
4116     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4117     break;\r
4118  \r
4119   case WM_MBUTTONDOWN:\r
4120   case WM_RBUTTONDOWN:\r
4121     ErrorPopDown();\r
4122     ReleaseCapture();\r
4123     fromX = fromY = -1;\r
4124     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4125     dragInfo.start.x = dragInfo.start.y = -1;\r
4126     dragInfo.from = dragInfo.start;\r
4127     dragInfo.lastpos = dragInfo.pos;\r
4128     if (appData.highlightDragging) {\r
4129       ClearHighlights();\r
4130     }\r
4131     if(y == -2) {\r
4132       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4133       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4134           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4135       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4136           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4137       }\r
4138       break;\r
4139     }\r
4140     DrawPosition(TRUE, NULL);\r
4141 \r
4142     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4143     switch (menuNr) {\r
4144     case 0:\r
4145       if (message == WM_MBUTTONDOWN) {\r
4146         buttonCount = 3;  /* even if system didn't think so */\r
4147         if (wParam & MK_SHIFT) \r
4148           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4149         else\r
4150           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4151       } else { /* message == WM_RBUTTONDOWN */\r
4152         /* Just have one menu, on the right button.  Windows users don't\r
4153            think to try the middle one, and sometimes other software steals\r
4154            it, or it doesn't really exist. */\r
4155         if(gameInfo.variant != VariantShogi)\r
4156             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4157         else\r
4158             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4159       }\r
4160       break;\r
4161     case 2:\r
4162       SetCapture(hwndMain);
4163       break;\r
4164     case 1:\r
4165       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4166       SetupDropMenu(hmenu);\r
4167       MenuPopup(hwnd, pt, hmenu, -1);\r
4168     default:\r
4169       break;\r
4170     }\r
4171     break;\r
4172   }\r
4173 \r
4174   recursive--;\r
4175 }\r
4176 \r
4177 /* Preprocess messages for buttons in main window */\r
4178 LRESULT CALLBACK\r
4179 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4180 {\r
4181   int id = GetWindowLong(hwnd, GWL_ID);\r
4182   int i, dir;\r
4183 \r
4184   for (i=0; i<N_BUTTONS; i++) {\r
4185     if (buttonDesc[i].id == id) break;\r
4186   }\r
4187   if (i == N_BUTTONS) return 0;\r
4188   switch (message) {\r
4189   case WM_KEYDOWN:\r
4190     switch (wParam) {\r
4191     case VK_LEFT:\r
4192     case VK_RIGHT:\r
4193       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4194       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4195       return TRUE;\r
4196     }\r
4197     break;\r
4198   case WM_CHAR:\r
4199     switch (wParam) {\r
4200     case '\r':\r
4201       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4202       return TRUE;\r
4203     default:\r
4204       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4205         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4206         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4207         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4208         SetFocus(h);\r
4209         SendMessage(h, WM_CHAR, wParam, lParam);\r
4210         return TRUE;\r
4211       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4212         PopUpMoveDialog((char)wParam);\r
4213       }\r
4214       break;\r
4215     }\r
4216     break;\r
4217   }\r
4218   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4219 }\r
4220 \r
4221 /* Process messages for Promotion dialog box */\r
4222 LRESULT CALLBACK\r
4223 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4224 {\r
4225   char promoChar;\r
4226 \r
4227   switch (message) {\r
4228   case WM_INITDIALOG: /* message: initialize dialog box */\r
4229     /* Center the dialog over the application window */\r
4230     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4231     Translate(hDlg, DLG_PromotionKing);\r
4232     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4233       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4234        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4235                SW_SHOW : SW_HIDE);\r
4236     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4237     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4238        ((PieceToChar(WhiteAngel) >= 'A' &&\r
4239          PieceToChar(WhiteAngel) != '~') ||\r
4240         (PieceToChar(BlackAngel) >= 'A' &&\r
4241          PieceToChar(BlackAngel) != '~')   ) ?\r
4242                SW_SHOW : SW_HIDE);\r
4243     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4244        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
4245          PieceToChar(WhiteMarshall) != '~') ||\r
4246         (PieceToChar(BlackMarshall) >= 'A' &&\r
4247          PieceToChar(BlackMarshall) != '~')   ) ?\r
4248                SW_SHOW : SW_HIDE);\r
4249     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4250     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4251        gameInfo.variant != VariantShogi ?\r
4252                SW_SHOW : SW_HIDE);\r
4253     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4254        gameInfo.variant != VariantShogi ?\r
4255                SW_SHOW : SW_HIDE);\r
4256     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
4257        gameInfo.variant == VariantShogi ?\r
4258                SW_SHOW : SW_HIDE);\r
4259     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
4260        gameInfo.variant == VariantShogi ?\r
4261                SW_SHOW : SW_HIDE);\r
4262     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4263        gameInfo.variant == VariantSuper ?\r
4264                SW_SHOW : SW_HIDE);\r
4265     return TRUE;\r
4266 \r
4267   case WM_COMMAND: /* message: received a command */\r
4268     switch (LOWORD(wParam)) {\r
4269     case IDCANCEL:\r
4270       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4271       ClearHighlights();\r
4272       DrawPosition(FALSE, NULL);\r
4273       return TRUE;\r
4274     case PB_King:\r
4275       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4276       break;\r
4277     case PB_Queen:\r
4278       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
4279       break;\r
4280     case PB_Rook:\r
4281       promoChar = PieceToChar(BlackRook);\r
4282       break;\r
4283     case PB_Bishop:\r
4284       promoChar = PieceToChar(BlackBishop);\r
4285       break;\r
4286     case PB_Chancellor:\r
4287       promoChar = PieceToChar(BlackMarshall);\r
4288       break;\r
4289     case PB_Archbishop:\r
4290       promoChar = PieceToChar(BlackAngel);\r
4291       break;\r
4292     case PB_Knight:\r
4293       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
4294       break;\r
4295     default:\r
4296       return FALSE;\r
4297     }\r
4298     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4299     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
4300        only show the popup when we are already sure the move is valid or\r
4301        legal. We pass a faulty move type, but the kludge is that FinishMove\r
4302        will figure out it is a promotion from the promoChar. */\r
4303     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4304     fromX = fromY = -1;\r
4305     if (!appData.highlightLastMove) {\r
4306       ClearHighlights();\r
4307       DrawPosition(FALSE, NULL);\r
4308     }\r
4309     return TRUE;\r
4310   }\r
4311   return FALSE;\r
4312 }\r
4313 \r
4314 /* Pop up promotion dialog */\r
4315 VOID\r
4316 PromotionPopup(HWND hwnd)\r
4317 {\r
4318   FARPROC lpProc;\r
4319 \r
4320   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4321   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4322     hwnd, (DLGPROC)lpProc);\r
4323   FreeProcInstance(lpProc);\r
4324 }\r
4325 \r
4326 void\r
4327 PromotionPopUp()\r
4328 {\r
4329   DrawPosition(TRUE, NULL);\r
4330   PromotionPopup(hwndMain);\r
4331 }\r
4332 \r
4333 /* Toggle ShowThinking */\r
4334 VOID\r
4335 ToggleShowThinking()\r
4336 {\r
4337   appData.showThinking = !appData.showThinking;\r
4338   ShowThinkingEvent();\r
4339 }\r
4340 \r
4341 VOID\r
4342 LoadGameDialog(HWND hwnd, char* title)\r
4343 {\r
4344   UINT number = 0;\r
4345   FILE *f;\r
4346   char fileTitle[MSG_SIZ];\r
4347   f = OpenFileDialog(hwnd, "rb", "",\r
4348                      appData.oldSaveStyle ? "gam" : "pgn",\r
4349                      GAME_FILT,\r
4350                      title, &number, fileTitle, NULL);\r
4351   if (f != NULL) {\r
4352     cmailMsgLoaded = FALSE;\r
4353     if (number == 0) {\r
4354       int error = GameListBuild(f);\r
4355       if (error) {\r
4356         DisplayError(_("Cannot build game list"), error);\r
4357       } else if (!ListEmpty(&gameList) &&\r
4358                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4359         GameListPopUp(f, fileTitle);\r
4360         return;\r
4361       }\r
4362       GameListDestroy();\r
4363       number = 1;\r
4364     }\r
4365     LoadGame(f, number, fileTitle, FALSE);\r
4366   }\r
4367 }\r
4368 \r
4369 int get_term_width()\r
4370 {\r
4371     HDC hdc;\r
4372     TEXTMETRIC tm;\r
4373     RECT rc;\r
4374     HFONT hfont, hold_font;\r
4375     LOGFONT lf;\r
4376     HWND hText;\r
4377 \r
4378     if (hwndConsole)\r
4379         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4380     else\r
4381         return 79;\r
4382 \r
4383     // get the text metrics\r
4384     hdc = GetDC(hText);\r
4385     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4386     if (consoleCF.dwEffects & CFE_BOLD)\r
4387         lf.lfWeight = FW_BOLD;\r
4388     if (consoleCF.dwEffects & CFE_ITALIC)\r
4389         lf.lfItalic = TRUE;\r
4390     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4391         lf.lfStrikeOut = TRUE;\r
4392     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4393         lf.lfUnderline = TRUE;\r
4394     hfont = CreateFontIndirect(&lf);\r
4395     hold_font = SelectObject(hdc, hfont);\r
4396     GetTextMetrics(hdc, &tm);\r
4397     SelectObject(hdc, hold_font);\r
4398     DeleteObject(hfont);\r
4399     ReleaseDC(hText, hdc);\r
4400 \r
4401     // get the rectangle\r
4402     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4403 \r
4404     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4405 }\r
4406 \r
4407 void UpdateICSWidth(HWND hText)\r
4408 {\r
4409     LONG old_width, new_width;\r
4410 \r
4411     new_width = get_term_width(hText, FALSE);\r
4412     old_width = GetWindowLong(hText, GWL_USERDATA);\r
4413     if (new_width != old_width)\r
4414     {\r
4415         ics_update_width(new_width);\r
4416         SetWindowLong(hText, GWL_USERDATA, new_width);\r
4417     }\r
4418 }\r
4419 \r
4420 VOID\r
4421 ChangedConsoleFont()\r
4422 {\r
4423   CHARFORMAT cfmt;\r
4424   CHARRANGE tmpsel, sel;\r
4425   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4426   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4427   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4428   PARAFORMAT paraf;\r
4429 \r
4430   cfmt.cbSize = sizeof(CHARFORMAT);\r
4431   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4432   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
4433   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4434    * size.  This was undocumented in the version of MSVC++ that I had\r
4435    * when I wrote the code, but is apparently documented now.\r
4436    */\r
4437   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4438   cfmt.bCharSet = f->lf.lfCharSet;\r
4439   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4440   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4441   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4442   /* Why are the following seemingly needed too? */\r
4443   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4444   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4445   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4446   tmpsel.cpMin = 0;\r
4447   tmpsel.cpMax = -1; /*999999?*/\r
4448   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4449   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4450   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4451    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4452    */\r
4453   paraf.cbSize = sizeof(paraf);\r
4454   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4455   paraf.dxStartIndent = 0;\r
4456   paraf.dxOffset = WRAP_INDENT;\r
4457   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4458   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4459   UpdateICSWidth(hText);\r
4460 }\r
4461 \r
4462 /*---------------------------------------------------------------------------*\\r
4463  *\r
4464  * Window Proc for main window\r
4465  *\r
4466 \*---------------------------------------------------------------------------*/\r
4467 \r
4468 /* Process messages for main window, etc. */\r
4469 LRESULT CALLBACK\r
4470 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4471 {\r
4472   FARPROC lpProc;\r
4473   int wmId, wmEvent;\r
4474   char *defName;\r
4475   FILE *f;\r
4476   UINT number;\r
4477   char fileTitle[MSG_SIZ];\r
4478   char buf[MSG_SIZ];\r
4479   static SnapData sd;\r
4480 \r
4481   switch (message) {\r
4482 \r
4483   case WM_PAINT: /* message: repaint portion of window */\r
4484     PaintProc(hwnd);\r
4485     break;\r
4486 \r
4487   case WM_ERASEBKGND:\r
4488     if (IsIconic(hwnd)) {\r
4489       /* Cheat; change the message */\r
4490       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4491     } else {\r
4492       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4493     }\r
4494     break;\r
4495 \r
4496   case WM_LBUTTONDOWN:\r
4497   case WM_MBUTTONDOWN:\r
4498   case WM_RBUTTONDOWN:\r
4499   case WM_LBUTTONUP:\r
4500   case WM_MBUTTONUP:\r
4501   case WM_RBUTTONUP:\r
4502   case WM_MOUSEMOVE:\r
4503   case WM_MOUSEWHEEL:\r
4504     MouseEvent(hwnd, message, wParam, lParam);\r
4505     break;\r
4506 \r
4507   JAWS_KB_NAVIGATION\r
4508 \r
4509   case WM_CHAR:\r
4510     \r
4511     JAWS_ALT_INTERCEPT\r
4512 \r
4513     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4514         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4515         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4516         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4517         SetFocus(h);\r
4518         SendMessage(h, message, wParam, lParam);\r
4519     } else if(lParam != KF_REPEAT) {\r
4520         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4521                 PopUpMoveDialog((char)wParam);\r
4522         } else if((char)wParam == 003) CopyGameToClipboard();\r
4523          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4524     }\r
4525 \r
4526     break;\r
4527 \r
4528   case WM_PALETTECHANGED:\r
4529     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4530       int nnew;\r
4531       HDC hdc = GetDC(hwndMain);\r
4532       SelectPalette(hdc, hPal, TRUE);\r
4533       nnew = RealizePalette(hdc);\r
4534       if (nnew > 0) {\r
4535         paletteChanged = TRUE;\r
4536         InvalidateRect(hwnd, &boardRect, FALSE);\r
4537       }\r
4538       ReleaseDC(hwnd, hdc);\r
4539     }\r
4540     break;\r
4541 \r
4542   case WM_QUERYNEWPALETTE:\r
4543     if (!appData.monoMode /*&& paletteChanged*/) {\r
4544       int nnew;\r
4545       HDC hdc = GetDC(hwndMain);\r
4546       paletteChanged = FALSE;\r
4547       SelectPalette(hdc, hPal, FALSE);\r
4548       nnew = RealizePalette(hdc);\r
4549       if (nnew > 0) {\r
4550         InvalidateRect(hwnd, &boardRect, FALSE);\r
4551       }\r
4552       ReleaseDC(hwnd, hdc);\r
4553       return TRUE;\r
4554     }\r
4555     return FALSE;\r
4556 \r
4557   case WM_COMMAND: /* message: command from application menu */\r
4558     wmId    = LOWORD(wParam);\r
4559     wmEvent = HIWORD(wParam);\r
4560 \r
4561     switch (wmId) {\r
4562     case IDM_NewGame:\r
4563       ResetGameEvent();\r
4564       SAY("new game enter a move to play against the computer with white");\r
4565       break;\r
4566 \r
4567     case IDM_NewGameFRC:\r
4568       if( NewGameFRC() == 0 ) {\r
4569         ResetGameEvent();\r
4570       }\r
4571       break;\r
4572 \r
4573     case IDM_NewVariant:\r
4574       NewVariantPopup(hwnd);\r
4575       break;\r
4576 \r
4577     case IDM_LoadGame:\r
4578       LoadGameDialog(hwnd, _("Load Game from File"));\r
4579       break;\r
4580 \r
4581     case IDM_LoadNextGame:\r
4582       ReloadGame(1);\r
4583       break;\r
4584 \r
4585     case IDM_LoadPrevGame:\r
4586       ReloadGame(-1);\r
4587       break;\r
4588 \r
4589     case IDM_ReloadGame:\r
4590       ReloadGame(0);\r
4591       break;\r
4592 \r
4593     case IDM_LoadPosition:\r
4594       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4595         Reset(FALSE, TRUE);\r
4596       }\r
4597       number = 1;\r
4598       f = OpenFileDialog(hwnd, "rb", "",\r
4599                          appData.oldSaveStyle ? "pos" : "fen",\r
4600                          POSITION_FILT,\r
4601                          _("Load Position from File"), &number, fileTitle, NULL);\r
4602       if (f != NULL) {\r
4603         LoadPosition(f, number, fileTitle);\r
4604       }\r
4605       break;\r
4606 \r
4607     case IDM_LoadNextPosition:\r
4608       ReloadPosition(1);\r
4609       break;\r
4610 \r
4611     case IDM_LoadPrevPosition:\r
4612       ReloadPosition(-1);\r
4613       break;\r
4614 \r
4615     case IDM_ReloadPosition:\r
4616       ReloadPosition(0);\r
4617       break;\r
4618 \r
4619     case IDM_SaveGame:\r
4620       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4621       f = OpenFileDialog(hwnd, "a", defName,\r
4622                          appData.oldSaveStyle ? "gam" : "pgn",\r
4623                          GAME_FILT,\r
4624                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4625       if (f != NULL) {\r
4626         SaveGame(f, 0, "");\r
4627       }\r
4628       break;\r
4629 \r
4630     case IDM_SavePosition:\r
4631       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4632       f = OpenFileDialog(hwnd, "a", defName,\r
4633                          appData.oldSaveStyle ? "pos" : "fen",\r
4634                          POSITION_FILT,\r
4635                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4636       if (f != NULL) {\r
4637         SavePosition(f, 0, "");\r
4638       }\r
4639       break;\r
4640 \r
4641     case IDM_SaveDiagram:\r
4642       defName = "diagram";\r
4643       f = OpenFileDialog(hwnd, "wb", defName,\r
4644                          "bmp",\r
4645                          DIAGRAM_FILT,\r
4646                          "Save Diagram to File", NULL, fileTitle, NULL);\r
4647       if (f != NULL) {\r
4648         SaveDiagram(f);\r
4649       }\r
4650       break;\r
4651 \r
4652     case IDM_CopyGame:\r
4653       CopyGameToClipboard();\r
4654       break;\r
4655 \r
4656     case IDM_PasteGame:\r
4657       PasteGameFromClipboard();\r
4658       break;\r
4659 \r
4660     case IDM_CopyGameListToClipboard:\r
4661       CopyGameListToClipboard();\r
4662       break;\r
4663 \r
4664     /* [AS] Autodetect FEN or PGN data */\r
4665     case IDM_PasteAny:\r
4666       PasteGameOrFENFromClipboard();\r
4667       break;\r
4668 \r
4669     /* [AS] Move history */\r
4670     case IDM_ShowMoveHistory:\r
4671         if( MoveHistoryIsUp() ) {\r
4672             MoveHistoryPopDown();\r
4673         }\r
4674         else {\r
4675             MoveHistoryPopUp();\r
4676         }\r
4677         break;\r
4678 \r
4679     /* [AS] Eval graph */\r
4680     case IDM_ShowEvalGraph:\r
4681         if( EvalGraphIsUp() ) {\r
4682             EvalGraphPopDown();\r
4683         }\r
4684         else {\r
4685             EvalGraphPopUp();\r
4686             SetFocus(hwndMain);\r
4687         }\r
4688         break;\r
4689 \r
4690     /* [AS] Engine output */\r
4691     case IDM_ShowEngineOutput:\r
4692         if( EngineOutputIsUp() ) {\r
4693             EngineOutputPopDown();\r
4694         }\r
4695         else {\r
4696             EngineOutputPopUp();\r
4697         }\r
4698         break;\r
4699 \r
4700     /* [AS] User adjudication */\r
4701     case IDM_UserAdjudication_White:\r
4702         UserAdjudicationEvent( +1 );\r
4703         break;\r
4704 \r
4705     case IDM_UserAdjudication_Black:\r
4706         UserAdjudicationEvent( -1 );\r
4707         break;\r
4708 \r
4709     case IDM_UserAdjudication_Draw:\r
4710         UserAdjudicationEvent( 0 );\r
4711         break;\r
4712 \r
4713     /* [AS] Game list options dialog */\r
4714     case IDM_GameListOptions:\r
4715       GameListOptions();\r
4716       break;\r
4717 \r
4718     case IDM_NewChat:\r
4719       ChatPopUp(NULL);\r
4720       break;\r
4721 \r
4722     case IDM_CopyPosition:\r
4723       CopyFENToClipboard();\r
4724       break;\r
4725 \r
4726     case IDM_PastePosition:\r
4727       PasteFENFromClipboard();\r
4728       break;\r
4729 \r
4730     case IDM_MailMove:\r
4731       MailMoveEvent();\r
4732       break;\r
4733 \r
4734     case IDM_ReloadCMailMsg:\r
4735       Reset(TRUE, TRUE);\r
4736       ReloadCmailMsgEvent(FALSE);\r
4737       break;\r
4738 \r
4739     case IDM_Minimize:\r
4740       ShowWindow(hwnd, SW_MINIMIZE);\r
4741       break;\r
4742 \r
4743     case IDM_Exit:\r
4744       ExitEvent(0);\r
4745       break;\r
4746 \r
4747     case IDM_MachineWhite:\r
4748       MachineWhiteEvent();\r
4749       /*\r
4750        * refresh the tags dialog only if it's visible\r
4751        */\r
4752       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4753           char *tags;\r
4754           tags = PGNTags(&gameInfo);\r
4755           TagsPopUp(tags, CmailMsg());\r
4756           free(tags);\r
4757       }\r
4758       SAY("computer starts playing white");\r
4759       break;\r
4760 \r
4761     case IDM_MachineBlack:\r
4762       MachineBlackEvent();\r
4763       /*\r
4764        * refresh the tags dialog only if it's visible\r
4765        */\r
4766       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4767           char *tags;\r
4768           tags = PGNTags(&gameInfo);\r
4769           TagsPopUp(tags, CmailMsg());\r
4770           free(tags);\r
4771       }\r
4772       SAY("computer starts playing black");\r
4773       break;\r
4774 \r
4775     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4776       if(gameMode != BeginningOfGame) break; // allow menu item to remain enabled for better mode highligting\r
4777       matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)\r
4778       appData.matchGames = appData.defaultMatchGames;\r
4779       matchGame = 1;\r
4780 \r
4781     case IDM_TwoMachines:\r
4782       TwoMachinesEvent();\r
4783       /*\r
4784        * refresh the tags dialog only if it's visible\r
4785        */\r
4786       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4787           char *tags;\r
4788           tags = PGNTags(&gameInfo);\r
4789           TagsPopUp(tags, CmailMsg());\r
4790           free(tags);\r
4791       }\r
4792       SAY("computer starts playing both sides");\r
4793       break;\r
4794 \r
4795     case IDM_AnalysisMode:\r
4796       if (!first.analysisSupport) {\r
4797         sprintf(buf, _("%s does not support analysis"), first.tidy);\r
4798         DisplayError(buf, 0);\r
4799       } else {\r
4800         SAY("analyzing current position");\r
4801         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4802         if (appData.icsActive) {\r
4803                if (gameMode != IcsObserving) {\r
4804                        sprintf(buf, "You are not observing a game");\r
4805                        DisplayError(buf, 0);\r
4806                        /* secure check */\r
4807                        if (appData.icsEngineAnalyze) {\r
4808                                if (appData.debugMode) \r
4809                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4810                                ExitAnalyzeMode();\r
4811                                ModeHighlight();\r
4812                                break;\r
4813                        }\r
4814                        break;\r
4815                } else {\r
4816                        /* if enable, user want disable icsEngineAnalyze */\r
4817                        if (appData.icsEngineAnalyze) {\r
4818                                ExitAnalyzeMode();\r
4819                                ModeHighlight();\r
4820                                break;\r
4821                        }\r
4822                        appData.icsEngineAnalyze = TRUE;\r
4823                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4824                }\r
4825         } \r
4826         if (!appData.showThinking) ToggleShowThinking();\r
4827         AnalyzeModeEvent();\r
4828       }\r
4829       break;\r
4830 \r
4831     case IDM_AnalyzeFile:\r
4832       if (!first.analysisSupport) {\r
4833         char buf[MSG_SIZ];\r
4834         sprintf(buf, _("%s does not support analysis"), first.tidy);\r
4835         DisplayError(buf, 0);\r
4836       } else {\r
4837         if (!appData.showThinking) ToggleShowThinking();\r
4838         AnalyzeFileEvent();\r
4839         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4840         AnalysisPeriodicEvent(1);\r
4841       }\r
4842       break;\r
4843 \r
4844     case IDM_IcsClient:\r
4845       IcsClientEvent();\r
4846       break;\r
4847 \r
4848     case IDM_EditGame:\r
4849       EditGameEvent();\r
4850       SAY("edit game");\r
4851       break;\r
4852 \r
4853     case IDM_EditPosition:\r
4854       EditPositionEvent();\r
4855       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4856       break;\r
4857 \r
4858     case IDM_Training:\r
4859       TrainingEvent();\r
4860       break;\r
4861 \r
4862     case IDM_ShowGameList:\r
4863       ShowGameListProc();\r
4864       break;\r
4865 \r
4866     case IDM_EditTags:\r
4867       EditTagsProc();\r
4868       break;\r
4869 \r
4870     case IDM_EditComment:\r
4871       if (commentUp && editComment) {\r
4872         CommentPopDown();\r
4873       } else {\r
4874         EditCommentEvent();\r
4875       }\r
4876       break;\r
4877 \r
4878     case IDM_Pause:\r
4879       PauseEvent();\r
4880       break;\r
4881 \r
4882     case IDM_Accept:\r
4883       AcceptEvent();\r
4884       break;\r
4885 \r
4886     case IDM_Decline:\r
4887       DeclineEvent();\r
4888       break;\r
4889 \r
4890     case IDM_Rematch:\r
4891       RematchEvent();\r
4892       break;\r
4893 \r
4894     case IDM_CallFlag:\r
4895       CallFlagEvent();\r
4896       break;\r
4897 \r
4898     case IDM_Draw:\r
4899       DrawEvent();\r
4900       break;\r
4901 \r
4902     case IDM_Adjourn:\r
4903       AdjournEvent();\r
4904       break;\r
4905 \r
4906     case IDM_Abort:\r
4907       AbortEvent();\r
4908       break;\r
4909 \r
4910     case IDM_Resign:\r
4911       ResignEvent();\r
4912       break;\r
4913 \r
4914     case IDM_StopObserving:\r
4915       StopObservingEvent();\r
4916       break;\r
4917 \r
4918     case IDM_StopExamining:\r
4919       StopExaminingEvent();\r
4920       break;\r
4921 \r
4922     case IDM_Upload:\r
4923       UploadGameEvent();\r
4924       break;\r
4925 \r
4926     case IDM_TypeInMove:\r
4927       PopUpMoveDialog('\000');\r
4928       break;\r
4929 \r
4930     case IDM_TypeInName:\r
4931       PopUpNameDialog('\000');\r
4932       break;\r
4933 \r
4934     case IDM_Backward:\r
4935       BackwardEvent();\r
4936       SetFocus(hwndMain);\r
4937       break;\r
4938 \r
4939     JAWS_MENU_ITEMS\r
4940 \r
4941     case IDM_Forward:\r
4942       ForwardEvent();\r
4943       SetFocus(hwndMain);\r
4944       break;\r
4945 \r
4946     case IDM_ToStart:\r
4947       ToStartEvent();\r
4948       SetFocus(hwndMain);\r
4949       break;\r
4950 \r
4951     case IDM_ToEnd:\r
4952       ToEndEvent();\r
4953       SetFocus(hwndMain);\r
4954       break;\r
4955 \r
4956     case IDM_Revert:\r
4957       RevertEvent(FALSE);\r
4958       break;\r
4959 \r
4960     case IDM_Annotate: // [HGM] vari: revert with annotation\r
4961       RevertEvent(TRUE);\r
4962       break;\r
4963 \r
4964     case IDM_TruncateGame:\r
4965       TruncateGameEvent();\r
4966       break;\r
4967 \r
4968     case IDM_MoveNow:\r
4969       MoveNowEvent();\r
4970       break;\r
4971 \r
4972     case IDM_RetractMove:\r
4973       RetractMoveEvent();\r
4974       break;\r
4975 \r
4976     case IDM_FlipView:\r
4977       flipView = !flipView;\r
4978       DrawPosition(FALSE, NULL);\r
4979       break;\r
4980 \r
4981     case IDM_FlipClock:\r
4982       flipClock = !flipClock;\r
4983       DisplayBothClocks();\r
4984       DrawPosition(FALSE, NULL);\r
4985       break;\r
4986 \r
4987     case IDM_MuteSounds:\r
4988       mute = !mute; // [HGM] mute: keep track of global muting variable\r
4989       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
4990                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
4991       break;\r
4992 \r
4993     case IDM_GeneralOptions:\r
4994       GeneralOptionsPopup(hwnd);\r
4995       DrawPosition(TRUE, NULL);\r
4996       break;\r
4997 \r
4998     case IDM_BoardOptions:\r
4999       BoardOptionsPopup(hwnd);\r
5000       break;\r
5001 \r
5002     case IDM_EnginePlayOptions:\r
5003       EnginePlayOptionsPopup(hwnd);\r
5004       break;\r
5005 \r
5006     case IDM_Engine1Options:\r
5007       EngineOptionsPopup(hwnd, &first);\r
5008       break;\r
5009 \r
5010     case IDM_Engine2Options:\r
5011       EngineOptionsPopup(hwnd, &second);\r
5012       break;\r
5013 \r
5014     case IDM_OptionsUCI:\r
5015       UciOptionsPopup(hwnd);\r
5016       break;\r
5017 \r
5018     case IDM_IcsOptions:\r
5019       IcsOptionsPopup(hwnd);\r
5020       break;\r
5021 \r
5022     case IDM_Fonts:\r
5023       FontsOptionsPopup(hwnd);\r
5024       break;\r
5025 \r
5026     case IDM_Sounds:\r
5027       SoundOptionsPopup(hwnd);\r
5028       break;\r
5029 \r
5030     case IDM_CommPort:\r
5031       CommPortOptionsPopup(hwnd);\r
5032       break;\r
5033 \r
5034     case IDM_LoadOptions:\r
5035       LoadOptionsPopup(hwnd);\r
5036       break;\r
5037 \r
5038     case IDM_SaveOptions:\r
5039       SaveOptionsPopup(hwnd);\r
5040       break;\r
5041 \r
5042     case IDM_TimeControl:\r
5043       TimeControlOptionsPopup(hwnd);\r
5044       break;\r
5045 \r
5046     case IDM_SaveSettings:\r
5047       SaveSettings(settingsFileName);\r
5048       break;\r
5049 \r
5050     case IDM_SaveSettingsOnExit:\r
5051       saveSettingsOnExit = !saveSettingsOnExit;\r
5052       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5053                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5054                                          MF_CHECKED : MF_UNCHECKED));\r
5055       break;\r
5056 \r
5057     case IDM_Hint:\r
5058       HintEvent();\r
5059       break;\r
5060 \r
5061     case IDM_Book:\r
5062       BookEvent();\r
5063       break;\r
5064 \r
5065     case IDM_AboutGame:\r
5066       AboutGameEvent();\r
5067       break;\r
5068 \r
5069     case IDM_Debug:\r
5070       appData.debugMode = !appData.debugMode;\r
5071       if (appData.debugMode) {\r
5072         char dir[MSG_SIZ];\r
5073         GetCurrentDirectory(MSG_SIZ, dir);\r
5074         SetCurrentDirectory(installDir);\r
5075         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5076         SetCurrentDirectory(dir);\r
5077         setbuf(debugFP, NULL);\r
5078       } else {\r
5079         fclose(debugFP);\r
5080         debugFP = NULL;\r
5081       }\r
5082       break;\r
5083 \r
5084     case IDM_HELPCONTENTS:\r
5085       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5086           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5087           MessageBox (GetFocus(),\r
5088                     _("Unable to activate help"),\r
5089                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5090       }\r
5091       break;\r
5092 \r
5093     case IDM_HELPSEARCH:\r
5094         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5095             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5096         MessageBox (GetFocus(),\r
5097                     _("Unable to activate help"),\r
5098                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5099       }\r
5100       break;\r
5101 \r
5102     case IDM_HELPHELP:\r
5103       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5104         MessageBox (GetFocus(),\r
5105                     _("Unable to activate help"),\r
5106                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5107       }\r
5108       break;\r
5109 \r
5110     case IDM_ABOUT:\r
5111       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5112       DialogBox(hInst, \r
5113         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5114         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5115       FreeProcInstance(lpProc);\r
5116       break;\r
5117 \r
5118     case IDM_DirectCommand1:\r
5119       AskQuestionEvent(_("Direct Command"),\r
5120                        _("Send to chess program:"), "", "1");\r
5121       break;\r
5122     case IDM_DirectCommand2:\r
5123       AskQuestionEvent(_("Direct Command"),\r
5124                        _("Send to second chess program:"), "", "2");\r
5125       break;\r
5126 \r
5127     case EP_WhitePawn:\r
5128       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5129       fromX = fromY = -1;\r
5130       break;\r
5131 \r
5132     case EP_WhiteKnight:\r
5133       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5134       fromX = fromY = -1;\r
5135       break;\r
5136 \r
5137     case EP_WhiteBishop:\r
5138       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5139       fromX = fromY = -1;\r
5140       break;\r
5141 \r
5142     case EP_WhiteRook:\r
5143       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5144       fromX = fromY = -1;\r
5145       break;\r
5146 \r
5147     case EP_WhiteQueen:\r
5148       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5149       fromX = fromY = -1;\r
5150       break;\r
5151 \r
5152     case EP_WhiteFerz:\r
5153       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5154       fromX = fromY = -1;\r
5155       break;\r
5156 \r
5157     case EP_WhiteWazir:\r
5158       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5159       fromX = fromY = -1;\r
5160       break;\r
5161 \r
5162     case EP_WhiteAlfil:\r
5163       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5164       fromX = fromY = -1;\r
5165       break;\r
5166 \r
5167     case EP_WhiteCannon:\r
5168       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5169       fromX = fromY = -1;\r
5170       break;\r
5171 \r
5172     case EP_WhiteCardinal:\r
5173       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5174       fromX = fromY = -1;\r
5175       break;\r
5176 \r
5177     case EP_WhiteMarshall:\r
5178       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5179       fromX = fromY = -1;\r
5180       break;\r
5181 \r
5182     case EP_WhiteKing:\r
5183       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5184       fromX = fromY = -1;\r
5185       break;\r
5186 \r
5187     case EP_BlackPawn:\r
5188       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5189       fromX = fromY = -1;\r
5190       break;\r
5191 \r
5192     case EP_BlackKnight:\r
5193       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5194       fromX = fromY = -1;\r
5195       break;\r
5196 \r
5197     case EP_BlackBishop:\r
5198       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5199       fromX = fromY = -1;\r
5200       break;\r
5201 \r
5202     case EP_BlackRook:\r
5203       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5204       fromX = fromY = -1;\r
5205       break;\r
5206 \r
5207     case EP_BlackQueen:\r
5208       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5209       fromX = fromY = -1;\r
5210       break;\r
5211 \r
5212     case EP_BlackFerz:\r
5213       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5214       fromX = fromY = -1;\r
5215       break;\r
5216 \r
5217     case EP_BlackWazir:\r
5218       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5219       fromX = fromY = -1;\r
5220       break;\r
5221 \r
5222     case EP_BlackAlfil:\r
5223       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5224       fromX = fromY = -1;\r
5225       break;\r
5226 \r
5227     case EP_BlackCannon:\r
5228       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5229       fromX = fromY = -1;\r
5230       break;\r
5231 \r
5232     case EP_BlackCardinal:\r
5233       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5234       fromX = fromY = -1;\r
5235       break;\r
5236 \r
5237     case EP_BlackMarshall:\r
5238       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5239       fromX = fromY = -1;\r
5240       break;\r
5241 \r
5242     case EP_BlackKing:\r
5243       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5244       fromX = fromY = -1;\r
5245       break;\r
5246 \r
5247     case EP_EmptySquare:\r
5248       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5249       fromX = fromY = -1;\r
5250       break;\r
5251 \r
5252     case EP_ClearBoard:\r
5253       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5254       fromX = fromY = -1;\r
5255       break;\r
5256 \r
5257     case EP_White:\r
5258       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5259       fromX = fromY = -1;\r
5260       break;\r
5261 \r
5262     case EP_Black:\r
5263       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5264       fromX = fromY = -1;\r
5265       break;\r
5266 \r
5267     case EP_Promote:\r
5268       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5269       fromX = fromY = -1;\r
5270       break;\r
5271 \r
5272     case EP_Demote:\r
5273       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5274       fromX = fromY = -1;\r
5275       break;\r
5276 \r
5277     case DP_Pawn:\r
5278       DropMenuEvent(WhitePawn, fromX, fromY);\r
5279       fromX = fromY = -1;\r
5280       break;\r
5281 \r
5282     case DP_Knight:\r
5283       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5284       fromX = fromY = -1;\r
5285       break;\r
5286 \r
5287     case DP_Bishop:\r
5288       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5289       fromX = fromY = -1;\r
5290       break;\r
5291 \r
5292     case DP_Rook:\r
5293       DropMenuEvent(WhiteRook, fromX, fromY);\r
5294       fromX = fromY = -1;\r
5295       break;\r
5296 \r
5297     case DP_Queen:\r
5298       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5299       fromX = fromY = -1;\r
5300       break;\r
5301 \r
5302     default:\r
5303       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5304     }\r
5305     break;\r
5306 \r
5307   case WM_TIMER:\r
5308     switch (wParam) {\r
5309     case CLOCK_TIMER_ID:\r
5310       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5311       clockTimerEvent = 0;\r
5312       DecrementClocks(); /* call into back end */\r
5313       break;\r
5314     case LOAD_GAME_TIMER_ID:\r
5315       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5316       loadGameTimerEvent = 0;\r
5317       AutoPlayGameLoop(); /* call into back end */\r
5318       break;\r
5319     case ANALYSIS_TIMER_ID:\r
5320       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5321                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5322         AnalysisPeriodicEvent(0);\r
5323       } else {\r
5324         KillTimer(hwnd, analysisTimerEvent);\r
5325         analysisTimerEvent = 0;\r
5326       }\r
5327       break;\r
5328     case DELAYED_TIMER_ID:\r
5329       KillTimer(hwnd, delayedTimerEvent);\r
5330       delayedTimerEvent = 0;\r
5331       delayedTimerCallback();\r
5332       break;\r
5333     }\r
5334     break;\r
5335 \r
5336   case WM_USER_Input:\r
5337     InputEvent(hwnd, message, wParam, lParam);\r
5338     break;\r
5339 \r
5340   /* [AS] Also move "attached" child windows */\r
5341   case WM_WINDOWPOSCHANGING:\r
5342 \r
5343     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5344         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5345 \r
5346         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5347             /* Window is moving */\r
5348             RECT rcMain;\r
5349 \r
5350 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5351             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5352             rcMain.right  = wpMain.x + wpMain.width;\r
5353             rcMain.top    = wpMain.y;\r
5354             rcMain.bottom = wpMain.y + wpMain.height;\r
5355             \r
5356             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5357             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5358             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5359             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5360             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5361             wpMain.x = lpwp->x;\r
5362             wpMain.y = lpwp->y;\r
5363         }\r
5364     }\r
5365     break;\r
5366 \r
5367   /* [AS] Snapping */\r
5368   case WM_ENTERSIZEMOVE:\r
5369     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5370     if (hwnd == hwndMain) {\r
5371       doingSizing = TRUE;\r
5372       lastSizing = 0;\r
5373     }\r
5374     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5375     break;\r
5376 \r
5377   case WM_SIZING:\r
5378     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5379     if (hwnd == hwndMain) {\r
5380       lastSizing = wParam;\r
5381     }\r
5382     break;\r
5383 \r
5384   case WM_MOVING:\r
5385     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5386       return OnMoving( &sd, hwnd, wParam, lParam );\r
5387 \r
5388   case WM_EXITSIZEMOVE:\r
5389     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5390     if (hwnd == hwndMain) {\r
5391       RECT client;\r
5392       doingSizing = FALSE;\r
5393       InvalidateRect(hwnd, &boardRect, FALSE);\r
5394       GetClientRect(hwnd, &client);\r
5395       ResizeBoard(client.right, client.bottom, lastSizing);\r
5396       lastSizing = 0;\r
5397       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5398     }\r
5399     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5400     break;\r
5401 \r
5402   case WM_DESTROY: /* message: window being destroyed */\r
5403     PostQuitMessage(0);\r
5404     break;\r
5405 \r
5406   case WM_CLOSE:\r
5407     if (hwnd == hwndMain) {\r
5408       ExitEvent(0);\r
5409     }\r
5410     break;\r
5411 \r
5412   default:      /* Passes it on if unprocessed */\r
5413     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5414   }\r
5415   return 0;\r
5416 }\r
5417 \r
5418 /*---------------------------------------------------------------------------*\\r
5419  *\r
5420  * Misc utility routines\r
5421  *\r
5422 \*---------------------------------------------------------------------------*/\r
5423 \r
5424 /*\r
5425  * Decent random number generator, at least not as bad as Windows\r
5426  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5427  */\r
5428 unsigned int randstate;\r
5429 \r
5430 int\r
5431 myrandom(void)\r
5432 {\r
5433   randstate = randstate * 1664525 + 1013904223;\r
5434   return (int) randstate & 0x7fffffff;\r
5435 }\r
5436 \r
5437 void\r
5438 mysrandom(unsigned int seed)\r
5439 {\r
5440   randstate = seed;\r
5441 }\r
5442 \r
5443 \r
5444 /* \r
5445  * returns TRUE if user selects a different color, FALSE otherwise \r
5446  */\r
5447 \r
5448 BOOL\r
5449 ChangeColor(HWND hwnd, COLORREF *which)\r
5450 {\r
5451   static BOOL firstTime = TRUE;\r
5452   static DWORD customColors[16];\r
5453   CHOOSECOLOR cc;\r
5454   COLORREF newcolor;\r
5455   int i;\r
5456   ColorClass ccl;\r
5457 \r
5458   if (firstTime) {\r
5459     /* Make initial colors in use available as custom colors */\r
5460     /* Should we put the compiled-in defaults here instead? */\r
5461     i = 0;\r
5462     customColors[i++] = lightSquareColor & 0xffffff;\r
5463     customColors[i++] = darkSquareColor & 0xffffff;\r
5464     customColors[i++] = whitePieceColor & 0xffffff;\r
5465     customColors[i++] = blackPieceColor & 0xffffff;\r
5466     customColors[i++] = highlightSquareColor & 0xffffff;\r
5467     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5468 \r
5469     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5470       customColors[i++] = textAttribs[ccl].color;\r
5471     }\r
5472     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5473     firstTime = FALSE;\r
5474   }\r
5475 \r
5476   cc.lStructSize = sizeof(cc);\r
5477   cc.hwndOwner = hwnd;\r
5478   cc.hInstance = NULL;\r
5479   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5480   cc.lpCustColors = (LPDWORD) customColors;\r
5481   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5482 \r
5483   if (!ChooseColor(&cc)) return FALSE;\r
5484 \r
5485   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5486   if (newcolor == *which) return FALSE;\r
5487   *which = newcolor;\r
5488   return TRUE;\r
5489 \r
5490   /*\r
5491   InitDrawingColors();\r
5492   InvalidateRect(hwnd, &boardRect, FALSE);\r
5493   */\r
5494 }\r
5495 \r
5496 BOOLEAN\r
5497 MyLoadSound(MySound *ms)\r
5498 {\r
5499   BOOL ok = FALSE;\r
5500   struct stat st;\r
5501   FILE *f;\r
5502 \r
5503   if (ms->data) free(ms->data);\r
5504   ms->data = NULL;\r
5505 \r
5506   switch (ms->name[0]) {\r
5507   case NULLCHAR:\r
5508     /* Silence */\r
5509     ok = TRUE;\r
5510     break;\r
5511   case '$':\r
5512     /* System sound from Control Panel.  Don't preload here. */\r
5513     ok = TRUE;\r
5514     break;\r
5515   case '!':\r
5516     if (ms->name[1] == NULLCHAR) {\r
5517       /* "!" alone = silence */\r
5518       ok = TRUE;\r
5519     } else {\r
5520       /* Builtin wave resource.  Error if not found. */\r
5521       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5522       if (h == NULL) break;\r
5523       ms->data = (void *)LoadResource(hInst, h);\r
5524       if (h == NULL) break;\r
5525       ok = TRUE;\r
5526     }\r
5527     break;\r
5528   default:\r
5529     /* .wav file.  Error if not found. */\r
5530     f = fopen(ms->name, "rb");\r
5531     if (f == NULL) break;\r
5532     if (fstat(fileno(f), &st) < 0) break;\r
5533     ms->data = malloc(st.st_size);\r
5534     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5535     fclose(f);\r
5536     ok = TRUE;\r
5537     break;\r
5538   }\r
5539   if (!ok) {\r
5540     char buf[MSG_SIZ];\r
5541     sprintf(buf, _("Error loading sound %s"), ms->name);\r
5542     DisplayError(buf, GetLastError());\r
5543   }\r
5544   return ok;\r
5545 }\r
5546 \r
5547 BOOLEAN\r
5548 MyPlaySound(MySound *ms)\r
5549 {\r
5550   BOOLEAN ok = FALSE;\r
5551 \r
5552   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5553   switch (ms->name[0]) {\r
5554   case NULLCHAR:\r
5555         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5556     /* Silence */\r
5557     ok = TRUE;\r
5558     break;\r
5559   case '$':\r
5560     /* System sound from Control Panel (deprecated feature).\r
5561        "$" alone or an unset sound name gets default beep (still in use). */\r
5562     if (ms->name[1]) {\r
5563       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5564     }\r
5565     if (!ok) ok = MessageBeep(MB_OK);\r
5566     break; \r
5567   case '!':\r
5568     /* Builtin wave resource, or "!" alone for silence */\r
5569     if (ms->name[1]) {\r
5570       if (ms->data == NULL) return FALSE;\r
5571       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5572     } else {\r
5573       ok = TRUE;\r
5574     }\r
5575     break;\r
5576   default:\r
5577     /* .wav file.  Error if not found. */\r
5578     if (ms->data == NULL) return FALSE;\r
5579     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5580     break;\r
5581   }\r
5582   /* Don't print an error: this can happen innocently if the sound driver\r
5583      is busy; for instance, if another instance of WinBoard is playing\r
5584      a sound at about the same time. */\r
5585   return ok;\r
5586 }\r
5587 \r
5588 \r
5589 LRESULT CALLBACK\r
5590 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5591 {\r
5592   BOOL ok;\r
5593   OPENFILENAME *ofn;\r
5594   static UINT *number; /* gross that this is static */\r
5595 \r
5596   switch (message) {\r
5597   case WM_INITDIALOG: /* message: initialize dialog box */\r
5598     /* Center the dialog over the application window */\r
5599     ofn = (OPENFILENAME *) lParam;\r
5600     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5601       number = (UINT *) ofn->lCustData;\r
5602       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5603     } else {\r
5604       number = NULL;\r
5605     }\r
5606     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5607     Translate(hDlg, 1536);\r
5608     return FALSE;  /* Allow for further processing */\r
5609 \r
5610   case WM_COMMAND:\r
5611     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5612       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5613     }\r
5614     return FALSE;  /* Allow for further processing */\r
5615   }\r
5616   return FALSE;\r
5617 }\r
5618 \r
5619 UINT APIENTRY\r
5620 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5621 {\r
5622   static UINT *number;\r
5623   OPENFILENAME *ofname;\r
5624   OFNOTIFY *ofnot;\r
5625   switch (uiMsg) {\r
5626   case WM_INITDIALOG:\r
5627     Translate(hdlg, DLG_IndexNumber);\r
5628     ofname = (OPENFILENAME *)lParam;\r
5629     number = (UINT *)(ofname->lCustData);\r
5630     break;\r
5631   case WM_NOTIFY:\r
5632     ofnot = (OFNOTIFY *)lParam;\r
5633     if (ofnot->hdr.code == CDN_FILEOK) {\r
5634       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5635     }\r
5636     break;\r
5637   }\r
5638   return 0;\r
5639 }\r
5640 \r
5641 \r
5642 FILE *\r
5643 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5644                char *nameFilt, char *dlgTitle, UINT *number,\r
5645                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5646 {\r
5647   OPENFILENAME openFileName;\r
5648   char buf1[MSG_SIZ];\r
5649   FILE *f;\r
5650 \r
5651   if (fileName == NULL) fileName = buf1;\r
5652   if (defName == NULL) {\r
5653     strcpy(fileName, "*.");\r
5654     strcat(fileName, defExt);\r
5655   } else {\r
5656     strcpy(fileName, defName);\r
5657   }\r
5658   if (fileTitle) strcpy(fileTitle, "");\r
5659   if (number) *number = 0;\r
5660 \r
5661   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5662   openFileName.hwndOwner         = hwnd;\r
5663   openFileName.hInstance         = (HANDLE) hInst;\r
5664   openFileName.lpstrFilter       = nameFilt;\r
5665   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5666   openFileName.nMaxCustFilter    = 0L;\r
5667   openFileName.nFilterIndex      = 1L;\r
5668   openFileName.lpstrFile         = fileName;\r
5669   openFileName.nMaxFile          = MSG_SIZ;\r
5670   openFileName.lpstrFileTitle    = fileTitle;\r
5671   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5672   openFileName.lpstrInitialDir   = NULL;\r
5673   openFileName.lpstrTitle        = dlgTitle;\r
5674   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5675     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5676     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5677     | (oldDialog ? 0 : OFN_EXPLORER);\r
5678   openFileName.nFileOffset       = 0;\r
5679   openFileName.nFileExtension    = 0;\r
5680   openFileName.lpstrDefExt       = defExt;\r
5681   openFileName.lCustData         = (LONG) number;\r
5682   openFileName.lpfnHook          = oldDialog ?\r
5683     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5684   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5685 \r
5686   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5687                         GetOpenFileName(&openFileName)) {\r
5688     /* open the file */\r
5689     f = fopen(openFileName.lpstrFile, write);\r
5690     if (f == NULL) {\r
5691       MessageBox(hwnd, _("File open failed"), NULL,\r
5692                  MB_OK|MB_ICONEXCLAMATION);\r
5693       return NULL;\r
5694     }\r
5695   } else {\r
5696     int err = CommDlgExtendedError();\r
5697     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5698     return FALSE;\r
5699   }\r
5700   return f;\r
5701 }\r
5702 \r
5703 \r
5704 \r
5705 VOID APIENTRY\r
5706 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5707 {\r
5708   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5709 \r
5710   /*\r
5711    * Get the first pop-up menu in the menu template. This is the\r
5712    * menu that TrackPopupMenu displays.\r
5713    */\r
5714   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5715 \r
5716   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5717 \r
5718   /*\r
5719    * TrackPopup uses screen coordinates, so convert the\r
5720    * coordinates of the mouse click to screen coordinates.\r
5721    */\r
5722   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5723 \r
5724   /* Draw and track the floating pop-up menu. */\r
5725   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5726                  pt.x, pt.y, 0, hwnd, NULL);\r
5727 \r
5728   /* Destroy the menu.*/\r
5729   DestroyMenu(hmenu);\r
5730 }\r
5731    \r
5732 typedef struct {\r
5733   HWND hDlg, hText;\r
5734   int sizeX, sizeY, newSizeX, newSizeY;\r
5735   HDWP hdwp;\r
5736 } ResizeEditPlusButtonsClosure;\r
5737 \r
5738 BOOL CALLBACK\r
5739 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5740 {\r
5741   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5742   RECT rect;\r
5743   POINT pt;\r
5744 \r
5745   if (hChild == cl->hText) return TRUE;\r
5746   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5747   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5748   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5749   ScreenToClient(cl->hDlg, &pt);\r
5750   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5751     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5752   return TRUE;\r
5753 }\r
5754 \r
5755 /* Resize a dialog that has a (rich) edit field filling most of\r
5756    the top, with a row of buttons below */\r
5757 VOID\r
5758 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5759 {\r
5760   RECT rectText;\r
5761   int newTextHeight, newTextWidth;\r
5762   ResizeEditPlusButtonsClosure cl;\r
5763   \r
5764   /*if (IsIconic(hDlg)) return;*/\r
5765   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5766   \r
5767   cl.hdwp = BeginDeferWindowPos(8);\r
5768 \r
5769   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5770   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5771   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5772   if (newTextHeight < 0) {\r
5773     newSizeY += -newTextHeight;\r
5774     newTextHeight = 0;\r
5775   }\r
5776   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5777     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5778 \r
5779   cl.hDlg = hDlg;\r
5780   cl.hText = hText;\r
5781   cl.sizeX = sizeX;\r
5782   cl.sizeY = sizeY;\r
5783   cl.newSizeX = newSizeX;\r
5784   cl.newSizeY = newSizeY;\r
5785   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5786 \r
5787   EndDeferWindowPos(cl.hdwp);\r
5788 }\r
5789 \r
5790 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5791 {\r
5792     RECT    rChild, rParent;\r
5793     int     wChild, hChild, wParent, hParent;\r
5794     int     wScreen, hScreen, xNew, yNew;\r
5795     HDC     hdc;\r
5796 \r
5797     /* Get the Height and Width of the child window */\r
5798     GetWindowRect (hwndChild, &rChild);\r
5799     wChild = rChild.right - rChild.left;\r
5800     hChild = rChild.bottom - rChild.top;\r
5801 \r
5802     /* Get the Height and Width of the parent window */\r
5803     GetWindowRect (hwndParent, &rParent);\r
5804     wParent = rParent.right - rParent.left;\r
5805     hParent = rParent.bottom - rParent.top;\r
5806 \r
5807     /* Get the display limits */\r
5808     hdc = GetDC (hwndChild);\r
5809     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5810     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5811     ReleaseDC(hwndChild, hdc);\r
5812 \r
5813     /* Calculate new X position, then adjust for screen */\r
5814     xNew = rParent.left + ((wParent - wChild) /2);\r
5815     if (xNew < 0) {\r
5816         xNew = 0;\r
5817     } else if ((xNew+wChild) > wScreen) {\r
5818         xNew = wScreen - wChild;\r
5819     }\r
5820 \r
5821     /* Calculate new Y position, then adjust for screen */\r
5822     if( mode == 0 ) {\r
5823         yNew = rParent.top  + ((hParent - hChild) /2);\r
5824     }\r
5825     else {\r
5826         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5827     }\r
5828 \r
5829     if (yNew < 0) {\r
5830         yNew = 0;\r
5831     } else if ((yNew+hChild) > hScreen) {\r
5832         yNew = hScreen - hChild;\r
5833     }\r
5834 \r
5835     /* Set it, and return */\r
5836     return SetWindowPos (hwndChild, NULL,\r
5837                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5838 }\r
5839 \r
5840 /* Center one window over another */\r
5841 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5842 {\r
5843     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5844 }\r
5845 \r
5846 /*---------------------------------------------------------------------------*\\r
5847  *\r
5848  * Startup Dialog functions\r
5849  *\r
5850 \*---------------------------------------------------------------------------*/\r
5851 void\r
5852 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5853 {\r
5854   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5855 \r
5856   while (*cd != NULL) {\r
5857     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5858     cd++;\r
5859   }\r
5860 }\r
5861 \r
5862 void\r
5863 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5864 {\r
5865   char buf1[MAX_ARG_LEN];\r
5866   int len;\r
5867 \r
5868   if (str[0] == '@') {\r
5869     FILE* f = fopen(str + 1, "r");\r
5870     if (f == NULL) {\r
5871       DisplayFatalError(str + 1, errno, 2);\r
5872       return;\r
5873     }\r
5874     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5875     fclose(f);\r
5876     buf1[len] = NULLCHAR;\r
5877     str = buf1;\r
5878   }\r
5879 \r
5880   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5881 \r
5882   for (;;) {\r
5883     char buf[MSG_SIZ];\r
5884     char *end = strchr(str, '\n');\r
5885     if (end == NULL) return;\r
5886     memcpy(buf, str, end - str);\r
5887     buf[end - str] = NULLCHAR;\r
5888     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5889     str = end + 1;\r
5890   }\r
5891 }\r
5892 \r
5893 void\r
5894 SetStartupDialogEnables(HWND hDlg)\r
5895 {\r
5896   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
5897     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5898     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
5899   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5900     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
5901   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
5902     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
5903   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
5904     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
5905   EnableWindow(GetDlgItem(hDlg, IDOK),\r
5906     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5907     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
5908     IsDlgButtonChecked(hDlg, OPT_View));\r
5909 }\r
5910 \r
5911 char *\r
5912 QuoteForFilename(char *filename)\r
5913 {\r
5914   int dquote, space;\r
5915   dquote = strchr(filename, '"') != NULL;\r
5916   space = strchr(filename, ' ') != NULL;\r
5917   if (dquote || space) {\r
5918     if (dquote) {\r
5919       return "'";\r
5920     } else {\r
5921       return "\"";\r
5922     }\r
5923   } else {\r
5924     return "";\r
5925   }\r
5926 }\r
5927 \r
5928 VOID\r
5929 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
5930 {\r
5931   char buf[MSG_SIZ];\r
5932   char *q;\r
5933 \r
5934   InitComboStringsFromOption(hwndCombo, nthnames);\r
5935   q = QuoteForFilename(nthcp);\r
5936   sprintf(buf, "%s%s%s", q, nthcp, q);\r
5937   if (*nthdir != NULLCHAR) {\r
5938     q = QuoteForFilename(nthdir);\r
5939     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
5940   }\r
5941   if (*nthcp == NULLCHAR) {\r
5942     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
5943   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
5944     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
5945     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
5946   }\r
5947 }\r
5948 \r
5949 LRESULT CALLBACK\r
5950 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5951 {\r
5952   char buf[MSG_SIZ];\r
5953   HANDLE hwndCombo;\r
5954   char *p;\r
5955 \r
5956   switch (message) {\r
5957   case WM_INITDIALOG:\r
5958     /* Center the dialog */\r
5959     CenterWindow (hDlg, GetDesktopWindow());\r
5960     Translate(hDlg, DLG_Startup);\r
5961     /* Initialize the dialog items */\r
5962     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
5963                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
5964                   firstChessProgramNames);\r
5965     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5966                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
5967                   secondChessProgramNames);\r
5968     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
5969     InitComboStringsFromOption(hwndCombo, icsNames);    \r
5970     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
5971     if (*appData.icsHelper != NULLCHAR) {\r
5972       char *q = QuoteForFilename(appData.icsHelper);\r
5973       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
5974     }\r
5975     if (*appData.icsHost == NULLCHAR) {\r
5976       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
5977       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
5978     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
5979       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
5980       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
5981     }\r
5982 \r
5983     if (appData.icsActive) {\r
5984       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
5985     }\r
5986     else if (appData.noChessProgram) {\r
5987       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
5988     }\r
5989     else {\r
5990       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
5991     }\r
5992 \r
5993     SetStartupDialogEnables(hDlg);\r
5994     return TRUE;\r
5995 \r
5996   case WM_COMMAND:\r
5997     switch (LOWORD(wParam)) {\r
5998     case IDOK:\r
5999       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6000         strcpy(buf, "/fcp=");\r
6001         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6002         p = buf;\r
6003         ParseArgs(StringGet, &p);\r
6004         strcpy(buf, "/scp=");\r
6005         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6006         p = buf;\r
6007         ParseArgs(StringGet, &p);\r
6008         appData.noChessProgram = FALSE;\r
6009         appData.icsActive = FALSE;\r
6010       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6011         strcpy(buf, "/ics /icshost=");\r
6012         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6013         p = buf;\r
6014         ParseArgs(StringGet, &p);\r
6015         if (appData.zippyPlay) {\r
6016           strcpy(buf, "/fcp=");\r
6017           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6018           p = buf;\r
6019           ParseArgs(StringGet, &p);\r
6020         }\r
6021       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6022         appData.noChessProgram = TRUE;\r
6023         appData.icsActive = FALSE;\r
6024       } else {\r
6025         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6026                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6027         return TRUE;\r
6028       }\r
6029       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6030         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6031         p = buf;\r
6032         ParseArgs(StringGet, &p);\r
6033       }\r
6034       EndDialog(hDlg, TRUE);\r
6035       return TRUE;\r
6036 \r
6037     case IDCANCEL:\r
6038       ExitEvent(0);\r
6039       return TRUE;\r
6040 \r
6041     case IDM_HELPCONTENTS:\r
6042       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6043         MessageBox (GetFocus(),\r
6044                     _("Unable to activate help"),\r
6045                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6046       }\r
6047       break;\r
6048 \r
6049     default:\r
6050       SetStartupDialogEnables(hDlg);\r
6051       break;\r
6052     }\r
6053     break;\r
6054   }\r
6055   return FALSE;\r
6056 }\r
6057 \r
6058 /*---------------------------------------------------------------------------*\\r
6059  *\r
6060  * About box dialog functions\r
6061  *\r
6062 \*---------------------------------------------------------------------------*/\r
6063 \r
6064 /* Process messages for "About" dialog box */\r
6065 LRESULT CALLBACK\r
6066 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6067 {\r
6068   switch (message) {\r
6069   case WM_INITDIALOG: /* message: initialize dialog box */\r
6070     /* Center the dialog over the application window */\r
6071     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6072     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6073     Translate(hDlg, ABOUTBOX);\r
6074     JAWS_COPYRIGHT\r
6075     return (TRUE);\r
6076 \r
6077   case WM_COMMAND: /* message: received a command */\r
6078     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6079         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6080       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6081       return (TRUE);\r
6082     }\r
6083     break;\r
6084   }\r
6085   return (FALSE);\r
6086 }\r
6087 \r
6088 /*---------------------------------------------------------------------------*\\r
6089  *\r
6090  * Comment Dialog functions\r
6091  *\r
6092 \*---------------------------------------------------------------------------*/\r
6093 \r
6094 LRESULT CALLBACK\r
6095 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6096 {\r
6097   static HANDLE hwndText = NULL;\r
6098   int len, newSizeX, newSizeY, flags;\r
6099   static int sizeX, sizeY;\r
6100   char *str;\r
6101   RECT rect;\r
6102   MINMAXINFO *mmi;\r
6103 \r
6104   switch (message) {\r
6105   case WM_INITDIALOG: /* message: initialize dialog box */\r
6106     /* Initialize the dialog items */\r
6107     Translate(hDlg, DLG_EditComment);\r
6108     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6109     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6110     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6111     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6112     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6113     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6114     SetWindowText(hDlg, commentTitle);\r
6115     if (editComment) {\r
6116       SetFocus(hwndText);\r
6117     } else {\r
6118       SetFocus(GetDlgItem(hDlg, IDOK));\r
6119     }\r
6120     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6121                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6122                 MAKELPARAM(FALSE, 0));\r
6123     /* Size and position the dialog */\r
6124     if (!commentDialog) {\r
6125       commentDialog = hDlg;\r
6126       flags = SWP_NOZORDER;\r
6127       GetClientRect(hDlg, &rect);\r
6128       sizeX = rect.right;\r
6129       sizeY = rect.bottom;\r
6130       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6131           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6132         WINDOWPLACEMENT wp;\r
6133         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6134         wp.length = sizeof(WINDOWPLACEMENT);\r
6135         wp.flags = 0;\r
6136         wp.showCmd = SW_SHOW;\r
6137         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6138         wp.rcNormalPosition.left = wpComment.x;\r
6139         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6140         wp.rcNormalPosition.top = wpComment.y;\r
6141         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6142         SetWindowPlacement(hDlg, &wp);\r
6143 \r
6144         GetClientRect(hDlg, &rect);\r
6145         newSizeX = rect.right;\r
6146         newSizeY = rect.bottom;\r
6147         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6148                               newSizeX, newSizeY);\r
6149         sizeX = newSizeX;\r
6150         sizeY = newSizeY;\r
6151       }\r
6152     }\r
6153     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );\r
6154     return FALSE;\r
6155 \r
6156   case WM_COMMAND: /* message: received a command */\r
6157     switch (LOWORD(wParam)) {\r
6158     case IDOK:\r
6159       if (editComment) {\r
6160         char *p, *q;\r
6161         /* Read changed options from the dialog box */\r
6162         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6163         len = GetWindowTextLength(hwndText);\r
6164         str = (char *) malloc(len + 1);\r
6165         GetWindowText(hwndText, str, len + 1);\r
6166         p = q = str;\r
6167         while (*q) {\r
6168           if (*q == '\r')\r
6169             q++;\r
6170           else\r
6171             *p++ = *q++;\r
6172         }\r
6173         *p = NULLCHAR;\r
6174         ReplaceComment(commentIndex, str);\r
6175         free(str);\r
6176       }\r
6177       CommentPopDown();\r
6178       return TRUE;\r
6179 \r
6180     case IDCANCEL:\r
6181     case OPT_CancelComment:\r
6182       CommentPopDown();\r
6183       return TRUE;\r
6184 \r
6185     case OPT_ClearComment:\r
6186       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6187       break;\r
6188 \r
6189     case OPT_EditComment:\r
6190       EditCommentEvent();\r
6191       return TRUE;\r
6192 \r
6193     default:\r
6194       break;\r
6195     }\r
6196     break;\r
6197 \r
6198   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6199         if( wParam == OPT_CommentText ) {\r
6200             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6201 \r
6202             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {\r
6203                 POINTL pt;\r
6204                 LRESULT index;\r
6205 \r
6206                 pt.x = LOWORD( lpMF->lParam );\r
6207                 pt.y = HIWORD( lpMF->lParam );\r
6208 \r
6209                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6210 \r
6211                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6212                 len = GetWindowTextLength(hwndText);\r
6213                 str = (char *) malloc(len + 1);\r
6214                 GetWindowText(hwndText, str, len + 1);\r
6215                 ReplaceComment(commentIndex, str);\r
6216                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6217                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6218                 free(str);\r
6219 \r
6220                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6221                 lpMF->msg = WM_USER;\r
6222 \r
6223                 return TRUE;\r
6224             }\r
6225         }\r
6226         break;\r
6227 \r
6228   case WM_SIZE:\r
6229     newSizeX = LOWORD(lParam);\r
6230     newSizeY = HIWORD(lParam);\r
6231     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6232     sizeX = newSizeX;\r
6233     sizeY = newSizeY;\r
6234     break;\r
6235 \r
6236   case WM_GETMINMAXINFO:\r
6237     /* Prevent resizing window too small */\r
6238     mmi = (MINMAXINFO *) lParam;\r
6239     mmi->ptMinTrackSize.x = 100;\r
6240     mmi->ptMinTrackSize.y = 100;\r
6241     break;\r
6242   }\r
6243   return FALSE;\r
6244 }\r
6245 \r
6246 VOID\r
6247 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6248 {\r
6249   FARPROC lpProc;\r
6250   char *p, *q;\r
6251 \r
6252   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6253 \r
6254   if (str == NULL) str = "";\r
6255   p = (char *) malloc(2 * strlen(str) + 2);\r
6256   q = p;\r
6257   while (*str) {\r
6258     if (*str == '\n') *q++ = '\r';\r
6259     *q++ = *str++;\r
6260   }\r
6261   *q = NULLCHAR;\r
6262   if (commentText != NULL) free(commentText);\r
6263 \r
6264   commentIndex = index;\r
6265   commentTitle = title;\r
6266   commentText = p;\r
6267   editComment = edit;\r
6268 \r
6269   if (commentDialog) {\r
6270     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6271     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6272   } else {\r
6273     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6274     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6275                  hwndMain, (DLGPROC)lpProc);\r
6276     FreeProcInstance(lpProc);\r
6277   }\r
6278   commentUp = TRUE;\r
6279 }\r
6280 \r
6281 \r
6282 /*---------------------------------------------------------------------------*\\r
6283  *\r
6284  * Type-in move dialog functions\r
6285  * \r
6286 \*---------------------------------------------------------------------------*/\r
6287 \r
6288 LRESULT CALLBACK\r
6289 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6290 {\r
6291   char move[MSG_SIZ];\r
6292   HWND hInput;\r
6293   ChessMove moveType;\r
6294   int fromX, fromY, toX, toY;\r
6295   char promoChar;\r
6296 \r
6297   switch (message) {\r
6298   case WM_INITDIALOG:\r
6299     move[0] = (char) lParam;\r
6300     move[1] = NULLCHAR;\r
6301     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6302     Translate(hDlg, DLG_TypeInMove);\r
6303     hInput = GetDlgItem(hDlg, OPT_Move);\r
6304     SetWindowText(hInput, move);\r
6305     SetFocus(hInput);\r
6306     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6307     return FALSE;\r
6308 \r
6309   case WM_COMMAND:\r
6310     switch (LOWORD(wParam)) {\r
6311     case IDOK:\r
6312       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6313       { int n; Board board;\r
6314         // [HGM] FENedit\r
6315         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
6316                 EditPositionPasteFEN(move);\r
6317                 EndDialog(hDlg, TRUE);\r
6318                 return TRUE;\r
6319         }\r
6320         // [HGM] movenum: allow move number to be typed in any mode\r
6321         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
6322           ToNrEvent(2*n-1);\r
6323           EndDialog(hDlg, TRUE);\r
6324           return TRUE;\r
6325         }\r
6326       }\r
6327       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6328         gameMode != Training) {\r
6329         DisplayMoveError(_("Displayed move is not current"));\r
6330       } else {\r
6331 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
6332         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6333           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
6334         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
6335         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6336           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6337           if (gameMode != Training)\r
6338               forwardMostMove = currentMove;\r
6339           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6340         } else {\r
6341           DisplayMoveError(_("Could not parse move"));\r
6342         }\r
6343       }\r
6344       EndDialog(hDlg, TRUE);\r
6345       return TRUE;\r
6346     case IDCANCEL:\r
6347       EndDialog(hDlg, FALSE);\r
6348       return TRUE;\r
6349     default:\r
6350       break;\r
6351     }\r
6352     break;\r
6353   }\r
6354   return FALSE;\r
6355 }\r
6356 \r
6357 VOID\r
6358 PopUpMoveDialog(char firstchar)\r
6359 {\r
6360     FARPROC lpProc;\r
6361     \r
6362     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6363         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6364         gameMode == AnalyzeMode || gameMode == EditGame || \r
6365         gameMode == EditPosition || gameMode == IcsExamining ||\r
6366         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6367         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
6368                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
6369                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
6370         gameMode == Training) {\r
6371       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6372       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6373         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6374       FreeProcInstance(lpProc);\r
6375     }\r
6376 }\r
6377 \r
6378 /*---------------------------------------------------------------------------*\\r
6379  *\r
6380  * Type-in name dialog functions\r
6381  * \r
6382 \*---------------------------------------------------------------------------*/\r
6383 \r
6384 LRESULT CALLBACK\r
6385 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6386 {\r
6387   char move[MSG_SIZ];\r
6388   HWND hInput;\r
6389 \r
6390   switch (message) {\r
6391   case WM_INITDIALOG:\r
6392     move[0] = (char) lParam;\r
6393     move[1] = NULLCHAR;\r
6394     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6395     Translate(hDlg, DLG_TypeInName);\r
6396     hInput = GetDlgItem(hDlg, OPT_Name);\r
6397     SetWindowText(hInput, move);\r
6398     SetFocus(hInput);\r
6399     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6400     return FALSE;\r
6401 \r
6402   case WM_COMMAND:\r
6403     switch (LOWORD(wParam)) {\r
6404     case IDOK:\r
6405       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6406       appData.userName = strdup(move);\r
6407       SetUserLogo();\r
6408       SetGameInfo();\r
6409       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6410         sprintf(move, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6411         DisplayTitle(move);\r
6412       }\r
6413 \r
6414 \r
6415       EndDialog(hDlg, TRUE);\r
6416       return TRUE;\r
6417     case IDCANCEL:\r
6418       EndDialog(hDlg, FALSE);\r
6419       return TRUE;\r
6420     default:\r
6421       break;\r
6422     }\r
6423     break;\r
6424   }\r
6425   return FALSE;\r
6426 }\r
6427 \r
6428 VOID\r
6429 PopUpNameDialog(char firstchar)\r
6430 {\r
6431     FARPROC lpProc;\r
6432     \r
6433       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6434       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6435         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6436       FreeProcInstance(lpProc);\r
6437 }\r
6438 \r
6439 /*---------------------------------------------------------------------------*\\r
6440  *\r
6441  *  Error dialogs\r
6442  * \r
6443 \*---------------------------------------------------------------------------*/\r
6444 \r
6445 /* Nonmodal error box */\r
6446 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6447                              WPARAM wParam, LPARAM lParam);\r
6448 \r
6449 VOID\r
6450 ErrorPopUp(char *title, char *content)\r
6451 {\r
6452   FARPROC lpProc;\r
6453   char *p, *q;\r
6454   BOOLEAN modal = hwndMain == NULL;\r
6455 \r
6456   p = content;\r
6457   q = errorMessage;\r
6458   while (*p) {\r
6459     if (*p == '\n') {\r
6460       if (modal) {\r
6461         *q++ = ' ';\r
6462         p++;\r
6463       } else {\r
6464         *q++ = '\r';\r
6465         *q++ = *p++;\r
6466       }\r
6467     } else {\r
6468       *q++ = *p++;\r
6469     }\r
6470   }\r
6471   *q = NULLCHAR;\r
6472   strncpy(errorTitle, title, sizeof(errorTitle));\r
6473   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6474   \r
6475   if (modal) {\r
6476     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6477   } else {\r
6478     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6479     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6480                  hwndMain, (DLGPROC)lpProc);\r
6481     FreeProcInstance(lpProc);\r
6482   }\r
6483 }\r
6484 \r
6485 VOID\r
6486 ErrorPopDown()\r
6487 {\r
6488   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6489   if (errorDialog == NULL) return;\r
6490   DestroyWindow(errorDialog);\r
6491   errorDialog = NULL;\r
6492   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6493 }\r
6494 \r
6495 LRESULT CALLBACK\r
6496 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6497 {\r
6498   HANDLE hwndText;\r
6499   RECT rChild;\r
6500 \r
6501   switch (message) {\r
6502   case WM_INITDIALOG:\r
6503     GetWindowRect(hDlg, &rChild);\r
6504 \r
6505     /*\r
6506     SetWindowPos(hDlg, NULL, rChild.left,\r
6507       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6508       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6509     */\r
6510 \r
6511     /* \r
6512         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6513         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6514         and it doesn't work when you resize the dialog.\r
6515         For now, just give it a default position.\r
6516     */\r
6517     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6518     Translate(hDlg, DLG_Error);\r
6519 \r
6520     errorDialog = hDlg;\r
6521     SetWindowText(hDlg, errorTitle);\r
6522     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6523     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6524     return FALSE;\r
6525 \r
6526   case WM_COMMAND:\r
6527     switch (LOWORD(wParam)) {\r
6528     case IDOK:\r
6529     case IDCANCEL:\r
6530       if (errorDialog == hDlg) errorDialog = NULL;\r
6531       DestroyWindow(hDlg);\r
6532       return TRUE;\r
6533 \r
6534     default:\r
6535       break;\r
6536     }\r
6537     break;\r
6538   }\r
6539   return FALSE;\r
6540 }\r
6541 \r
6542 #ifdef GOTHIC\r
6543 HWND gothicDialog = NULL;\r
6544 \r
6545 LRESULT CALLBACK\r
6546 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6547 {\r
6548   HANDLE hwndText;\r
6549   RECT rChild;\r
6550   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6551 \r
6552   switch (message) {\r
6553   case WM_INITDIALOG:\r
6554     GetWindowRect(hDlg, &rChild);\r
6555 \r
6556     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6557                                                              SWP_NOZORDER);\r
6558 \r
6559     /* \r
6560         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6561         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6562         and it doesn't work when you resize the dialog.\r
6563         For now, just give it a default position.\r
6564     */\r
6565     gothicDialog = hDlg;\r
6566     SetWindowText(hDlg, errorTitle);\r
6567     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6568     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6569     return FALSE;\r
6570 \r
6571   case WM_COMMAND:\r
6572     switch (LOWORD(wParam)) {\r
6573     case IDOK:\r
6574     case IDCANCEL:\r
6575       if (errorDialog == hDlg) errorDialog = NULL;\r
6576       DestroyWindow(hDlg);\r
6577       return TRUE;\r
6578 \r
6579     default:\r
6580       break;\r
6581     }\r
6582     break;\r
6583   }\r
6584   return FALSE;\r
6585 }\r
6586 \r
6587 VOID\r
6588 GothicPopUp(char *title, VariantClass variant)\r
6589 {\r
6590   FARPROC lpProc;\r
6591   static char *lastTitle;\r
6592 \r
6593   strncpy(errorTitle, title, sizeof(errorTitle));\r
6594   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6595 \r
6596   if(lastTitle != title && gothicDialog != NULL) {\r
6597     DestroyWindow(gothicDialog);\r
6598     gothicDialog = NULL;\r
6599   }\r
6600   if(variant != VariantNormal && gothicDialog == NULL) {\r
6601     title = lastTitle;\r
6602     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6603     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6604                  hwndMain, (DLGPROC)lpProc);\r
6605     FreeProcInstance(lpProc);\r
6606   }\r
6607 }\r
6608 #endif\r
6609 \r
6610 /*---------------------------------------------------------------------------*\\r
6611  *\r
6612  *  Ics Interaction console functions\r
6613  *\r
6614 \*---------------------------------------------------------------------------*/\r
6615 \r
6616 #define HISTORY_SIZE 64\r
6617 static char *history[HISTORY_SIZE];\r
6618 int histIn = 0, histP = 0;\r
6619 \r
6620 VOID\r
6621 SaveInHistory(char *cmd)\r
6622 {\r
6623   if (history[histIn] != NULL) {\r
6624     free(history[histIn]);\r
6625     history[histIn] = NULL;\r
6626   }\r
6627   if (*cmd == NULLCHAR) return;\r
6628   history[histIn] = StrSave(cmd);\r
6629   histIn = (histIn + 1) % HISTORY_SIZE;\r
6630   if (history[histIn] != NULL) {\r
6631     free(history[histIn]);\r
6632     history[histIn] = NULL;\r
6633   }\r
6634   histP = histIn;\r
6635 }\r
6636 \r
6637 char *\r
6638 PrevInHistory(char *cmd)\r
6639 {\r
6640   int newhp;\r
6641   if (histP == histIn) {\r
6642     if (history[histIn] != NULL) free(history[histIn]);\r
6643     history[histIn] = StrSave(cmd);\r
6644   }\r
6645   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6646   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6647   histP = newhp;\r
6648   return history[histP];\r
6649 }\r
6650 \r
6651 char *\r
6652 NextInHistory()\r
6653 {\r
6654   if (histP == histIn) return NULL;\r
6655   histP = (histP + 1) % HISTORY_SIZE;\r
6656   return history[histP];   \r
6657 }\r
6658 \r
6659 HMENU\r
6660 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6661 {\r
6662   HMENU hmenu, h;\r
6663   int i = 0;\r
6664   hmenu = LoadMenu(hInst, "TextMenu");\r
6665   h = GetSubMenu(hmenu, 0);\r
6666   while (e->item) {\r
6667     if (strcmp(e->item, "-") == 0) {\r
6668       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6669     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6670       int flags = MF_STRING, j = 0;\r
6671       if (e->item[0] == '|') {\r
6672         flags |= MF_MENUBARBREAK;\r
6673         j++;\r
6674       }\r
6675       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6676       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6677     }\r
6678     e++;\r
6679     i++;\r
6680   } \r
6681   return hmenu;\r
6682 }\r
6683 \r
6684 WNDPROC consoleTextWindowProc;\r
6685 \r
6686 void\r
6687 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6688 {\r
6689   char buf[MSG_SIZ], name[MSG_SIZ];\r
6690   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6691   CHARRANGE sel;\r
6692 \r
6693   if (!getname) {\r
6694     SetWindowText(hInput, command);\r
6695     if (immediate) {\r
6696       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6697     } else {\r
6698       sel.cpMin = 999999;\r
6699       sel.cpMax = 999999;\r
6700       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6701       SetFocus(hInput);\r
6702     }\r
6703     return;\r
6704   }    \r
6705   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6706   if (sel.cpMin == sel.cpMax) {\r
6707     /* Expand to surrounding word */\r
6708     TEXTRANGE tr;\r
6709     do {\r
6710       tr.chrg.cpMax = sel.cpMin;\r
6711       tr.chrg.cpMin = --sel.cpMin;\r
6712       if (sel.cpMin < 0) break;\r
6713       tr.lpstrText = name;\r
6714       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6715     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6716     sel.cpMin++;\r
6717 \r
6718     do {\r
6719       tr.chrg.cpMin = sel.cpMax;\r
6720       tr.chrg.cpMax = ++sel.cpMax;\r
6721       tr.lpstrText = name;\r
6722       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6723     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6724     sel.cpMax--;\r
6725 \r
6726     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6727       MessageBeep(MB_ICONEXCLAMATION);\r
6728       return;\r
6729     }\r
6730     tr.chrg = sel;\r
6731     tr.lpstrText = name;\r
6732     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6733   } else {\r
6734     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6735       MessageBeep(MB_ICONEXCLAMATION);\r
6736       return;\r
6737     }\r
6738     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6739   }\r
6740   if (immediate) {\r
6741     sprintf(buf, "%s %s", command, name);\r
6742     SetWindowText(hInput, buf);\r
6743     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6744   } else {\r
6745     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6746     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
6747     SetWindowText(hInput, buf);\r
6748     sel.cpMin = 999999;\r
6749     sel.cpMax = 999999;\r
6750     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6751     SetFocus(hInput);\r
6752   }\r
6753 }\r
6754 \r
6755 LRESULT CALLBACK \r
6756 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6757 {\r
6758   HWND hInput;\r
6759   CHARRANGE sel;\r
6760 \r
6761   switch (message) {\r
6762   case WM_KEYDOWN:\r
6763     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6764     switch (wParam) {\r
6765     case VK_PRIOR:\r
6766       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6767       return 0;\r
6768     case VK_NEXT:\r
6769       sel.cpMin = 999999;\r
6770       sel.cpMax = 999999;\r
6771       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6772       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6773       return 0;\r
6774     }\r
6775     break;\r
6776   case WM_CHAR:\r
6777    if(wParam != '\022') {\r
6778     if (wParam == '\t') {\r
6779       if (GetKeyState(VK_SHIFT) < 0) {\r
6780         /* shifted */\r
6781         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6782         if (buttonDesc[0].hwnd) {\r
6783           SetFocus(buttonDesc[0].hwnd);\r
6784         } else {\r
6785           SetFocus(hwndMain);\r
6786         }\r
6787       } else {\r
6788         /* unshifted */\r
6789         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6790       }\r
6791     } else {\r
6792       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6793       JAWS_DELETE( SetFocus(hInput); )\r
6794       SendMessage(hInput, message, wParam, lParam);\r
6795     }\r
6796     return 0;\r
6797    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
6798   case WM_RBUTTONDOWN:\r
6799     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6800       /* Move selection here if it was empty */\r
6801       POINT pt;\r
6802       pt.x = LOWORD(lParam);\r
6803       pt.y = HIWORD(lParam);\r
6804       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6805       if (sel.cpMin == sel.cpMax) {\r
6806         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6807         sel.cpMax = sel.cpMin;\r
6808         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6809       }\r
6810       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6811 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6812       POINT pt;\r
6813       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6814       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6815       if (sel.cpMin == sel.cpMax) {\r
6816         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6817         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6818       }\r
6819       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6820         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6821       }\r
6822       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6823       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6824       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6825       MenuPopup(hwnd, pt, hmenu, -1);\r
6826 }\r
6827     }\r
6828     return 0;\r
6829   case WM_RBUTTONUP:\r
6830     if (GetKeyState(VK_SHIFT) & ~1) {\r
6831       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6832         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6833     }\r
6834     return 0;\r
6835   case WM_PASTE:\r
6836     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6837     SetFocus(hInput);\r
6838     return SendMessage(hInput, message, wParam, lParam);\r
6839   case WM_MBUTTONDOWN:\r
6840     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6841   case WM_COMMAND:\r
6842     switch (LOWORD(wParam)) {\r
6843     case IDM_QuickPaste:\r
6844       {\r
6845         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6846         if (sel.cpMin == sel.cpMax) {\r
6847           MessageBeep(MB_ICONEXCLAMATION);\r
6848           return 0;\r
6849         }\r
6850         SendMessage(hwnd, WM_COPY, 0, 0);\r
6851         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6852         SendMessage(hInput, WM_PASTE, 0, 0);\r
6853         SetFocus(hInput);\r
6854         return 0;\r
6855       }\r
6856     case IDM_Cut:\r
6857       SendMessage(hwnd, WM_CUT, 0, 0);\r
6858       return 0;\r
6859     case IDM_Paste:\r
6860       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6861       return 0;\r
6862     case IDM_Copy:\r
6863       SendMessage(hwnd, WM_COPY, 0, 0);\r
6864       return 0;\r
6865     default:\r
6866       {\r
6867         int i = LOWORD(wParam) - IDM_CommandX;\r
6868         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6869             icsTextMenuEntry[i].command != NULL) {\r
6870           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6871                    icsTextMenuEntry[i].getname,\r
6872                    icsTextMenuEntry[i].immediate);\r
6873           return 0;\r
6874         }\r
6875       }\r
6876       break;\r
6877     }\r
6878     break;\r
6879   }\r
6880   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6881 }\r
6882 \r
6883 WNDPROC consoleInputWindowProc;\r
6884 \r
6885 LRESULT CALLBACK\r
6886 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6887 {\r
6888   char buf[MSG_SIZ];\r
6889   char *p;\r
6890   static BOOL sendNextChar = FALSE;\r
6891   static BOOL quoteNextChar = FALSE;\r
6892   InputSource *is = consoleInputSource;\r
6893   CHARFORMAT cf;\r
6894   CHARRANGE sel;\r
6895 \r
6896   switch (message) {\r
6897   case WM_CHAR:\r
6898     if (!appData.localLineEditing || sendNextChar) {\r
6899       is->buf[0] = (CHAR) wParam;\r
6900       is->count = 1;\r
6901       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6902       sendNextChar = FALSE;\r
6903       return 0;\r
6904     }\r
6905     if (quoteNextChar) {\r
6906       buf[0] = (char) wParam;\r
6907       buf[1] = NULLCHAR;\r
6908       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
6909       quoteNextChar = FALSE;\r
6910       return 0;\r
6911     }\r
6912     switch (wParam) {\r
6913     case '\r':   /* Enter key */\r
6914       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
6915       if (consoleEcho) SaveInHistory(is->buf);\r
6916       is->buf[is->count++] = '\n';\r
6917       is->buf[is->count] = NULLCHAR;\r
6918       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6919       if (consoleEcho) {\r
6920         ConsoleOutput(is->buf, is->count, TRUE);\r
6921       } else if (appData.localLineEditing) {\r
6922         ConsoleOutput("\n", 1, TRUE);\r
6923       }\r
6924       /* fall thru */\r
6925     case '\033': /* Escape key */\r
6926       SetWindowText(hwnd, "");\r
6927       cf.cbSize = sizeof(CHARFORMAT);\r
6928       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
6929       if (consoleEcho) {\r
6930         cf.crTextColor = textAttribs[ColorNormal].color;\r
6931       } else {\r
6932         cf.crTextColor = COLOR_ECHOOFF;\r
6933       }\r
6934       cf.dwEffects = textAttribs[ColorNormal].effects;\r
6935       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
6936       return 0;\r
6937     case '\t':   /* Tab key */\r
6938       if (GetKeyState(VK_SHIFT) < 0) {\r
6939         /* shifted */\r
6940         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
6941       } else {\r
6942         /* unshifted */\r
6943         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6944         if (buttonDesc[0].hwnd) {\r
6945           SetFocus(buttonDesc[0].hwnd);\r
6946         } else {\r
6947           SetFocus(hwndMain);\r
6948         }\r
6949       }\r
6950       return 0;\r
6951     case '\023': /* Ctrl+S */\r
6952       sendNextChar = TRUE;\r
6953       return 0;\r
6954     case '\021': /* Ctrl+Q */\r
6955       quoteNextChar = TRUE;\r
6956       return 0;\r
6957     JAWS_REPLAY\r
6958     default:\r
6959       break;\r
6960     }\r
6961     break;\r
6962   case WM_KEYDOWN:\r
6963     switch (wParam) {\r
6964     case VK_UP:\r
6965       GetWindowText(hwnd, buf, MSG_SIZ);\r
6966       p = PrevInHistory(buf);\r
6967       if (p != NULL) {\r
6968         SetWindowText(hwnd, p);\r
6969         sel.cpMin = 999999;\r
6970         sel.cpMax = 999999;\r
6971         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6972         return 0;\r
6973       }\r
6974       break;\r
6975     case VK_DOWN:\r
6976       p = NextInHistory();\r
6977       if (p != NULL) {\r
6978         SetWindowText(hwnd, p);\r
6979         sel.cpMin = 999999;\r
6980         sel.cpMax = 999999;\r
6981         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6982         return 0;\r
6983       }\r
6984       break;\r
6985     case VK_HOME:\r
6986     case VK_END:\r
6987       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6988       /* fall thru */\r
6989     case VK_PRIOR:\r
6990     case VK_NEXT:\r
6991       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
6992       return 0;\r
6993     }\r
6994     break;\r
6995   case WM_MBUTTONDOWN:\r
6996     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6997       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6998     break;\r
6999   case WM_RBUTTONUP:\r
7000     if (GetKeyState(VK_SHIFT) & ~1) {\r
7001       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7002         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7003     } else {\r
7004       POINT pt;\r
7005       HMENU hmenu;\r
7006       hmenu = LoadMenu(hInst, "InputMenu");\r
7007       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7008       if (sel.cpMin == sel.cpMax) {\r
7009         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7010         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7011       }\r
7012       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7013         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7014       }\r
7015       pt.x = LOWORD(lParam);\r
7016       pt.y = HIWORD(lParam);\r
7017       MenuPopup(hwnd, pt, hmenu, -1);\r
7018     }\r
7019     return 0;\r
7020   case WM_COMMAND:\r
7021     switch (LOWORD(wParam)) { \r
7022     case IDM_Undo:\r
7023       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7024       return 0;\r
7025     case IDM_SelectAll:\r
7026       sel.cpMin = 0;\r
7027       sel.cpMax = -1; /*999999?*/\r
7028       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7029       return 0;\r
7030     case IDM_Cut:\r
7031       SendMessage(hwnd, WM_CUT, 0, 0);\r
7032       return 0;\r
7033     case IDM_Paste:\r
7034       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7035       return 0;\r
7036     case IDM_Copy:\r
7037       SendMessage(hwnd, WM_COPY, 0, 0);\r
7038       return 0;\r
7039     }\r
7040     break;\r
7041   }\r
7042   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7043 }\r
7044 \r
7045 #define CO_MAX  100000\r
7046 #define CO_TRIM   1000\r
7047 \r
7048 LRESULT CALLBACK\r
7049 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7050 {\r
7051   static SnapData sd;\r
7052   HWND hText, hInput;\r
7053   RECT rect;\r
7054   static int sizeX, sizeY;\r
7055   int newSizeX, newSizeY;\r
7056   MINMAXINFO *mmi;\r
7057   WORD wMask;\r
7058 \r
7059   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7060   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7061 \r
7062   switch (message) {\r
7063   case WM_NOTIFY:\r
7064     if (((NMHDR*)lParam)->code == EN_LINK)\r
7065     {\r
7066       ENLINK *pLink = (ENLINK*)lParam;\r
7067       if (pLink->msg == WM_LBUTTONUP)\r
7068       {\r
7069         TEXTRANGE tr;\r
7070 \r
7071         tr.chrg = pLink->chrg;\r
7072         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7073         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7074         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7075         free(tr.lpstrText);\r
7076       }\r
7077     }\r
7078     break;\r
7079   case WM_INITDIALOG: /* message: initialize dialog box */\r
7080     hwndConsole = hDlg;\r
7081     SetFocus(hInput);\r
7082     consoleTextWindowProc = (WNDPROC)\r
7083       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7084     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7085     consoleInputWindowProc = (WNDPROC)\r
7086       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7087     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7088     Colorize(ColorNormal, TRUE);\r
7089     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7090     ChangedConsoleFont();\r
7091     GetClientRect(hDlg, &rect);\r
7092     sizeX = rect.right;\r
7093     sizeY = rect.bottom;\r
7094     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7095         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7096       WINDOWPLACEMENT wp;\r
7097       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7098       wp.length = sizeof(WINDOWPLACEMENT);\r
7099       wp.flags = 0;\r
7100       wp.showCmd = SW_SHOW;\r
7101       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7102       wp.rcNormalPosition.left = wpConsole.x;\r
7103       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7104       wp.rcNormalPosition.top = wpConsole.y;\r
7105       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7106       SetWindowPlacement(hDlg, &wp);\r
7107     }\r
7108 \r
7109    // [HGM] Chessknight's change 2004-07-13\r
7110    else { /* Determine Defaults */\r
7111        WINDOWPLACEMENT wp;\r
7112        wpConsole.x = wpMain.width + 1;\r
7113        wpConsole.y = wpMain.y;\r
7114        wpConsole.width = screenWidth -  wpMain.width;\r
7115        wpConsole.height = wpMain.height;\r
7116        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7117        wp.length = sizeof(WINDOWPLACEMENT);\r
7118        wp.flags = 0;\r
7119        wp.showCmd = SW_SHOW;\r
7120        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7121        wp.rcNormalPosition.left = wpConsole.x;\r
7122        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7123        wp.rcNormalPosition.top = wpConsole.y;\r
7124        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7125        SetWindowPlacement(hDlg, &wp);\r
7126     }\r
7127 \r
7128    // Allow hText to highlight URLs and send notifications on them\r
7129    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7130    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7131    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7132    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
7133 \r
7134     return FALSE;\r
7135 \r
7136   case WM_SETFOCUS:\r
7137     SetFocus(hInput);\r
7138     return 0;\r
7139 \r
7140   case WM_CLOSE:\r
7141     ExitEvent(0);\r
7142     /* not reached */\r
7143     break;\r
7144 \r
7145   case WM_SIZE:\r
7146     if (IsIconic(hDlg)) break;\r
7147     newSizeX = LOWORD(lParam);\r
7148     newSizeY = HIWORD(lParam);\r
7149     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7150       RECT rectText, rectInput;\r
7151       POINT pt;\r
7152       int newTextHeight, newTextWidth;\r
7153       GetWindowRect(hText, &rectText);\r
7154       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7155       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7156       if (newTextHeight < 0) {\r
7157         newSizeY += -newTextHeight;\r
7158         newTextHeight = 0;\r
7159       }\r
7160       SetWindowPos(hText, NULL, 0, 0,\r
7161         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7162       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7163       pt.x = rectInput.left;\r
7164       pt.y = rectInput.top + newSizeY - sizeY;\r
7165       ScreenToClient(hDlg, &pt);\r
7166       SetWindowPos(hInput, NULL, \r
7167         pt.x, pt.y, /* needs client coords */   \r
7168         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7169         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7170     }\r
7171     sizeX = newSizeX;\r
7172     sizeY = newSizeY;\r
7173     break;\r
7174 \r
7175   case WM_GETMINMAXINFO:\r
7176     /* Prevent resizing window too small */\r
7177     mmi = (MINMAXINFO *) lParam;\r
7178     mmi->ptMinTrackSize.x = 100;\r
7179     mmi->ptMinTrackSize.y = 100;\r
7180     break;\r
7181 \r
7182   /* [AS] Snapping */\r
7183   case WM_ENTERSIZEMOVE:\r
7184     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7185 \r
7186   case WM_SIZING:\r
7187     return OnSizing( &sd, hDlg, wParam, lParam );\r
7188 \r
7189   case WM_MOVING:\r
7190     return OnMoving( &sd, hDlg, wParam, lParam );\r
7191 \r
7192   case WM_EXITSIZEMOVE:\r
7193         UpdateICSWidth(hText);\r
7194     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7195   }\r
7196 \r
7197   return DefWindowProc(hDlg, message, wParam, lParam);\r
7198 }\r
7199 \r
7200 \r
7201 VOID\r
7202 ConsoleCreate()\r
7203 {\r
7204   HWND hCons;\r
7205   if (hwndConsole) return;\r
7206   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7207   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7208 }\r
7209 \r
7210 \r
7211 VOID\r
7212 ConsoleOutput(char* data, int length, int forceVisible)\r
7213 {\r
7214   HWND hText;\r
7215   int trim, exlen;\r
7216   char *p, *q;\r
7217   char buf[CO_MAX+1];\r
7218   POINT pEnd;\r
7219   RECT rect;\r
7220   static int delayLF = 0;\r
7221   CHARRANGE savesel, sel;\r
7222 \r
7223   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7224   p = data;\r
7225   q = buf;\r
7226   if (delayLF) {\r
7227     *q++ = '\r';\r
7228     *q++ = '\n';\r
7229     delayLF = 0;\r
7230   }\r
7231   while (length--) {\r
7232     if (*p == '\n') {\r
7233       if (*++p) {\r
7234         *q++ = '\r';\r
7235         *q++ = '\n';\r
7236       } else {\r
7237         delayLF = 1;\r
7238       }\r
7239     } else if (*p == '\007') {\r
7240        MyPlaySound(&sounds[(int)SoundBell]);\r
7241        p++;\r
7242     } else {\r
7243       *q++ = *p++;\r
7244     }\r
7245   }\r
7246   *q = NULLCHAR;\r
7247   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7248   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7249   /* Save current selection */\r
7250   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7251   exlen = GetWindowTextLength(hText);\r
7252   /* Find out whether current end of text is visible */\r
7253   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7254   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7255   /* Trim existing text if it's too long */\r
7256   if (exlen + (q - buf) > CO_MAX) {\r
7257     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7258     sel.cpMin = 0;\r
7259     sel.cpMax = trim;\r
7260     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7261     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7262     exlen -= trim;\r
7263     savesel.cpMin -= trim;\r
7264     savesel.cpMax -= trim;\r
7265     if (exlen < 0) exlen = 0;\r
7266     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7267     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7268   }\r
7269   /* Append the new text */\r
7270   sel.cpMin = exlen;\r
7271   sel.cpMax = exlen;\r
7272   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7273   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7274   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7275   if (forceVisible || exlen == 0 ||\r
7276       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7277        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7278     /* Scroll to make new end of text visible if old end of text\r
7279        was visible or new text is an echo of user typein */\r
7280     sel.cpMin = 9999999;\r
7281     sel.cpMax = 9999999;\r
7282     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7283     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7284     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7285     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7286   }\r
7287   if (savesel.cpMax == exlen || forceVisible) {\r
7288     /* Move insert point to new end of text if it was at the old\r
7289        end of text or if the new text is an echo of user typein */\r
7290     sel.cpMin = 9999999;\r
7291     sel.cpMax = 9999999;\r
7292     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7293   } else {\r
7294     /* Restore previous selection */\r
7295     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7296   }\r
7297   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7298 }\r
7299 \r
7300 /*---------*/\r
7301 \r
7302 \r
7303 void\r
7304 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7305 {\r
7306   char buf[100];\r
7307   char *str;\r
7308   COLORREF oldFg, oldBg;\r
7309   HFONT oldFont;\r
7310   RECT rect;\r
7311 \r
7312   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
7313 \r
7314   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7315   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7316   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7317 \r
7318   rect.left = x;\r
7319   rect.right = x + squareSize;\r
7320   rect.top  = y;\r
7321   rect.bottom = y + squareSize;\r
7322   str = buf;\r
7323 \r
7324   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7325                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7326              y, ETO_CLIPPED|ETO_OPAQUE,\r
7327              &rect, str, strlen(str), NULL);\r
7328 \r
7329   (void) SetTextColor(hdc, oldFg);\r
7330   (void) SetBkColor(hdc, oldBg);\r
7331   (void) SelectObject(hdc, oldFont);\r
7332 }\r
7333 \r
7334 void\r
7335 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7336               RECT *rect, char *color, char *flagFell)\r
7337 {\r
7338   char buf[100];\r
7339   char *str;\r
7340   COLORREF oldFg, oldBg;\r
7341   HFONT oldFont;\r
7342 \r
7343   if (appData.clockMode) {\r
7344     if (tinyLayout)\r
7345       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7346     else\r
7347       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7348     str = buf;\r
7349   } else {\r
7350     str = color;\r
7351   }\r
7352 \r
7353   if (highlight) {\r
7354     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7355     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7356   } else {\r
7357     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7358     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7359   }\r
7360   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7361 \r
7362   JAWS_SILENCE\r
7363 \r
7364   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7365              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7366              rect, str, strlen(str), NULL);\r
7367   if(logoHeight > 0 && appData.clockMode) {\r
7368       RECT r;\r
7369       sprintf(buf, "%s %s", buf+7, flagFell);\r
7370       r.top = rect->top + logoHeight/2;\r
7371       r.left = rect->left;\r
7372       r.right = rect->right;\r
7373       r.bottom = rect->bottom;\r
7374       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7375                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7376                  &r, str, strlen(str), NULL);\r
7377   }\r
7378   (void) SetTextColor(hdc, oldFg);\r
7379   (void) SetBkColor(hdc, oldBg);\r
7380   (void) SelectObject(hdc, oldFont);\r
7381 }\r
7382 \r
7383 \r
7384 int\r
7385 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7386            OVERLAPPED *ovl)\r
7387 {\r
7388   int ok, err;\r
7389 \r
7390   /* [AS]  */\r
7391   if( count <= 0 ) {\r
7392     if (appData.debugMode) {\r
7393       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7394     }\r
7395 \r
7396     return ERROR_INVALID_USER_BUFFER;\r
7397   }\r
7398 \r
7399   ResetEvent(ovl->hEvent);\r
7400   ovl->Offset = ovl->OffsetHigh = 0;\r
7401   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7402   if (ok) {\r
7403     err = NO_ERROR;\r
7404   } else {\r
7405     err = GetLastError();\r
7406     if (err == ERROR_IO_PENDING) {\r
7407       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7408       if (ok)\r
7409         err = NO_ERROR;\r
7410       else\r
7411         err = GetLastError();\r
7412     }\r
7413   }\r
7414   return err;\r
7415 }\r
7416 \r
7417 int\r
7418 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7419             OVERLAPPED *ovl)\r
7420 {\r
7421   int ok, err;\r
7422 \r
7423   ResetEvent(ovl->hEvent);\r
7424   ovl->Offset = ovl->OffsetHigh = 0;\r
7425   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7426   if (ok) {\r
7427     err = NO_ERROR;\r
7428   } else {\r
7429     err = GetLastError();\r
7430     if (err == ERROR_IO_PENDING) {\r
7431       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7432       if (ok)\r
7433         err = NO_ERROR;\r
7434       else\r
7435         err = GetLastError();\r
7436     }\r
7437   }\r
7438   return err;\r
7439 }\r
7440 \r
7441 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7442 void CheckForInputBufferFull( InputSource * is )\r
7443 {\r
7444     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7445         /* Look for end of line */\r
7446         char * p = is->buf;\r
7447         \r
7448         while( p < is->next && *p != '\n' ) {\r
7449             p++;\r
7450         }\r
7451 \r
7452         if( p >= is->next ) {\r
7453             if (appData.debugMode) {\r
7454                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7455             }\r
7456 \r
7457             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7458             is->count = (DWORD) -1;\r
7459             is->next = is->buf;\r
7460         }\r
7461     }\r
7462 }\r
7463 \r
7464 DWORD\r
7465 InputThread(LPVOID arg)\r
7466 {\r
7467   InputSource *is;\r
7468   OVERLAPPED ovl;\r
7469 \r
7470   is = (InputSource *) arg;\r
7471   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7472   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7473   while (is->hThread != NULL) {\r
7474     is->error = DoReadFile(is->hFile, is->next,\r
7475                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7476                            &is->count, &ovl);\r
7477     if (is->error == NO_ERROR) {\r
7478       is->next += is->count;\r
7479     } else {\r
7480       if (is->error == ERROR_BROKEN_PIPE) {\r
7481         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7482         is->count = 0;\r
7483       } else {\r
7484         is->count = (DWORD) -1;\r
7485         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7486         break; \r
7487       }\r
7488     }\r
7489 \r
7490     CheckForInputBufferFull( is );\r
7491 \r
7492     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7493 \r
7494     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7495 \r
7496     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7497   }\r
7498 \r
7499   CloseHandle(ovl.hEvent);\r
7500   CloseHandle(is->hFile);\r
7501 \r
7502   if (appData.debugMode) {\r
7503     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7504   }\r
7505 \r
7506   return 0;\r
7507 }\r
7508 \r
7509 \r
7510 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7511 DWORD\r
7512 NonOvlInputThread(LPVOID arg)\r
7513 {\r
7514   InputSource *is;\r
7515   char *p, *q;\r
7516   int i;\r
7517   char prev;\r
7518 \r
7519   is = (InputSource *) arg;\r
7520   while (is->hThread != NULL) {\r
7521     is->error = ReadFile(is->hFile, is->next,\r
7522                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7523                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7524     if (is->error == NO_ERROR) {\r
7525       /* Change CRLF to LF */\r
7526       if (is->next > is->buf) {\r
7527         p = is->next - 1;\r
7528         i = is->count + 1;\r
7529       } else {\r
7530         p = is->next;\r
7531         i = is->count;\r
7532       }\r
7533       q = p;\r
7534       prev = NULLCHAR;\r
7535       while (i > 0) {\r
7536         if (prev == '\r' && *p == '\n') {\r
7537           *(q-1) = '\n';\r
7538           is->count--;\r
7539         } else { \r
7540           *q++ = *p;\r
7541         }\r
7542         prev = *p++;\r
7543         i--;\r
7544       }\r
7545       *q = NULLCHAR;\r
7546       is->next = q;\r
7547     } else {\r
7548       if (is->error == ERROR_BROKEN_PIPE) {\r
7549         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7550         is->count = 0; \r
7551       } else {\r
7552         is->count = (DWORD) -1;\r
7553       }\r
7554     }\r
7555 \r
7556     CheckForInputBufferFull( is );\r
7557 \r
7558     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7559 \r
7560     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7561 \r
7562     if (is->count < 0) break;  /* Quit on error */\r
7563   }\r
7564   CloseHandle(is->hFile);\r
7565   return 0;\r
7566 }\r
7567 \r
7568 DWORD\r
7569 SocketInputThread(LPVOID arg)\r
7570 {\r
7571   InputSource *is;\r
7572 \r
7573   is = (InputSource *) arg;\r
7574   while (is->hThread != NULL) {\r
7575     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7576     if ((int)is->count == SOCKET_ERROR) {\r
7577       is->count = (DWORD) -1;\r
7578       is->error = WSAGetLastError();\r
7579     } else {\r
7580       is->error = NO_ERROR;\r
7581       is->next += is->count;\r
7582       if (is->count == 0 && is->second == is) {\r
7583         /* End of file on stderr; quit with no message */\r
7584         break;\r
7585       }\r
7586     }\r
7587     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7588 \r
7589     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7590 \r
7591     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7592   }\r
7593   return 0;\r
7594 }\r
7595 \r
7596 VOID\r
7597 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7598 {\r
7599   InputSource *is;\r
7600 \r
7601   is = (InputSource *) lParam;\r
7602   if (is->lineByLine) {\r
7603     /* Feed in lines one by one */\r
7604     char *p = is->buf;\r
7605     char *q = p;\r
7606     while (q < is->next) {\r
7607       if (*q++ == '\n') {\r
7608         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7609         p = q;\r
7610       }\r
7611     }\r
7612     \r
7613     /* Move any partial line to the start of the buffer */\r
7614     q = is->buf;\r
7615     while (p < is->next) {\r
7616       *q++ = *p++;\r
7617     }\r
7618     is->next = q;\r
7619 \r
7620     if (is->error != NO_ERROR || is->count == 0) {\r
7621       /* Notify backend of the error.  Note: If there was a partial\r
7622          line at the end, it is not flushed through. */\r
7623       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7624     }\r
7625   } else {\r
7626     /* Feed in the whole chunk of input at once */\r
7627     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7628     is->next = is->buf;\r
7629   }\r
7630 }\r
7631 \r
7632 /*---------------------------------------------------------------------------*\\r
7633  *\r
7634  *  Menu enables. Used when setting various modes.\r
7635  *\r
7636 \*---------------------------------------------------------------------------*/\r
7637 \r
7638 typedef struct {\r
7639   int item;\r
7640   int flags;\r
7641 } Enables;\r
7642 \r
7643 VOID\r
7644 GreyRevert(Boolean grey)\r
7645 { // [HGM] vari: for retracting variations in local mode\r
7646   HMENU hmenu = GetMenu(hwndMain);\r
7647   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7648   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7649 }\r
7650 \r
7651 VOID\r
7652 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7653 {\r
7654   while (enab->item > 0) {\r
7655     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7656     enab++;\r
7657   }\r
7658 }\r
7659 \r
7660 Enables gnuEnables[] = {\r
7661   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7662   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7663   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7664   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7665   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7666   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7667   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7668   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7669   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7670   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7671   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7672   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7673   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7674   { -1, -1 }\r
7675 };\r
7676 \r
7677 Enables icsEnables[] = {\r
7678   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7679   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7680   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7681   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7682   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7683   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7684   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7685   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7686   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7687   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7688   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7689   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7690   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7691   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7692   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7693   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7694   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7695   { -1, -1 }\r
7696 };\r
7697 \r
7698 #if ZIPPY\r
7699 Enables zippyEnables[] = {\r
7700   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7701   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7702   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7703   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7704   { -1, -1 }\r
7705 };\r
7706 #endif\r
7707 \r
7708 Enables ncpEnables[] = {\r
7709   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7710   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7711   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7712   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7713   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7714   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7715   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7716   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7717   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7718   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7719   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7720   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7721   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7722   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7723   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7724   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7725   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7726   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7727   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7728   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7729   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7730   { -1, -1 }\r
7731 };\r
7732 \r
7733 Enables trainingOnEnables[] = {\r
7734   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7735   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7736   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7737   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7738   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7739   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7740   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7741   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7742   { -1, -1 }\r
7743 };\r
7744 \r
7745 Enables trainingOffEnables[] = {\r
7746   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7747   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7748   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7749   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7750   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7751   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7752   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7753   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7754   { -1, -1 }\r
7755 };\r
7756 \r
7757 /* These modify either ncpEnables or gnuEnables */\r
7758 Enables cmailEnables[] = {\r
7759   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7760   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7761   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7762   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7763   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7764   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7765   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7766   { -1, -1 }\r
7767 };\r
7768 \r
7769 Enables machineThinkingEnables[] = {\r
7770   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7771   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7772   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7773   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7774   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7775   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7776   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7777   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7778   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7779   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7780   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7781   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7782   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7783   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7784   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7785   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7786   { -1, -1 }\r
7787 };\r
7788 \r
7789 Enables userThinkingEnables[] = {\r
7790   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7791   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7792   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7793   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7794   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7795   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7796   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7797   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7798   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7799   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7800   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7801   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7802   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7803   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7804   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7805   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7806   { -1, -1 }\r
7807 };\r
7808 \r
7809 /*---------------------------------------------------------------------------*\\r
7810  *\r
7811  *  Front-end interface functions exported by XBoard.\r
7812  *  Functions appear in same order as prototypes in frontend.h.\r
7813  * \r
7814 \*---------------------------------------------------------------------------*/\r
7815 VOID\r
7816 ModeHighlight()\r
7817 {\r
7818   static UINT prevChecked = 0;\r
7819   static int prevPausing = 0;\r
7820   UINT nowChecked;\r
7821 \r
7822   if (pausing != prevPausing) {\r
7823     prevPausing = pausing;\r
7824     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7825                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7826     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7827   }\r
7828 \r
7829   switch (gameMode) {\r
7830   case BeginningOfGame:\r
7831     if (appData.icsActive)\r
7832       nowChecked = IDM_IcsClient;\r
7833     else if (appData.noChessProgram)\r
7834       nowChecked = IDM_EditGame;\r
7835     else\r
7836       nowChecked = IDM_MachineBlack;\r
7837     break;\r
7838   case MachinePlaysBlack:\r
7839     nowChecked = IDM_MachineBlack;\r
7840     break;\r
7841   case MachinePlaysWhite:\r
7842     nowChecked = IDM_MachineWhite;\r
7843     break;\r
7844   case TwoMachinesPlay:\r
7845     nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match\r
7846     break;\r
7847   case AnalyzeMode:\r
7848     nowChecked = IDM_AnalysisMode;\r
7849     break;\r
7850   case AnalyzeFile:\r
7851     nowChecked = IDM_AnalyzeFile;\r
7852     break;\r
7853   case EditGame:\r
7854     nowChecked = IDM_EditGame;\r
7855     break;\r
7856   case PlayFromGameFile:\r
7857     nowChecked = IDM_LoadGame;\r
7858     break;\r
7859   case EditPosition:\r
7860     nowChecked = IDM_EditPosition;\r
7861     break;\r
7862   case Training:\r
7863     nowChecked = IDM_Training;\r
7864     break;\r
7865   case IcsPlayingWhite:\r
7866   case IcsPlayingBlack:\r
7867   case IcsObserving:\r
7868   case IcsIdle:\r
7869     nowChecked = IDM_IcsClient;\r
7870     break;\r
7871   default:\r
7872   case EndOfGame:\r
7873     nowChecked = 0;\r
7874     break;\r
7875   }\r
7876   if (prevChecked != 0)\r
7877     (void) CheckMenuItem(GetMenu(hwndMain),\r
7878                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7879   if (nowChecked != 0)\r
7880     (void) CheckMenuItem(GetMenu(hwndMain),\r
7881                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
7882 \r
7883   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7884     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7885                           MF_BYCOMMAND|MF_ENABLED);\r
7886   } else {\r
7887     (void) EnableMenuItem(GetMenu(hwndMain), \r
7888                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7889   }\r
7890 \r
7891   prevChecked = nowChecked;\r
7892 \r
7893   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
7894   if (appData.icsActive) {\r
7895        if (appData.icsEngineAnalyze) {\r
7896                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7897                        MF_BYCOMMAND|MF_CHECKED);\r
7898        } else {\r
7899                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7900                        MF_BYCOMMAND|MF_UNCHECKED);\r
7901        }\r
7902   }\r
7903 }\r
7904 \r
7905 VOID\r
7906 SetICSMode()\r
7907 {\r
7908   HMENU hmenu = GetMenu(hwndMain);\r
7909   SetMenuEnables(hmenu, icsEnables);\r
7910   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
7911     MF_BYPOSITION|MF_ENABLED);\r
7912 #if ZIPPY\r
7913   if (appData.zippyPlay) {\r
7914     SetMenuEnables(hmenu, zippyEnables);\r
7915     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
7916          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7917           MF_BYCOMMAND|MF_ENABLED);\r
7918   }\r
7919 #endif\r
7920 }\r
7921 \r
7922 VOID\r
7923 SetGNUMode()\r
7924 {\r
7925   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
7926 }\r
7927 \r
7928 VOID\r
7929 SetNCPMode()\r
7930 {\r
7931   HMENU hmenu = GetMenu(hwndMain);\r
7932   SetMenuEnables(hmenu, ncpEnables);\r
7933   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
7934     MF_BYPOSITION|MF_GRAYED);\r
7935     DrawMenuBar(hwndMain);\r
7936 }\r
7937 \r
7938 VOID\r
7939 SetCmailMode()\r
7940 {\r
7941   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
7942 }\r
7943 \r
7944 VOID \r
7945 SetTrainingModeOn()\r
7946 {\r
7947   int i;\r
7948   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
7949   for (i = 0; i < N_BUTTONS; i++) {\r
7950     if (buttonDesc[i].hwnd != NULL)\r
7951       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
7952   }\r
7953   CommentPopDown();\r
7954 }\r
7955 \r
7956 VOID SetTrainingModeOff()\r
7957 {\r
7958   int i;\r
7959   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
7960   for (i = 0; i < N_BUTTONS; i++) {\r
7961     if (buttonDesc[i].hwnd != NULL)\r
7962       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
7963   }\r
7964 }\r
7965 \r
7966 \r
7967 VOID\r
7968 SetUserThinkingEnables()\r
7969 {\r
7970   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
7971 }\r
7972 \r
7973 VOID\r
7974 SetMachineThinkingEnables()\r
7975 {\r
7976   HMENU hMenu = GetMenu(hwndMain);\r
7977   int flags = MF_BYCOMMAND|MF_ENABLED;\r
7978 \r
7979   SetMenuEnables(hMenu, machineThinkingEnables);\r
7980 \r
7981   if (gameMode == MachinePlaysBlack) {\r
7982     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
7983   } else if (gameMode == MachinePlaysWhite) {\r
7984     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
7985   } else if (gameMode == TwoMachinesPlay) {\r
7986     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
7987   }\r
7988 }\r
7989 \r
7990 \r
7991 VOID\r
7992 DisplayTitle(char *str)\r
7993 {\r
7994   char title[MSG_SIZ], *host;\r
7995   if (str[0] != NULLCHAR) {\r
7996     strcpy(title, str);\r
7997   } else if (appData.icsActive) {\r
7998     if (appData.icsCommPort[0] != NULLCHAR)\r
7999       host = "ICS";\r
8000     else \r
8001       host = appData.icsHost;\r
8002     sprintf(title, "%s: %s", szTitle, host);\r
8003   } else if (appData.noChessProgram) {\r
8004     strcpy(title, szTitle);\r
8005   } else {\r
8006     strcpy(title, szTitle);\r
8007     strcat(title, ": ");\r
8008     strcat(title, first.tidy);\r
8009   }\r
8010   SetWindowText(hwndMain, title);\r
8011 }\r
8012 \r
8013 \r
8014 VOID\r
8015 DisplayMessage(char *str1, char *str2)\r
8016 {\r
8017   HDC hdc;\r
8018   HFONT oldFont;\r
8019   int remain = MESSAGE_TEXT_MAX - 1;\r
8020   int len;\r
8021 \r
8022   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8023   messageText[0] = NULLCHAR;\r
8024   if (*str1) {\r
8025     len = strlen(str1);\r
8026     if (len > remain) len = remain;\r
8027     strncpy(messageText, str1, len);\r
8028     messageText[len] = NULLCHAR;\r
8029     remain -= len;\r
8030   }\r
8031   if (*str2 && remain >= 2) {\r
8032     if (*str1) {\r
8033       strcat(messageText, "  ");\r
8034       remain -= 2;\r
8035     }\r
8036     len = strlen(str2);\r
8037     if (len > remain) len = remain;\r
8038     strncat(messageText, str2, len);\r
8039   }\r
8040   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8041 \r
8042   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8043 \r
8044   SAYMACHINEMOVE();\r
8045 \r
8046   hdc = GetDC(hwndMain);\r
8047   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8048   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8049              &messageRect, messageText, strlen(messageText), NULL);\r
8050   (void) SelectObject(hdc, oldFont);\r
8051   (void) ReleaseDC(hwndMain, hdc);\r
8052 }\r
8053 \r
8054 VOID\r
8055 DisplayError(char *str, int error)\r
8056 {\r
8057   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8058   int len;\r
8059 \r
8060   if (error == 0) {\r
8061     strcpy(buf, str);\r
8062   } else {\r
8063     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8064                         NULL, error, LANG_NEUTRAL,\r
8065                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8066     if (len > 0) {\r
8067       sprintf(buf, "%s:\n%s", str, buf2);\r
8068     } else {\r
8069       ErrorMap *em = errmap;\r
8070       while (em->err != 0 && em->err != error) em++;\r
8071       if (em->err != 0) {\r
8072         sprintf(buf, "%s:\n%s", str, em->msg);\r
8073       } else {\r
8074         sprintf(buf, "%s:\nError code %d", str, error);\r
8075       }\r
8076     }\r
8077   }\r
8078   \r
8079   ErrorPopUp(_("Error"), buf);\r
8080 }\r
8081 \r
8082 \r
8083 VOID\r
8084 DisplayMoveError(char *str)\r
8085 {\r
8086   fromX = fromY = -1;\r
8087   ClearHighlights();\r
8088   DrawPosition(FALSE, NULL);\r
8089   if (appData.popupMoveErrors) {\r
8090     ErrorPopUp(_("Error"), str);\r
8091   } else {\r
8092     DisplayMessage(str, "");\r
8093     moveErrorMessageUp = TRUE;\r
8094   }\r
8095 }\r
8096 \r
8097 VOID\r
8098 DisplayFatalError(char *str, int error, int exitStatus)\r
8099 {\r
8100   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8101   int len;\r
8102   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8103 \r
8104   if (error != 0) {\r
8105     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8106                         NULL, error, LANG_NEUTRAL,\r
8107                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8108     if (len > 0) {\r
8109       sprintf(buf, "%s:\n%s", str, buf2);\r
8110     } else {\r
8111       ErrorMap *em = errmap;\r
8112       while (em->err != 0 && em->err != error) em++;\r
8113       if (em->err != 0) {\r
8114         sprintf(buf, "%s:\n%s", str, em->msg);\r
8115       } else {\r
8116         sprintf(buf, "%s:\nError code %d", str, error);\r
8117       }\r
8118     }\r
8119     str = buf;\r
8120   }\r
8121   if (appData.debugMode) {\r
8122     fprintf(debugFP, "%s: %s\n", label, str);\r
8123   }\r
8124   if (appData.popupExitMessage) {\r
8125     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8126                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8127   }\r
8128   ExitEvent(exitStatus);\r
8129 }\r
8130 \r
8131 \r
8132 VOID\r
8133 DisplayInformation(char *str)\r
8134 {\r
8135   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8136 }\r
8137 \r
8138 \r
8139 VOID\r
8140 DisplayNote(char *str)\r
8141 {\r
8142   ErrorPopUp(_("Note"), str);\r
8143 }\r
8144 \r
8145 \r
8146 typedef struct {\r
8147   char *title, *question, *replyPrefix;\r
8148   ProcRef pr;\r
8149 } QuestionParams;\r
8150 \r
8151 LRESULT CALLBACK\r
8152 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8153 {\r
8154   static QuestionParams *qp;\r
8155   char reply[MSG_SIZ];\r
8156   int len, err;\r
8157 \r
8158   switch (message) {\r
8159   case WM_INITDIALOG:\r
8160     qp = (QuestionParams *) lParam;\r
8161     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8162     Translate(hDlg, DLG_Question);\r
8163     SetWindowText(hDlg, qp->title);\r
8164     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8165     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8166     return FALSE;\r
8167 \r
8168   case WM_COMMAND:\r
8169     switch (LOWORD(wParam)) {\r
8170     case IDOK:\r
8171       strcpy(reply, qp->replyPrefix);\r
8172       if (*reply) strcat(reply, " ");\r
8173       len = strlen(reply);\r
8174       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8175       strcat(reply, "\n");\r
8176       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8177       EndDialog(hDlg, TRUE);\r
8178       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8179       return TRUE;\r
8180     case IDCANCEL:\r
8181       EndDialog(hDlg, FALSE);\r
8182       return TRUE;\r
8183     default:\r
8184       break;\r
8185     }\r
8186     break;\r
8187   }\r
8188   return FALSE;\r
8189 }\r
8190 \r
8191 VOID\r
8192 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8193 {\r
8194     QuestionParams qp;\r
8195     FARPROC lpProc;\r
8196     \r
8197     qp.title = title;\r
8198     qp.question = question;\r
8199     qp.replyPrefix = replyPrefix;\r
8200     qp.pr = pr;\r
8201     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8202     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8203       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8204     FreeProcInstance(lpProc);\r
8205 }\r
8206 \r
8207 /* [AS] Pick FRC position */\r
8208 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8209 {\r
8210     static int * lpIndexFRC;\r
8211     BOOL index_is_ok;\r
8212     char buf[16];\r
8213 \r
8214     switch( message )\r
8215     {\r
8216     case WM_INITDIALOG:\r
8217         lpIndexFRC = (int *) lParam;\r
8218 \r
8219         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8220         Translate(hDlg, DLG_NewGameFRC);\r
8221 \r
8222         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8223         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8224         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8225         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8226 \r
8227         break;\r
8228 \r
8229     case WM_COMMAND:\r
8230         switch( LOWORD(wParam) ) {\r
8231         case IDOK:\r
8232             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8233             EndDialog( hDlg, 0 );\r
8234             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8235             return TRUE;\r
8236         case IDCANCEL:\r
8237             EndDialog( hDlg, 1 );   \r
8238             return TRUE;\r
8239         case IDC_NFG_Edit:\r
8240             if( HIWORD(wParam) == EN_CHANGE ) {\r
8241                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8242 \r
8243                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8244             }\r
8245             return TRUE;\r
8246         case IDC_NFG_Random:\r
8247             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8248             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8249             return TRUE;\r
8250         }\r
8251 \r
8252         break;\r
8253     }\r
8254 \r
8255     return FALSE;\r
8256 }\r
8257 \r
8258 int NewGameFRC()\r
8259 {\r
8260     int result;\r
8261     int index = appData.defaultFrcPosition;\r
8262     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8263 \r
8264     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8265 \r
8266     if( result == 0 ) {\r
8267         appData.defaultFrcPosition = index;\r
8268     }\r
8269 \r
8270     return result;\r
8271 }\r
8272 \r
8273 /* [AS] Game list options. Refactored by HGM */\r
8274 \r
8275 HWND gameListOptionsDialog;\r
8276 \r
8277 // low-level front-end: clear text edit / list widget\r
8278 void\r
8279 GLT_ClearList()\r
8280 {\r
8281     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8282 }\r
8283 \r
8284 // low-level front-end: clear text edit / list widget\r
8285 void\r
8286 GLT_DeSelectList()\r
8287 {\r
8288     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8289 }\r
8290 \r
8291 // low-level front-end: append line to text edit / list widget\r
8292 void\r
8293 GLT_AddToList( char *name )\r
8294 {\r
8295     if( name != 0 ) {\r
8296             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8297     }\r
8298 }\r
8299 \r
8300 // low-level front-end: get line from text edit / list widget\r
8301 Boolean\r
8302 GLT_GetFromList( int index, char *name )\r
8303 {\r
8304     if( name != 0 ) {\r
8305             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8306                 return TRUE;\r
8307     }\r
8308     return FALSE;\r
8309 }\r
8310 \r
8311 void GLT_MoveSelection( HWND hDlg, int delta )\r
8312 {\r
8313     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8314     int idx2 = idx1 + delta;\r
8315     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8316 \r
8317     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8318         char buf[128];\r
8319 \r
8320         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8321         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8322         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8323         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8324     }\r
8325 }\r
8326 \r
8327 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8328 {\r
8329     switch( message )\r
8330     {\r
8331     case WM_INITDIALOG:\r
8332         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8333         \r
8334         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8335         Translate(hDlg, DLG_GameListOptions);\r
8336 \r
8337         /* Initialize list */\r
8338         GLT_TagsToList( lpUserGLT );\r
8339 \r
8340         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8341 \r
8342         break;\r
8343 \r
8344     case WM_COMMAND:\r
8345         switch( LOWORD(wParam) ) {\r
8346         case IDOK:\r
8347             GLT_ParseList();\r
8348             EndDialog( hDlg, 0 );\r
8349             return TRUE;\r
8350         case IDCANCEL:\r
8351             EndDialog( hDlg, 1 );\r
8352             return TRUE;\r
8353 \r
8354         case IDC_GLT_Default:\r
8355             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8356             return TRUE;\r
8357 \r
8358         case IDC_GLT_Restore:\r
8359             GLT_TagsToList( appData.gameListTags );\r
8360             return TRUE;\r
8361 \r
8362         case IDC_GLT_Up:\r
8363             GLT_MoveSelection( hDlg, -1 );\r
8364             return TRUE;\r
8365 \r
8366         case IDC_GLT_Down:\r
8367             GLT_MoveSelection( hDlg, +1 );\r
8368             return TRUE;\r
8369         }\r
8370 \r
8371         break;\r
8372     }\r
8373 \r
8374     return FALSE;\r
8375 }\r
8376 \r
8377 int GameListOptions()\r
8378 {\r
8379     int result;\r
8380     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8381 \r
8382     strcpy( lpUserGLT, appData.gameListTags );\r
8383 \r
8384     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8385 \r
8386     if( result == 0 ) {\r
8387         /* [AS] Memory leak here! */\r
8388         appData.gameListTags = strdup( lpUserGLT ); \r
8389     }\r
8390 \r
8391     return result;\r
8392 }\r
8393 \r
8394 VOID\r
8395 DisplayIcsInteractionTitle(char *str)\r
8396 {\r
8397   char consoleTitle[MSG_SIZ];\r
8398 \r
8399   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
8400   SetWindowText(hwndConsole, consoleTitle);\r
8401 }\r
8402 \r
8403 void\r
8404 DrawPosition(int fullRedraw, Board board)\r
8405 {\r
8406   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8407 }\r
8408 \r
8409 void NotifyFrontendLogin()\r
8410 {\r
8411         if (hwndConsole)\r
8412                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8413 }\r
8414 \r
8415 VOID\r
8416 ResetFrontEnd()\r
8417 {\r
8418   fromX = fromY = -1;\r
8419   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8420     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8421     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8422     dragInfo.lastpos = dragInfo.pos;\r
8423     dragInfo.start.x = dragInfo.start.y = -1;\r
8424     dragInfo.from = dragInfo.start;\r
8425     ReleaseCapture();\r
8426     DrawPosition(TRUE, NULL);\r
8427   }\r
8428   TagsPopDown();\r
8429 }\r
8430 \r
8431 \r
8432 VOID\r
8433 CommentPopUp(char *title, char *str)\r
8434 {\r
8435   HWND hwnd = GetActiveWindow();\r
8436   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8437   SAY(str);\r
8438   SetActiveWindow(hwnd);\r
8439 }\r
8440 \r
8441 VOID\r
8442 CommentPopDown(void)\r
8443 {\r
8444   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
8445   if (commentDialog) {\r
8446     ShowWindow(commentDialog, SW_HIDE);\r
8447   }\r
8448   commentUp = FALSE;\r
8449 }\r
8450 \r
8451 VOID\r
8452 EditCommentPopUp(int index, char *title, char *str)\r
8453 {\r
8454   EitherCommentPopUp(index, title, str, TRUE);\r
8455 }\r
8456 \r
8457 \r
8458 VOID\r
8459 RingBell()\r
8460 {\r
8461   MyPlaySound(&sounds[(int)SoundMove]);\r
8462 }\r
8463 \r
8464 VOID PlayIcsWinSound()\r
8465 {\r
8466   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8467 }\r
8468 \r
8469 VOID PlayIcsLossSound()\r
8470 {\r
8471   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8472 }\r
8473 \r
8474 VOID PlayIcsDrawSound()\r
8475 {\r
8476   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8477 }\r
8478 \r
8479 VOID PlayIcsUnfinishedSound()\r
8480 {\r
8481   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8482 }\r
8483 \r
8484 VOID\r
8485 PlayAlarmSound()\r
8486 {\r
8487   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8488 }\r
8489 \r
8490 \r
8491 VOID\r
8492 EchoOn()\r
8493 {\r
8494   HWND hInput;\r
8495   consoleEcho = TRUE;\r
8496   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8497   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8498   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8499 }\r
8500 \r
8501 \r
8502 VOID\r
8503 EchoOff()\r
8504 {\r
8505   CHARFORMAT cf;\r
8506   HWND hInput;\r
8507   consoleEcho = FALSE;\r
8508   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8509   /* This works OK: set text and background both to the same color */\r
8510   cf = consoleCF;\r
8511   cf.crTextColor = COLOR_ECHOOFF;\r
8512   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8513   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8514 }\r
8515 \r
8516 /* No Raw()...? */\r
8517 \r
8518 void Colorize(ColorClass cc, int continuation)\r
8519 {\r
8520   currentColorClass = cc;\r
8521   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8522   consoleCF.crTextColor = textAttribs[cc].color;\r
8523   consoleCF.dwEffects = textAttribs[cc].effects;\r
8524   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8525 }\r
8526 \r
8527 char *\r
8528 UserName()\r
8529 {\r
8530   static char buf[MSG_SIZ];\r
8531   DWORD bufsiz = MSG_SIZ;\r
8532 \r
8533   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8534         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8535   }\r
8536   if (!GetUserName(buf, &bufsiz)) {\r
8537     /*DisplayError("Error getting user name", GetLastError());*/\r
8538     strcpy(buf, _("User"));\r
8539   }\r
8540   return buf;\r
8541 }\r
8542 \r
8543 char *\r
8544 HostName()\r
8545 {\r
8546   static char buf[MSG_SIZ];\r
8547   DWORD bufsiz = MSG_SIZ;\r
8548 \r
8549   if (!GetComputerName(buf, &bufsiz)) {\r
8550     /*DisplayError("Error getting host name", GetLastError());*/\r
8551     strcpy(buf, _("Unknown"));\r
8552   }\r
8553   return buf;\r
8554 }\r
8555 \r
8556 \r
8557 int\r
8558 ClockTimerRunning()\r
8559 {\r
8560   return clockTimerEvent != 0;\r
8561 }\r
8562 \r
8563 int\r
8564 StopClockTimer()\r
8565 {\r
8566   if (clockTimerEvent == 0) return FALSE;\r
8567   KillTimer(hwndMain, clockTimerEvent);\r
8568   clockTimerEvent = 0;\r
8569   return TRUE;\r
8570 }\r
8571 \r
8572 void\r
8573 StartClockTimer(long millisec)\r
8574 {\r
8575   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8576                              (UINT) millisec, NULL);\r
8577 }\r
8578 \r
8579 void\r
8580 DisplayWhiteClock(long timeRemaining, int highlight)\r
8581 {\r
8582   HDC hdc;\r
8583   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8584 \r
8585   if(appData.noGUI) return;\r
8586   hdc = GetDC(hwndMain);\r
8587   if (!IsIconic(hwndMain)) {\r
8588     DisplayAClock(hdc, timeRemaining, highlight, \r
8589                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8590   }\r
8591   if (highlight && iconCurrent == iconBlack) {\r
8592     iconCurrent = iconWhite;\r
8593     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8594     if (IsIconic(hwndMain)) {\r
8595       DrawIcon(hdc, 2, 2, iconCurrent);\r
8596     }\r
8597   }\r
8598   (void) ReleaseDC(hwndMain, hdc);\r
8599   if (hwndConsole)\r
8600     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8601 }\r
8602 \r
8603 void\r
8604 DisplayBlackClock(long timeRemaining, int highlight)\r
8605 {\r
8606   HDC hdc;\r
8607   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8608 \r
8609   if(appData.noGUI) return;\r
8610   hdc = GetDC(hwndMain);\r
8611   if (!IsIconic(hwndMain)) {\r
8612     DisplayAClock(hdc, timeRemaining, highlight, \r
8613                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8614   }\r
8615   if (highlight && iconCurrent == iconWhite) {\r
8616     iconCurrent = iconBlack;\r
8617     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8618     if (IsIconic(hwndMain)) {\r
8619       DrawIcon(hdc, 2, 2, iconCurrent);\r
8620     }\r
8621   }\r
8622   (void) ReleaseDC(hwndMain, hdc);\r
8623   if (hwndConsole)\r
8624     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8625 }\r
8626 \r
8627 \r
8628 int\r
8629 LoadGameTimerRunning()\r
8630 {\r
8631   return loadGameTimerEvent != 0;\r
8632 }\r
8633 \r
8634 int\r
8635 StopLoadGameTimer()\r
8636 {\r
8637   if (loadGameTimerEvent == 0) return FALSE;\r
8638   KillTimer(hwndMain, loadGameTimerEvent);\r
8639   loadGameTimerEvent = 0;\r
8640   return TRUE;\r
8641 }\r
8642 \r
8643 void\r
8644 StartLoadGameTimer(long millisec)\r
8645 {\r
8646   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8647                                 (UINT) millisec, NULL);\r
8648 }\r
8649 \r
8650 void\r
8651 AutoSaveGame()\r
8652 {\r
8653   char *defName;\r
8654   FILE *f;\r
8655   char fileTitle[MSG_SIZ];\r
8656 \r
8657   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8658   f = OpenFileDialog(hwndMain, "a", defName,\r
8659                      appData.oldSaveStyle ? "gam" : "pgn",\r
8660                      GAME_FILT, \r
8661                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8662   if (f != NULL) {\r
8663     SaveGame(f, 0, "");\r
8664     fclose(f);\r
8665   }\r
8666 }\r
8667 \r
8668 \r
8669 void\r
8670 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8671 {\r
8672   if (delayedTimerEvent != 0) {\r
8673     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8674       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8675     }\r
8676     KillTimer(hwndMain, delayedTimerEvent);\r
8677     delayedTimerEvent = 0;\r
8678     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8679     delayedTimerCallback();\r
8680   }\r
8681   delayedTimerCallback = cb;\r
8682   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8683                                 (UINT) millisec, NULL);\r
8684 }\r
8685 \r
8686 DelayedEventCallback\r
8687 GetDelayedEvent()\r
8688 {\r
8689   if (delayedTimerEvent) {\r
8690     return delayedTimerCallback;\r
8691   } else {\r
8692     return NULL;\r
8693   }\r
8694 }\r
8695 \r
8696 void\r
8697 CancelDelayedEvent()\r
8698 {\r
8699   if (delayedTimerEvent) {\r
8700     KillTimer(hwndMain, delayedTimerEvent);\r
8701     delayedTimerEvent = 0;\r
8702   }\r
8703 }\r
8704 \r
8705 DWORD GetWin32Priority(int nice)\r
8706 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8707 /*\r
8708 REALTIME_PRIORITY_CLASS     0x00000100\r
8709 HIGH_PRIORITY_CLASS         0x00000080\r
8710 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8711 NORMAL_PRIORITY_CLASS       0x00000020\r
8712 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8713 IDLE_PRIORITY_CLASS         0x00000040\r
8714 */\r
8715         if (nice < -15) return 0x00000080;\r
8716         if (nice < 0)   return 0x00008000;\r
8717         if (nice == 0)  return 0x00000020;\r
8718         if (nice < 15)  return 0x00004000;\r
8719         return 0x00000040;\r
8720 }\r
8721 \r
8722 /* Start a child process running the given program.\r
8723    The process's standard output can be read from "from", and its\r
8724    standard input can be written to "to".\r
8725    Exit with fatal error if anything goes wrong.\r
8726    Returns an opaque pointer that can be used to destroy the process\r
8727    later.\r
8728 */\r
8729 int\r
8730 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8731 {\r
8732 #define BUFSIZE 4096\r
8733 \r
8734   HANDLE hChildStdinRd, hChildStdinWr,\r
8735     hChildStdoutRd, hChildStdoutWr;\r
8736   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8737   SECURITY_ATTRIBUTES saAttr;\r
8738   BOOL fSuccess;\r
8739   PROCESS_INFORMATION piProcInfo;\r
8740   STARTUPINFO siStartInfo;\r
8741   ChildProc *cp;\r
8742   char buf[MSG_SIZ];\r
8743   DWORD err;\r
8744 \r
8745   if (appData.debugMode) {\r
8746     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8747   }\r
8748 \r
8749   *pr = NoProc;\r
8750 \r
8751   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8752   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8753   saAttr.bInheritHandle = TRUE;\r
8754   saAttr.lpSecurityDescriptor = NULL;\r
8755 \r
8756   /*\r
8757    * The steps for redirecting child's STDOUT:\r
8758    *     1. Create anonymous pipe to be STDOUT for child.\r
8759    *     2. Create a noninheritable duplicate of read handle,\r
8760    *         and close the inheritable read handle.\r
8761    */\r
8762 \r
8763   /* Create a pipe for the child's STDOUT. */\r
8764   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8765     return GetLastError();\r
8766   }\r
8767 \r
8768   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8769   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8770                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8771                              FALSE,     /* not inherited */\r
8772                              DUPLICATE_SAME_ACCESS);\r
8773   if (! fSuccess) {\r
8774     return GetLastError();\r
8775   }\r
8776   CloseHandle(hChildStdoutRd);\r
8777 \r
8778   /*\r
8779    * The steps for redirecting child's STDIN:\r
8780    *     1. Create anonymous pipe to be STDIN for child.\r
8781    *     2. Create a noninheritable duplicate of write handle,\r
8782    *         and close the inheritable write handle.\r
8783    */\r
8784 \r
8785   /* Create a pipe for the child's STDIN. */\r
8786   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8787     return GetLastError();\r
8788   }\r
8789 \r
8790   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8791   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8792                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8793                              FALSE,     /* not inherited */\r
8794                              DUPLICATE_SAME_ACCESS);\r
8795   if (! fSuccess) {\r
8796     return GetLastError();\r
8797   }\r
8798   CloseHandle(hChildStdinWr);\r
8799 \r
8800   /* Arrange to (1) look in dir for the child .exe file, and\r
8801    * (2) have dir be the child's working directory.  Interpret\r
8802    * dir relative to the directory WinBoard loaded from. */\r
8803   GetCurrentDirectory(MSG_SIZ, buf);\r
8804   SetCurrentDirectory(installDir);\r
8805   SetCurrentDirectory(dir);\r
8806 \r
8807   /* Now create the child process. */\r
8808 \r
8809   siStartInfo.cb = sizeof(STARTUPINFO);\r
8810   siStartInfo.lpReserved = NULL;\r
8811   siStartInfo.lpDesktop = NULL;\r
8812   siStartInfo.lpTitle = NULL;\r
8813   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8814   siStartInfo.cbReserved2 = 0;\r
8815   siStartInfo.lpReserved2 = NULL;\r
8816   siStartInfo.hStdInput = hChildStdinRd;\r
8817   siStartInfo.hStdOutput = hChildStdoutWr;\r
8818   siStartInfo.hStdError = hChildStdoutWr;\r
8819 \r
8820   fSuccess = CreateProcess(NULL,\r
8821                            cmdLine,        /* command line */\r
8822                            NULL,           /* process security attributes */\r
8823                            NULL,           /* primary thread security attrs */\r
8824                            TRUE,           /* handles are inherited */\r
8825                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8826                            NULL,           /* use parent's environment */\r
8827                            NULL,\r
8828                            &siStartInfo, /* STARTUPINFO pointer */\r
8829                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8830 \r
8831   err = GetLastError();\r
8832   SetCurrentDirectory(buf); /* return to prev directory */\r
8833   if (! fSuccess) {\r
8834     return err;\r
8835   }\r
8836 \r
8837   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8838     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8839     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8840   }\r
8841 \r
8842   /* Close the handles we don't need in the parent */\r
8843   CloseHandle(piProcInfo.hThread);\r
8844   CloseHandle(hChildStdinRd);\r
8845   CloseHandle(hChildStdoutWr);\r
8846 \r
8847   /* Prepare return value */\r
8848   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8849   cp->kind = CPReal;\r
8850   cp->hProcess = piProcInfo.hProcess;\r
8851   cp->pid = piProcInfo.dwProcessId;\r
8852   cp->hFrom = hChildStdoutRdDup;\r
8853   cp->hTo = hChildStdinWrDup;\r
8854 \r
8855   *pr = (void *) cp;\r
8856 \r
8857   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8858      2000 where engines sometimes don't see the initial command(s)\r
8859      from WinBoard and hang.  I don't understand how that can happen,\r
8860      but the Sleep is harmless, so I've put it in.  Others have also\r
8861      reported what may be the same problem, so hopefully this will fix\r
8862      it for them too.  */\r
8863   Sleep(500);\r
8864 \r
8865   return NO_ERROR;\r
8866 }\r
8867 \r
8868 \r
8869 void\r
8870 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8871 {\r
8872   ChildProc *cp; int result;\r
8873 \r
8874   cp = (ChildProc *) pr;\r
8875   if (cp == NULL) return;\r
8876 \r
8877   switch (cp->kind) {\r
8878   case CPReal:\r
8879     /* TerminateProcess is considered harmful, so... */\r
8880     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8881     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8882     /* The following doesn't work because the chess program\r
8883        doesn't "have the same console" as WinBoard.  Maybe\r
8884        we could arrange for this even though neither WinBoard\r
8885        nor the chess program uses a console for stdio? */\r
8886     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8887 \r
8888     /* [AS] Special termination modes for misbehaving programs... */\r
8889     if( signal == 9 ) { \r
8890         result = TerminateProcess( cp->hProcess, 0 );\r
8891 \r
8892         if ( appData.debugMode) {\r
8893             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
8894         }\r
8895     }\r
8896     else if( signal == 10 ) {\r
8897         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
8898 \r
8899         if( dw != WAIT_OBJECT_0 ) {\r
8900             result = TerminateProcess( cp->hProcess, 0 );\r
8901 \r
8902             if ( appData.debugMode) {\r
8903                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
8904             }\r
8905 \r
8906         }\r
8907     }\r
8908 \r
8909     CloseHandle(cp->hProcess);\r
8910     break;\r
8911 \r
8912   case CPComm:\r
8913     if (cp->hFrom) CloseHandle(cp->hFrom);\r
8914     break;\r
8915 \r
8916   case CPSock:\r
8917     closesocket(cp->sock);\r
8918     WSACleanup();\r
8919     break;\r
8920 \r
8921   case CPRcmd:\r
8922     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
8923     closesocket(cp->sock);\r
8924     closesocket(cp->sock2);\r
8925     WSACleanup();\r
8926     break;\r
8927   }\r
8928   free(cp);\r
8929 }\r
8930 \r
8931 void\r
8932 InterruptChildProcess(ProcRef pr)\r
8933 {\r
8934   ChildProc *cp;\r
8935 \r
8936   cp = (ChildProc *) pr;\r
8937   if (cp == NULL) return;\r
8938   switch (cp->kind) {\r
8939   case CPReal:\r
8940     /* The following doesn't work because the chess program\r
8941        doesn't "have the same console" as WinBoard.  Maybe\r
8942        we could arrange for this even though neither WinBoard\r
8943        nor the chess program uses a console for stdio */\r
8944     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
8945     break;\r
8946 \r
8947   case CPComm:\r
8948   case CPSock:\r
8949     /* Can't interrupt */\r
8950     break;\r
8951 \r
8952   case CPRcmd:\r
8953     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
8954     break;\r
8955   }\r
8956 }\r
8957 \r
8958 \r
8959 int\r
8960 OpenTelnet(char *host, char *port, ProcRef *pr)\r
8961 {\r
8962   char cmdLine[MSG_SIZ];\r
8963 \r
8964   if (port[0] == NULLCHAR) {\r
8965     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
8966   } else {\r
8967     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
8968   }\r
8969   return StartChildProcess(cmdLine, "", pr);\r
8970 }\r
8971 \r
8972 \r
8973 /* Code to open TCP sockets */\r
8974 \r
8975 int\r
8976 OpenTCP(char *host, char *port, ProcRef *pr)\r
8977 {\r
8978   ChildProc *cp;\r
8979   int err;\r
8980   SOCKET s;\r
8981   struct sockaddr_in sa, mysa;\r
8982   struct hostent FAR *hp;\r
8983   unsigned short uport;\r
8984   WORD wVersionRequested;\r
8985   WSADATA wsaData;\r
8986 \r
8987   /* Initialize socket DLL */\r
8988   wVersionRequested = MAKEWORD(1, 1);\r
8989   err = WSAStartup(wVersionRequested, &wsaData);\r
8990   if (err != 0) return err;\r
8991 \r
8992   /* Make socket */\r
8993   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
8994     err = WSAGetLastError();\r
8995     WSACleanup();\r
8996     return err;\r
8997   }\r
8998 \r
8999   /* Bind local address using (mostly) don't-care values.\r
9000    */\r
9001   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9002   mysa.sin_family = AF_INET;\r
9003   mysa.sin_addr.s_addr = INADDR_ANY;\r
9004   uport = (unsigned short) 0;\r
9005   mysa.sin_port = htons(uport);\r
9006   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9007       == SOCKET_ERROR) {\r
9008     err = WSAGetLastError();\r
9009     WSACleanup();\r
9010     return err;\r
9011   }\r
9012 \r
9013   /* Resolve remote host name */\r
9014   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9015   if (!(hp = gethostbyname(host))) {\r
9016     unsigned int b0, b1, b2, b3;\r
9017 \r
9018     err = WSAGetLastError();\r
9019 \r
9020     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9021       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9022       hp->h_addrtype = AF_INET;\r
9023       hp->h_length = 4;\r
9024       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9025       hp->h_addr_list[0] = (char *) malloc(4);\r
9026       hp->h_addr_list[0][0] = (char) b0;\r
9027       hp->h_addr_list[0][1] = (char) b1;\r
9028       hp->h_addr_list[0][2] = (char) b2;\r
9029       hp->h_addr_list[0][3] = (char) b3;\r
9030     } else {\r
9031       WSACleanup();\r
9032       return err;\r
9033     }\r
9034   }\r
9035   sa.sin_family = hp->h_addrtype;\r
9036   uport = (unsigned short) atoi(port);\r
9037   sa.sin_port = htons(uport);\r
9038   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9039 \r
9040   /* Make connection */\r
9041   if (connect(s, (struct sockaddr *) &sa,\r
9042               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9043     err = WSAGetLastError();\r
9044     WSACleanup();\r
9045     return err;\r
9046   }\r
9047 \r
9048   /* Prepare return value */\r
9049   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9050   cp->kind = CPSock;\r
9051   cp->sock = s;\r
9052   *pr = (ProcRef *) cp;\r
9053 \r
9054   return NO_ERROR;\r
9055 }\r
9056 \r
9057 int\r
9058 OpenCommPort(char *name, ProcRef *pr)\r
9059 {\r
9060   HANDLE h;\r
9061   COMMTIMEOUTS ct;\r
9062   ChildProc *cp;\r
9063   char fullname[MSG_SIZ];\r
9064 \r
9065   if (*name != '\\')\r
9066     sprintf(fullname, "\\\\.\\%s", name);\r
9067   else\r
9068     strcpy(fullname, name);\r
9069 \r
9070   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9071                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9072   if (h == (HANDLE) -1) {\r
9073     return GetLastError();\r
9074   }\r
9075   hCommPort = h;\r
9076 \r
9077   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9078 \r
9079   /* Accumulate characters until a 100ms pause, then parse */\r
9080   ct.ReadIntervalTimeout = 100;\r
9081   ct.ReadTotalTimeoutMultiplier = 0;\r
9082   ct.ReadTotalTimeoutConstant = 0;\r
9083   ct.WriteTotalTimeoutMultiplier = 0;\r
9084   ct.WriteTotalTimeoutConstant = 0;\r
9085   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9086 \r
9087   /* Prepare return value */\r
9088   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9089   cp->kind = CPComm;\r
9090   cp->hFrom = h;\r
9091   cp->hTo = h;\r
9092   *pr = (ProcRef *) cp;\r
9093 \r
9094   return NO_ERROR;\r
9095 }\r
9096 \r
9097 int\r
9098 OpenLoopback(ProcRef *pr)\r
9099 {\r
9100   DisplayFatalError(_("Not implemented"), 0, 1);\r
9101   return NO_ERROR;\r
9102 }\r
9103 \r
9104 \r
9105 int\r
9106 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9107 {\r
9108   ChildProc *cp;\r
9109   int err;\r
9110   SOCKET s, s2, s3;\r
9111   struct sockaddr_in sa, mysa;\r
9112   struct hostent FAR *hp;\r
9113   unsigned short uport;\r
9114   WORD wVersionRequested;\r
9115   WSADATA wsaData;\r
9116   int fromPort;\r
9117   char stderrPortStr[MSG_SIZ];\r
9118 \r
9119   /* Initialize socket DLL */\r
9120   wVersionRequested = MAKEWORD(1, 1);\r
9121   err = WSAStartup(wVersionRequested, &wsaData);\r
9122   if (err != 0) return err;\r
9123 \r
9124   /* Resolve remote host name */\r
9125   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9126   if (!(hp = gethostbyname(host))) {\r
9127     unsigned int b0, b1, b2, b3;\r
9128 \r
9129     err = WSAGetLastError();\r
9130 \r
9131     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9132       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9133       hp->h_addrtype = AF_INET;\r
9134       hp->h_length = 4;\r
9135       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9136       hp->h_addr_list[0] = (char *) malloc(4);\r
9137       hp->h_addr_list[0][0] = (char) b0;\r
9138       hp->h_addr_list[0][1] = (char) b1;\r
9139       hp->h_addr_list[0][2] = (char) b2;\r
9140       hp->h_addr_list[0][3] = (char) b3;\r
9141     } else {\r
9142       WSACleanup();\r
9143       return err;\r
9144     }\r
9145   }\r
9146   sa.sin_family = hp->h_addrtype;\r
9147   uport = (unsigned short) 514;\r
9148   sa.sin_port = htons(uport);\r
9149   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9150 \r
9151   /* Bind local socket to unused "privileged" port address\r
9152    */\r
9153   s = INVALID_SOCKET;\r
9154   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9155   mysa.sin_family = AF_INET;\r
9156   mysa.sin_addr.s_addr = INADDR_ANY;\r
9157   for (fromPort = 1023;; fromPort--) {\r
9158     if (fromPort < 0) {\r
9159       WSACleanup();\r
9160       return WSAEADDRINUSE;\r
9161     }\r
9162     if (s == INVALID_SOCKET) {\r
9163       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9164         err = WSAGetLastError();\r
9165         WSACleanup();\r
9166         return err;\r
9167       }\r
9168     }\r
9169     uport = (unsigned short) fromPort;\r
9170     mysa.sin_port = htons(uport);\r
9171     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9172         == SOCKET_ERROR) {\r
9173       err = WSAGetLastError();\r
9174       if (err == WSAEADDRINUSE) continue;\r
9175       WSACleanup();\r
9176       return err;\r
9177     }\r
9178     if (connect(s, (struct sockaddr *) &sa,\r
9179       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9180       err = WSAGetLastError();\r
9181       if (err == WSAEADDRINUSE) {\r
9182         closesocket(s);\r
9183         s = -1;\r
9184         continue;\r
9185       }\r
9186       WSACleanup();\r
9187       return err;\r
9188     }\r
9189     break;\r
9190   }\r
9191 \r
9192   /* Bind stderr local socket to unused "privileged" port address\r
9193    */\r
9194   s2 = INVALID_SOCKET;\r
9195   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9196   mysa.sin_family = AF_INET;\r
9197   mysa.sin_addr.s_addr = INADDR_ANY;\r
9198   for (fromPort = 1023;; fromPort--) {\r
9199     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9200     if (fromPort < 0) {\r
9201       (void) closesocket(s);\r
9202       WSACleanup();\r
9203       return WSAEADDRINUSE;\r
9204     }\r
9205     if (s2 == INVALID_SOCKET) {\r
9206       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9207         err = WSAGetLastError();\r
9208         closesocket(s);\r
9209         WSACleanup();\r
9210         return err;\r
9211       }\r
9212     }\r
9213     uport = (unsigned short) fromPort;\r
9214     mysa.sin_port = htons(uport);\r
9215     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9216         == SOCKET_ERROR) {\r
9217       err = WSAGetLastError();\r
9218       if (err == WSAEADDRINUSE) continue;\r
9219       (void) closesocket(s);\r
9220       WSACleanup();\r
9221       return err;\r
9222     }\r
9223     if (listen(s2, 1) == SOCKET_ERROR) {\r
9224       err = WSAGetLastError();\r
9225       if (err == WSAEADDRINUSE) {\r
9226         closesocket(s2);\r
9227         s2 = INVALID_SOCKET;\r
9228         continue;\r
9229       }\r
9230       (void) closesocket(s);\r
9231       (void) closesocket(s2);\r
9232       WSACleanup();\r
9233       return err;\r
9234     }\r
9235     break;\r
9236   }\r
9237   prevStderrPort = fromPort; // remember port used\r
9238   sprintf(stderrPortStr, "%d", fromPort);\r
9239 \r
9240   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9241     err = WSAGetLastError();\r
9242     (void) closesocket(s);\r
9243     (void) closesocket(s2);\r
9244     WSACleanup();\r
9245     return err;\r
9246   }\r
9247 \r
9248   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9249     err = WSAGetLastError();\r
9250     (void) closesocket(s);\r
9251     (void) closesocket(s2);\r
9252     WSACleanup();\r
9253     return err;\r
9254   }\r
9255   if (*user == NULLCHAR) user = UserName();\r
9256   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9257     err = WSAGetLastError();\r
9258     (void) closesocket(s);\r
9259     (void) closesocket(s2);\r
9260     WSACleanup();\r
9261     return err;\r
9262   }\r
9263   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9264     err = WSAGetLastError();\r
9265     (void) closesocket(s);\r
9266     (void) closesocket(s2);\r
9267     WSACleanup();\r
9268     return err;\r
9269   }\r
9270 \r
9271   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9272     err = WSAGetLastError();\r
9273     (void) closesocket(s);\r
9274     (void) closesocket(s2);\r
9275     WSACleanup();\r
9276     return err;\r
9277   }\r
9278   (void) closesocket(s2);  /* Stop listening */\r
9279 \r
9280   /* Prepare return value */\r
9281   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9282   cp->kind = CPRcmd;\r
9283   cp->sock = s;\r
9284   cp->sock2 = s3;\r
9285   *pr = (ProcRef *) cp;\r
9286 \r
9287   return NO_ERROR;\r
9288 }\r
9289 \r
9290 \r
9291 InputSourceRef\r
9292 AddInputSource(ProcRef pr, int lineByLine,\r
9293                InputCallback func, VOIDSTAR closure)\r
9294 {\r
9295   InputSource *is, *is2 = NULL;\r
9296   ChildProc *cp = (ChildProc *) pr;\r
9297 \r
9298   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9299   is->lineByLine = lineByLine;\r
9300   is->func = func;\r
9301   is->closure = closure;\r
9302   is->second = NULL;\r
9303   is->next = is->buf;\r
9304   if (pr == NoProc) {\r
9305     is->kind = CPReal;\r
9306     consoleInputSource = is;\r
9307   } else {\r
9308     is->kind = cp->kind;\r
9309     /* \r
9310         [AS] Try to avoid a race condition if the thread is given control too early:\r
9311         we create all threads suspended so that the is->hThread variable can be\r
9312         safely assigned, then let the threads start with ResumeThread.\r
9313     */\r
9314     switch (cp->kind) {\r
9315     case CPReal:\r
9316       is->hFile = cp->hFrom;\r
9317       cp->hFrom = NULL; /* now owned by InputThread */\r
9318       is->hThread =\r
9319         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9320                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9321       break;\r
9322 \r
9323     case CPComm:\r
9324       is->hFile = cp->hFrom;\r
9325       cp->hFrom = NULL; /* now owned by InputThread */\r
9326       is->hThread =\r
9327         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9328                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9329       break;\r
9330 \r
9331     case CPSock:\r
9332       is->sock = cp->sock;\r
9333       is->hThread =\r
9334         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9335                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9336       break;\r
9337 \r
9338     case CPRcmd:\r
9339       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9340       *is2 = *is;\r
9341       is->sock = cp->sock;\r
9342       is->second = is2;\r
9343       is2->sock = cp->sock2;\r
9344       is2->second = is2;\r
9345       is->hThread =\r
9346         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9347                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9348       is2->hThread =\r
9349         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9350                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9351       break;\r
9352     }\r
9353 \r
9354     if( is->hThread != NULL ) {\r
9355         ResumeThread( is->hThread );\r
9356     }\r
9357 \r
9358     if( is2 != NULL && is2->hThread != NULL ) {\r
9359         ResumeThread( is2->hThread );\r
9360     }\r
9361   }\r
9362 \r
9363   return (InputSourceRef) is;\r
9364 }\r
9365 \r
9366 void\r
9367 RemoveInputSource(InputSourceRef isr)\r
9368 {\r
9369   InputSource *is;\r
9370 \r
9371   is = (InputSource *) isr;\r
9372   is->hThread = NULL;  /* tell thread to stop */\r
9373   CloseHandle(is->hThread);\r
9374   if (is->second != NULL) {\r
9375     is->second->hThread = NULL;\r
9376     CloseHandle(is->second->hThread);\r
9377   }\r
9378 }\r
9379 \r
9380 int no_wrap(char *message, int count)\r
9381 {\r
9382     ConsoleOutput(message, count, FALSE);\r
9383     return count;\r
9384 }\r
9385 \r
9386 int\r
9387 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9388 {\r
9389   DWORD dOutCount;\r
9390   int outCount = SOCKET_ERROR;\r
9391   ChildProc *cp = (ChildProc *) pr;\r
9392   static OVERLAPPED ovl;\r
9393   static int line = 0;\r
9394 \r
9395   if (pr == NoProc)\r
9396   {\r
9397     if (appData.noJoin || !appData.useInternalWrap)\r
9398       return no_wrap(message, count);\r
9399     else\r
9400     {\r
9401       int width = get_term_width();\r
9402       int len = wrap(NULL, message, count, width, &line);\r
9403       char *msg = malloc(len);\r
9404       int dbgchk;\r
9405 \r
9406       if (!msg)\r
9407         return no_wrap(message, count);\r
9408       else\r
9409       {\r
9410         dbgchk = wrap(msg, message, count, width, &line);\r
9411         if (dbgchk != len && appData.debugMode)\r
9412             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9413         ConsoleOutput(msg, len, FALSE);\r
9414         free(msg);\r
9415         return len;\r
9416       }\r
9417     }\r
9418   }\r
9419 \r
9420   if (ovl.hEvent == NULL) {\r
9421     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9422   }\r
9423   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9424 \r
9425   switch (cp->kind) {\r
9426   case CPSock:\r
9427   case CPRcmd:\r
9428     outCount = send(cp->sock, message, count, 0);\r
9429     if (outCount == SOCKET_ERROR) {\r
9430       *outError = WSAGetLastError();\r
9431     } else {\r
9432       *outError = NO_ERROR;\r
9433     }\r
9434     break;\r
9435 \r
9436   case CPReal:\r
9437     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9438                   &dOutCount, NULL)) {\r
9439       *outError = NO_ERROR;\r
9440       outCount = (int) dOutCount;\r
9441     } else {\r
9442       *outError = GetLastError();\r
9443     }\r
9444     break;\r
9445 \r
9446   case CPComm:\r
9447     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9448                             &dOutCount, &ovl);\r
9449     if (*outError == NO_ERROR) {\r
9450       outCount = (int) dOutCount;\r
9451     }\r
9452     break;\r
9453   }\r
9454   return outCount;\r
9455 }\r
9456 \r
9457 int\r
9458 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9459                        long msdelay)\r
9460 {\r
9461   /* Ignore delay, not implemented for WinBoard */\r
9462   return OutputToProcess(pr, message, count, outError);\r
9463 }\r
9464 \r
9465 \r
9466 void\r
9467 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9468                         char *buf, int count, int error)\r
9469 {\r
9470   DisplayFatalError(_("Not implemented"), 0, 1);\r
9471 }\r
9472 \r
9473 /* see wgamelist.c for Game List functions */\r
9474 /* see wedittags.c for Edit Tags functions */\r
9475 \r
9476 \r
9477 VOID\r
9478 ICSInitScript()\r
9479 {\r
9480   FILE *f;\r
9481   char buf[MSG_SIZ];\r
9482   char *dummy;\r
9483 \r
9484   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9485     f = fopen(buf, "r");\r
9486     if (f != NULL) {\r
9487       ProcessICSInitScript(f);\r
9488       fclose(f);\r
9489     }\r
9490   }\r
9491 }\r
9492 \r
9493 \r
9494 VOID\r
9495 StartAnalysisClock()\r
9496 {\r
9497   if (analysisTimerEvent) return;\r
9498   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9499                                         (UINT) 2000, NULL);\r
9500 }\r
9501 \r
9502 VOID\r
9503 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9504 {\r
9505   highlightInfo.sq[0].x = fromX;\r
9506   highlightInfo.sq[0].y = fromY;\r
9507   highlightInfo.sq[1].x = toX;\r
9508   highlightInfo.sq[1].y = toY;\r
9509 }\r
9510 \r
9511 VOID\r
9512 ClearHighlights()\r
9513 {\r
9514   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9515     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9516 }\r
9517 \r
9518 VOID\r
9519 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9520 {\r
9521   premoveHighlightInfo.sq[0].x = fromX;\r
9522   premoveHighlightInfo.sq[0].y = fromY;\r
9523   premoveHighlightInfo.sq[1].x = toX;\r
9524   premoveHighlightInfo.sq[1].y = toY;\r
9525 }\r
9526 \r
9527 VOID\r
9528 ClearPremoveHighlights()\r
9529 {\r
9530   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9531     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9532 }\r
9533 \r
9534 VOID\r
9535 ShutDownFrontEnd()\r
9536 {\r
9537   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9538   DeleteClipboardTempFiles();\r
9539 }\r
9540 \r
9541 void\r
9542 BoardToTop()\r
9543 {\r
9544     if (IsIconic(hwndMain))\r
9545       ShowWindow(hwndMain, SW_RESTORE);\r
9546 \r
9547     SetActiveWindow(hwndMain);\r
9548 }\r
9549 \r
9550 /*\r
9551  * Prototypes for animation support routines\r
9552  */\r
9553 static void ScreenSquare(int column, int row, POINT * pt);\r
9554 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9555      POINT frames[], int * nFrames);\r
9556 \r
9557 \r
9558 void\r
9559 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
9560 {       // [HGM] atomic: animate blast wave\r
9561         int i;\r
9562 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
9563         explodeInfo.fromX = fromX;\r
9564         explodeInfo.fromY = fromY;\r
9565         explodeInfo.toX = toX;\r
9566         explodeInfo.toY = toY;\r
9567         for(i=1; i<nFrames; i++) {\r
9568             explodeInfo.radius = (i*180)/(nFrames-1);\r
9569             DrawPosition(FALSE, NULL);\r
9570             Sleep(appData.animSpeed);\r
9571         }\r
9572         explodeInfo.radius = 0;\r
9573         DrawPosition(TRUE, NULL);\r
9574 }\r
9575 \r
9576 #define kFactor 4\r
9577 \r
9578 void\r
9579 AnimateMove(board, fromX, fromY, toX, toY)\r
9580      Board board;\r
9581      int fromX;\r
9582      int fromY;\r
9583      int toX;\r
9584      int toY;\r
9585 {\r
9586   ChessSquare piece;\r
9587   POINT start, finish, mid;\r
9588   POINT frames[kFactor * 2 + 1];\r
9589   int nFrames, n;\r
9590 \r
9591   if (!appData.animate) return;\r
9592   if (doingSizing) return;\r
9593   if (fromY < 0 || fromX < 0) return;\r
9594   piece = board[fromY][fromX];\r
9595   if (piece >= EmptySquare) return;\r
9596 \r
9597   ScreenSquare(fromX, fromY, &start);\r
9598   ScreenSquare(toX, toY, &finish);\r
9599 \r
9600   /* All pieces except knights move in straight line */\r
9601   if (piece != WhiteKnight && piece != BlackKnight) {\r
9602     mid.x = start.x + (finish.x - start.x) / 2;\r
9603     mid.y = start.y + (finish.y - start.y) / 2;\r
9604   } else {\r
9605     /* Knight: make diagonal movement then straight */\r
9606     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9607        mid.x = start.x + (finish.x - start.x) / 2;\r
9608        mid.y = finish.y;\r
9609      } else {\r
9610        mid.x = finish.x;\r
9611        mid.y = start.y + (finish.y - start.y) / 2;\r
9612      }\r
9613   }\r
9614   \r
9615   /* Don't use as many frames for very short moves */\r
9616   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9617     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9618   else\r
9619     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9620 \r
9621   animInfo.from.x = fromX;\r
9622   animInfo.from.y = fromY;\r
9623   animInfo.to.x = toX;\r
9624   animInfo.to.y = toY;\r
9625   animInfo.lastpos = start;\r
9626   animInfo.piece = piece;\r
9627   for (n = 0; n < nFrames; n++) {\r
9628     animInfo.pos = frames[n];\r
9629     DrawPosition(FALSE, NULL);\r
9630     animInfo.lastpos = animInfo.pos;\r
9631     Sleep(appData.animSpeed);\r
9632   }\r
9633   animInfo.pos = finish;\r
9634   DrawPosition(FALSE, NULL);\r
9635   animInfo.piece = EmptySquare;\r
9636   if(gameInfo.variant == VariantAtomic && \r
9637      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
9638         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
9639 }\r
9640 \r
9641 /*      Convert board position to corner of screen rect and color       */\r
9642 \r
9643 static void\r
9644 ScreenSquare(column, row, pt)\r
9645      int column; int row; POINT * pt;\r
9646 {\r
9647   if (flipView) {\r
9648     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9649     pt->y = lineGap + row * (squareSize + lineGap);\r
9650   } else {\r
9651     pt->x = lineGap + column * (squareSize + lineGap);\r
9652     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9653   }\r
9654 }\r
9655 \r
9656 /*      Generate a series of frame coords from start->mid->finish.\r
9657         The movement rate doubles until the half way point is\r
9658         reached, then halves back down to the final destination,\r
9659         which gives a nice slow in/out effect. The algorithmn\r
9660         may seem to generate too many intermediates for short\r
9661         moves, but remember that the purpose is to attract the\r
9662         viewers attention to the piece about to be moved and\r
9663         then to where it ends up. Too few frames would be less\r
9664         noticeable.                                             */\r
9665 \r
9666 static void\r
9667 Tween(start, mid, finish, factor, frames, nFrames)\r
9668      POINT * start; POINT * mid;\r
9669      POINT * finish; int factor;\r
9670      POINT frames[]; int * nFrames;\r
9671 {\r
9672   int n, fraction = 1, count = 0;\r
9673 \r
9674   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9675   for (n = 0; n < factor; n++)\r
9676     fraction *= 2;\r
9677   for (n = 0; n < factor; n++) {\r
9678     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9679     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9680     count ++;\r
9681     fraction = fraction / 2;\r
9682   }\r
9683   \r
9684   /* Midpoint */\r
9685   frames[count] = *mid;\r
9686   count ++;\r
9687   \r
9688   /* Slow out, stepping 1/2, then 1/4, ... */\r
9689   fraction = 2;\r
9690   for (n = 0; n < factor; n++) {\r
9691     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9692     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9693     count ++;\r
9694     fraction = fraction * 2;\r
9695   }\r
9696   *nFrames = count;\r
9697 }\r
9698 \r
9699 void\r
9700 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9701 {\r
9702     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9703 \r
9704     EvalGraphSet( first, last, current, pvInfoList );\r
9705 }\r