Merge commit 'v4.3.16'
[xboard.git] / winboard / wengineo.c
1 /*\r
2  * Engine output (PV)\r
3  *\r
4  * Author: Alessandro Scotti (Dec 2005)\r
5  *\r
6  * ------------------------------------------------------------------------\r
7  * This program is free software; you can redistribute it and/or modify\r
8  * it under the terms of the GNU General Public License as published by\r
9  * the Free Software Foundation; either version 2 of the License, or\r
10  * (at your option) any later version.\r
11  *\r
12  * This program is distributed in the hope that it will be useful,\r
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
15  * GNU General Public License for more details.\r
16  *\r
17  * You should have received a copy of the GNU General Public License\r
18  * along with this program; if not, write to the Free Software\r
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
20  * ------------------------------------------------------------------------\r
21  */\r
22 #include "config.h"\r
23 \r
24 #include <windows.h> /* required for all Windows applications */\r
25 #include <richedit.h>\r
26 #include <stdio.h>\r
27 #include <stdlib.h>\r
28 #include <malloc.h>\r
29 #include <commdlg.h>\r
30 #include <dlgs.h>\r
31 \r
32 #include "common.h"\r
33 #include "winboard.h"\r
34 #include "frontend.h"\r
35 #include "backend.h"\r
36 \r
37 #include "wsnap.h"\r
38 \r
39 // [HGM] define numbers to indicate icons, for referring to them in platform-independent way\r
40 #define nColorBlack   1\r
41 #define nColorWhite   2\r
42 #define nColorUnknown 3\r
43 #define nClear        4\r
44 #define nPondering    5\r
45 #define nThinking     6\r
46 #define nAnalyzing    7\r
47 \r
48 HICON icons[8]; // [HGM] this front-end array translates back-end icon indicator to handle\r
49 \r
50 // [HGM] same for output fields (note that there are two of each type, one per color)\r
51 #define nColorIcon 1\r
52 #define nStateIcon 2\r
53 #define nLabel     3\r
54 #define nStateData 4\r
55 #define nLabelNPS  5\r
56 #define nMemo      6\r
57 \r
58 HWND outputField[2][7]; // [HGM] front-end array to translate output field to window handle\r
59 \r
60 void EngineOutputPopUp();\r
61 void EngineOutputPopDown();\r
62 int  EngineOutputIsUp();\r
63 \r
64 #define SHOW_PONDERING\r
65 \r
66 /* Imports from backend.c */\r
67 char * SavePart(char *str);\r
68 extern int opponentKibitzes;\r
69 \r
70 /* Imports from winboard.c */\r
71 extern HWND engineOutputDialog;\r
72 extern int     engineOutputDialogUp;\r
73 \r
74 extern HINSTANCE hInst;\r
75 extern HWND hwndMain;\r
76 \r
77 extern WindowPlacement wpEngineOutput;\r
78 \r
79 /* Module variables */\r
80 #define H_MARGIN            2\r
81 #define V_MARGIN            2\r
82 #define LABEL_V_DISTANCE    1   /* Distance between label and memo */\r
83 #define SPLITTER_SIZE       4   /* Distance between first memo and second label */\r
84 \r
85 #define ICON_SIZE           14\r
86 \r
87 #define STATE_UNKNOWN   -1\r
88 #define STATE_THINKING   0\r
89 #define STATE_IDLE       1\r
90 #define STATE_PONDERING  2\r
91 #define STATE_ANALYZING  3\r
92 \r
93 static int  windowMode = 1;\r
94 \r
95 static int  needInit = TRUE;\r
96 \r
97 static int  lastDepth[2] = { -1, -1 };\r
98 static int  lastForwardMostMove[2] = { -1, -1 };\r
99 static int  engineState[2] = { -1, -1 };\r
100 \r
101 typedef struct {\r
102 //    HWND hColorIcon; // [HGM] the output-control handles are no loger passed,\r
103 //    HWND hLabel;     //       to give better front-end / back-end separation\r
104 //    HWND hStateIcon; //       the front-end routines now get them from a (front-end)\r
105 //    HWND hStateData; //       table, indexed by output-field indicators.\r
106 //    HWND hLabelNPS;\r
107 //    HWND hMemo;\r
108     char * name;\r
109     int which;\r
110     int depth;\r
111     u64 nodes;\r
112     int score;\r
113     int time;\r
114     char * pv;\r
115     char * hint;\r
116     int an_move_index;\r
117     int an_move_count;\r
118 } EngineOutputData;\r
119 \r
120 static VerifyDisplayMode();\r
121 static void UpdateControls( EngineOutputData * ed );\r
122 static SetEngineState( int which, int state, char * state_data );\r
123 \r
124 // front end\r
125 static HICON LoadIconEx( int id )\r
126 {\r
127     return LoadImage( hInst, MAKEINTRESOURCE(id), IMAGE_ICON, ICON_SIZE, ICON_SIZE, 0 );\r
128 }\r
129 \r
130 // [HGM] the platform-dependent way of indicating where output should go is now all\r
131 // concentrated here, where a table of platform-dependent handles are initialized.\r
132 // This cleanses most other routines of front-end stuff, so they can go into the back end.\r
133 static void InitializeEngineOutput()\r
134 {\r
135  //   if( needInit ) { // needInit was already tested before call\r
136         // [HGM] made this into a table, rather than separate global variables\r
137         icons[nColorBlack]   = LoadIconEx( IDI_BLACK_14 );\r
138         icons[nColorWhite]   = LoadIconEx( IDI_WHITE_14 );\r
139         icons[nColorUnknown] = LoadIconEx( IDI_UNKNOWN_14 );\r
140         icons[nClear]        = LoadIconEx( IDI_TRANS_14 );\r
141         icons[nPondering]    = LoadIconEx( IDI_PONDER_14 );\r
142         icons[nThinking]     = LoadIconEx( IDI_CLOCK_14 );\r
143         icons[nAnalyzing]    = LoadIconEx( IDI_ANALYZE2_14 );\r
144 \r
145         // [HGM] also make a table of handles to output controls\r
146         // Note that engineOutputDialog must be defined first!\r
147         outputField[0][nColorIcon] = GetDlgItem( engineOutputDialog, IDC_Color1 );\r
148         outputField[0][nLabel]     = GetDlgItem( engineOutputDialog, IDC_EngineLabel1 );\r
149         outputField[0][nStateIcon] = GetDlgItem( engineOutputDialog, IDC_StateIcon1 );\r
150         outputField[0][nStateData] = GetDlgItem( engineOutputDialog, IDC_StateData1 );\r
151         outputField[0][nLabelNPS]  = GetDlgItem( engineOutputDialog, IDC_Engine1_NPS );\r
152         outputField[0][nMemo]      = GetDlgItem( engineOutputDialog, IDC_EngineMemo1 );\r
153 \r
154         outputField[1][nColorIcon] = GetDlgItem( engineOutputDialog, IDC_Color2 );\r
155         outputField[1][nLabel]     = GetDlgItem( engineOutputDialog, IDC_EngineLabel2 );\r
156         outputField[1][nStateIcon] = GetDlgItem( engineOutputDialog, IDC_StateIcon2 );\r
157         outputField[1][nStateData] = GetDlgItem( engineOutputDialog, IDC_StateData2 );\r
158         outputField[1][nLabelNPS]  = GetDlgItem( engineOutputDialog, IDC_Engine2_NPS );\r
159         outputField[1][nMemo]      = GetDlgItem( engineOutputDialog, IDC_EngineMemo2 );\r
160 //        needInit = FALSE;\r
161 //    }\r
162 }\r
163 \r
164 // front end\r
165 static void SetControlPos( HWND hDlg, int id, int x, int y, int width, int height )\r
166 {\r
167     HWND hControl = GetDlgItem( hDlg, id );\r
168 \r
169     SetWindowPos( hControl, HWND_TOP, x, y, width, height, SWP_NOZORDER );\r
170 }\r
171 \r
172 #define HIDDEN_X    20000\r
173 #define HIDDEN_Y    20000\r
174 \r
175 // front end\r
176 static void HideControl( HWND hDlg, int id )\r
177 {\r
178     HWND hControl = GetDlgItem( hDlg, id );\r
179     RECT rc;\r
180 \r
181     GetWindowRect( hControl, &rc );\r
182 \r
183     /* \r
184         Avoid hiding an already hidden control, because that causes many\r
185         unnecessary WM_ERASEBKGND messages!\r
186     */\r
187     if( rc.left != HIDDEN_X || rc.top != HIDDEN_Y ) {\r
188         SetControlPos( hDlg, id, 20000, 20000, 100, 100 );\r
189     }\r
190 }\r
191 \r
192 // front end, although we might make GetWindowRect front end instead\r
193 static int GetControlWidth( HWND hDlg, int id )\r
194 {\r
195     RECT rc;\r
196 \r
197     GetWindowRect( GetDlgItem( hDlg, id ), &rc );\r
198 \r
199     return rc.right - rc.left;\r
200 }\r
201 \r
202 // front end?\r
203 static int GetControlHeight( HWND hDlg, int id )\r
204 {\r
205     RECT rc;\r
206 \r
207     GetWindowRect( GetDlgItem( hDlg, id ), &rc );\r
208 \r
209     return rc.bottom - rc.top;\r
210 }\r
211 \r
212 static int GetHeaderHeight()\r
213 {\r
214     int result = GetControlHeight( engineOutputDialog, IDC_EngineLabel1 );\r
215 \r
216     if( result < ICON_SIZE ) result = ICON_SIZE;\r
217 \r
218     return result;\r
219 }\r
220 \r
221 // The size calculations should be backend? If setControlPos is a platform-dependent way of doing things,\r
222 // a platform-independent wrapper for it should be supplied.\r
223 static void PositionControlSet( HWND hDlg, int x, int y, int clientWidth, int memoHeight, int idColor, int idEngineLabel, int idNPS, int idMemo, int idStateIcon, int idStateData )\r
224 {\r
225     int label_x = x + ICON_SIZE + H_MARGIN;\r
226     int label_h = GetControlHeight( hDlg, IDC_EngineLabel1 );\r
227     int label_y = y + ICON_SIZE - label_h;\r
228     int nps_w = GetControlWidth( hDlg, IDC_Engine1_NPS );\r
229     int nps_x = clientWidth - H_MARGIN - nps_w;\r
230     int state_data_w = GetControlWidth( hDlg, IDC_StateData1 );\r
231     int state_data_x = nps_x - H_MARGIN - state_data_w;\r
232     int state_icon_x = state_data_x - ICON_SIZE - 2;\r
233     int max_w = clientWidth - 2*H_MARGIN;\r
234     int memo_y = y + ICON_SIZE + LABEL_V_DISTANCE;\r
235 \r
236     SetControlPos( hDlg, idColor, x, y, ICON_SIZE, ICON_SIZE );\r
237     SetControlPos( hDlg, idEngineLabel, label_x, label_y, state_icon_x - label_x, label_h );\r
238     SetControlPos( hDlg, idStateIcon, state_icon_x, y, ICON_SIZE, ICON_SIZE );\r
239     SetControlPos( hDlg, idStateData, state_data_x, label_y, state_data_w, label_h );\r
240     SetControlPos( hDlg, idNPS, nps_x, label_y, nps_w, label_h );\r
241     SetControlPos( hDlg, idMemo, x, memo_y, max_w, memoHeight );\r
242 }\r
243 \r
244 // Also here some of the size calculations should go to the back end, and their actual application to a front-end routine\r
245 static void ResizeWindowControls( HWND hDlg, int mode )\r
246 {\r
247     RECT rc;\r
248     int headerHeight = GetHeaderHeight();\r
249     int labelHeight = GetControlHeight( hDlg, IDC_EngineLabel1 );\r
250     int labelOffset = H_MARGIN + ICON_SIZE + H_MARGIN;\r
251     int labelDeltaY = ICON_SIZE - labelHeight;\r
252     int clientWidth;\r
253     int clientHeight;\r
254     int maxControlWidth;\r
255     int npsWidth;\r
256 \r
257     /* Initialize variables */\r
258     GetClientRect( hDlg, &rc );\r
259 \r
260     clientWidth = rc.right - rc.left;\r
261     clientHeight = rc.bottom - rc.top;\r
262 \r
263     maxControlWidth = clientWidth - 2*H_MARGIN;\r
264 \r
265     npsWidth = GetControlWidth( hDlg, IDC_Engine1_NPS );\r
266 \r
267     /* Resize controls */\r
268     if( mode == 0 ) {\r
269         /* One engine */\r
270         PositionControlSet( hDlg, H_MARGIN, V_MARGIN, \r
271             clientWidth, \r
272             clientHeight - V_MARGIN - LABEL_V_DISTANCE - headerHeight- V_MARGIN,\r
273             IDC_Color1, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineMemo1, IDC_StateIcon1, IDC_StateData1 );\r
274 \r
275         /* Hide controls for the second engine */\r
276         HideControl( hDlg, IDC_Color2 );\r
277         HideControl( hDlg, IDC_EngineLabel2 );\r
278         HideControl( hDlg, IDC_StateIcon2 );\r
279         HideControl( hDlg, IDC_StateData2 );\r
280         HideControl( hDlg, IDC_Engine2_NPS );\r
281         HideControl( hDlg, IDC_EngineMemo2 );\r
282         SendDlgItemMessage( hDlg, IDC_EngineMemo2, WM_SETTEXT, 0, (LPARAM) "" );\r
283         /* TODO: we should also hide/disable them!!! what about tab stops?!?! */\r
284     }\r
285     else {\r
286         /* Two engines */\r
287         int memo_h = (clientHeight - headerHeight*2 - V_MARGIN*2 - LABEL_V_DISTANCE*2 - SPLITTER_SIZE) / 2;\r
288         int header1_y = V_MARGIN;\r
289         int header2_y = V_MARGIN + headerHeight + LABEL_V_DISTANCE + memo_h + SPLITTER_SIZE;\r
290 \r
291         PositionControlSet( hDlg, H_MARGIN, header1_y, clientWidth, memo_h,\r
292             IDC_Color1, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineMemo1, IDC_StateIcon1, IDC_StateData1 );\r
293 \r
294         PositionControlSet( hDlg, H_MARGIN, header2_y, clientWidth, memo_h,\r
295             IDC_Color2, IDC_EngineLabel2, IDC_Engine2_NPS, IDC_EngineMemo2, IDC_StateIcon2, IDC_StateData2 );\r
296     }\r
297 \r
298     InvalidateRect( GetDlgItem(hDlg,IDC_EngineMemo1), NULL, FALSE );\r
299     InvalidateRect( GetDlgItem(hDlg,IDC_EngineMemo2), NULL, FALSE );\r
300 }\r
301 \r
302 // front end. Actual printing of PV lines into the output field\r
303 static void InsertIntoMemo( int which, char * text )\r
304 {\r
305     SendMessage( outputField[which][nMemo], EM_SETSEL, 0, 0 );\r
306 \r
307     SendMessage( outputField[which][nMemo], EM_REPLACESEL, (WPARAM) FALSE, (LPARAM) text );\r
308 }\r
309 \r
310 // front end. Associates an icon with an output field ("control" in Windows jargon).\r
311 // [HGM] let it find out the output field from the 'which' number by itself\r
312 static void SetIcon( int which, int field, int nIcon )\r
313 {\r
314 \r
315     if( nIcon != 0 ) {\r
316         SendMessage( outputField[which][field], STM_SETICON, (WPARAM) icons[nIcon], 0 );\r
317     }\r
318 }\r
319 \r
320 // front end wrapper for SetWindowText, taking control number in stead of handle\r
321 void DoSetWindowText(int which, int field, char *s_label)\r
322 {\r
323     SetWindowText( outputField[which][field], s_label );\r
324 }\r
325 \r
326 // This seems pure front end\r
327 LRESULT CALLBACK EngineOutputProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )\r
328 {\r
329     static SnapData sd;\r
330 \r
331     switch (message) {\r
332     case WM_INITDIALOG:\r
333         if( engineOutputDialog == NULL ) {\r
334             engineOutputDialog = hDlg;\r
335 \r
336             RestoreWindowPlacement( hDlg, &wpEngineOutput ); /* Restore window placement */\r
337 \r
338             ResizeWindowControls( hDlg, windowMode );\r
339 \r
340             SetEngineState( 0, STATE_IDLE, "" );\r
341             SetEngineState( 1, STATE_IDLE, "" );\r
342         }\r
343 \r
344         return FALSE;\r
345 \r
346     case WM_COMMAND:\r
347         switch (LOWORD(wParam)) {\r
348         case IDOK:\r
349           EndDialog(hDlg, TRUE);\r
350           return TRUE;\r
351 \r
352         case IDCANCEL:\r
353           EndDialog(hDlg, FALSE);\r
354           return TRUE;\r
355 \r
356         default:\r
357           break;\r
358         }\r
359 \r
360         break;\r
361 \r
362     case WM_GETMINMAXINFO:\r
363         {\r
364             MINMAXINFO * mmi = (MINMAXINFO *) lParam;\r
365         \r
366             mmi->ptMinTrackSize.x = 100;\r
367             mmi->ptMinTrackSize.y = 160;\r
368         }\r
369         break;\r
370 \r
371     case WM_CLOSE:\r
372         EngineOutputPopDown();\r
373         break;\r
374 \r
375     case WM_SIZE:\r
376         ResizeWindowControls( hDlg, windowMode );\r
377         break;\r
378 \r
379     case WM_ENTERSIZEMOVE:\r
380         return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
381 \r
382     case WM_SIZING:\r
383         return OnSizing( &sd, hDlg, wParam, lParam );\r
384 \r
385     case WM_MOVING:\r
386         return OnMoving( &sd, hDlg, wParam, lParam );\r
387 \r
388     case WM_EXITSIZEMOVE:\r
389         return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
390     }\r
391 \r
392     return FALSE;\r
393 }\r
394 \r
395 // front end\r
396 void EngineOutputPopUp()\r
397 {\r
398   FARPROC lpProc;\r
399   \r
400   CheckMenuItem(GetMenu(hwndMain), IDM_ShowEngineOutput, MF_CHECKED);\r
401 \r
402   if( engineOutputDialog ) {\r
403     SendMessage( engineOutputDialog, WM_INITDIALOG, 0, 0 );\r
404 \r
405     if( ! engineOutputDialogUp ) {\r
406         ShowWindow(engineOutputDialog, SW_SHOW);\r
407     }\r
408   }\r
409   else {\r
410     lpProc = MakeProcInstance( (FARPROC) EngineOutputProc, hInst );\r
411 \r
412     /* Note to self: dialog must have the WS_VISIBLE style set, otherwise it's not shown! */\r
413     CreateDialog( hInst, MAKEINTRESOURCE(DLG_EngineOutput), hwndMain, (DLGPROC)lpProc );\r
414 \r
415     FreeProcInstance(lpProc);\r
416   }\r
417 \r
418   // [HGM] displaced to after creation of dialog, to allow initialization of output fields\r
419   if( needInit ) {\r
420       InitializeEngineOutput();\r
421       needInit = FALSE;\r
422   }\r
423 \r
424   engineOutputDialogUp = TRUE;\r
425 }\r
426 \r
427 // front end\r
428 void EngineOutputPopDown()\r
429 {\r
430   CheckMenuItem(GetMenu(hwndMain), IDM_ShowEngineOutput, MF_UNCHECKED);\r
431 \r
432   if( engineOutputDialog ) {\r
433       ShowWindow(engineOutputDialog, SW_HIDE);\r
434   }\r
435 \r
436   engineOutputDialogUp = FALSE;\r
437 }\r
438 \r
439 // front end. [HGM] Takes handle of output control from table, so only number is passed\r
440 void DoClearMemo(int which)\r
441 {\r
442         SendMessage( outputField[which][nMemo], WM_SETTEXT, 0, (LPARAM) "" );\r
443 }\r
444 \r
445 //------------------------ pure back-end routines -------------------------------\r
446 \r
447 \r
448 // back end, due to front-end wrapper for SetWindowText, and new SetIcon arguments\r
449 static SetEngineState( int which, int state, char * state_data )\r
450 {\r
451     int x_which = 1 - which;\r
452 \r
453     if( engineState[ which ] != state ) {\r
454         engineState[ which ] = state;\r
455 \r
456         switch( state ) {\r
457         case STATE_THINKING:\r
458             SetIcon( which, nStateIcon, nThinking );\r
459             if( engineState[ x_which ] == STATE_THINKING ) {\r
460                 SetEngineState( x_which, STATE_IDLE, "" );\r
461             }\r
462             break;\r
463         case STATE_PONDERING:\r
464             SetIcon( which, nStateIcon, nPondering );\r
465             break;\r
466         case STATE_ANALYZING:\r
467             SetIcon( which, nStateIcon, nAnalyzing );\r
468             break;\r
469         default:\r
470             SetIcon( which, nStateIcon, nClear );\r
471             break;\r
472         }\r
473     }\r
474 \r
475     if( state_data != 0 ) {\r
476         DoSetWindowText( which, nStateData, state_data );\r
477     }\r
478 }\r
479 \r
480 // back end, now the front-end wrapper ClearMemo is used, and ed no longer contains handles.\r
481 void EngineOutputUpdate( FrontEndProgramStats * stats )\r
482 {\r
483     EngineOutputData ed;\r
484     int clearMemo = FALSE;\r
485     int which;\r
486     int depth;\r
487 \r
488     if( stats == 0 ) {\r
489         SetEngineState( 0, STATE_IDLE, "" );\r
490         SetEngineState( 1, STATE_IDLE, "" );\r
491         return;\r
492     }\r
493 \r
494     if(gameMode == IcsObserving && !appData.icsEngineAnalyze)\r
495         return; // [HGM] kibitz: shut up engine if we are observing an ICS game\r
496 \r
497     which = stats->which;\r
498     depth = stats->depth;\r
499 \r
500     if( which < 0 || which > 1 || depth < 0 || stats->time < 0 || stats->pv == 0 ) {\r
501         return;\r
502     }\r
503 \r
504     if( engineOutputDialog == NULL ) {\r
505         return;\r
506     }\r
507 \r
508     VerifyDisplayMode();\r
509 \r
510     ed.which = which;\r
511     ed.depth = depth;\r
512     ed.nodes = stats->nodes;\r
513     ed.score = stats->score;\r
514     ed.time = stats->time;\r
515     ed.pv = stats->pv;\r
516     ed.hint = stats->hint;\r
517     ed.an_move_index = stats->an_move_index;\r
518     ed.an_move_count = stats->an_move_count;\r
519 \r
520     /* Get target control. [HGM] this is moved to front end, which get them from a table */\r
521     if( which == 0 ) {\r
522         ed.name = first.tidy;\r
523     }\r
524     else {\r
525         ed.name = second.tidy;\r
526     }\r
527 \r
528     /* Clear memo if needed */\r
529     if( lastDepth[which] > depth || (lastDepth[which] == depth && depth <= 1) ) {\r
530         clearMemo = TRUE;\r
531     }\r
532 \r
533     if( lastForwardMostMove[which] != forwardMostMove ) {\r
534         clearMemo = TRUE;\r
535     }\r
536 \r
537     if( clearMemo ) DoClearMemo(which);\r
538 \r
539     /* Update */\r
540     lastDepth[which] = depth;\r
541     lastForwardMostMove[which] = forwardMostMove;\r
542 \r
543     if( ed.pv != 0 && ed.pv[0] == ' ' ) {\r
544         if( strncmp( ed.pv, " no PV", 6 ) == 0 ) { /* Hack on hack! :-O */\r
545             ed.pv = "";\r
546         }\r
547     }\r
548 \r
549     UpdateControls( &ed );\r
550 }\r
551 \r
552 #define ENGINE_COLOR_WHITE      'w'\r
553 #define ENGINE_COLOR_BLACK      'b'\r
554 #define ENGINE_COLOR_UNKNOWN    ' '\r
555 \r
556 // pure back end\r
557 char GetEngineColor( int which )\r
558 {\r
559     char result = ENGINE_COLOR_UNKNOWN;\r
560 \r
561     if( which == 0 || which == 1 ) {\r
562         ChessProgramState * cps;\r
563 \r
564         switch (gameMode) {\r
565         case MachinePlaysBlack:\r
566         case IcsPlayingBlack:\r
567             result = ENGINE_COLOR_BLACK;\r
568             break;\r
569         case MachinePlaysWhite:\r
570         case IcsPlayingWhite:\r
571             result = ENGINE_COLOR_WHITE;\r
572             break;\r
573         case AnalyzeMode:\r
574         case AnalyzeFile:\r
575             result = WhiteOnMove(forwardMostMove) ? ENGINE_COLOR_WHITE : ENGINE_COLOR_BLACK;\r
576             break;\r
577         case TwoMachinesPlay:\r
578             cps = (which == 0) ? &first : &second;\r
579             result = cps->twoMachinesColor[0];\r
580             result = result == 'w' ? ENGINE_COLOR_WHITE : ENGINE_COLOR_BLACK;\r
581             break;\r
582         }\r
583     }\r
584 \r
585     return result;\r
586 }\r
587 \r
588 // pure back end\r
589 char GetActiveEngineColor()\r
590 {\r
591     char result = ENGINE_COLOR_UNKNOWN;\r
592 \r
593     if( gameMode == TwoMachinesPlay ) {\r
594         result = WhiteOnMove(forwardMostMove) ? ENGINE_COLOR_WHITE : ENGINE_COLOR_BLACK;\r
595     }\r
596 \r
597     return result;\r
598 }\r
599 \r
600 // pure back end\r
601 static int IsEnginePondering( int which )\r
602 {\r
603     int result = FALSE;\r
604 \r
605     switch (gameMode) {\r
606     case MachinePlaysBlack:\r
607     case IcsPlayingBlack:\r
608         if( WhiteOnMove(forwardMostMove) ) result = TRUE;\r
609         break;\r
610     case MachinePlaysWhite:\r
611     case IcsPlayingWhite:\r
612         if( ! WhiteOnMove(forwardMostMove) ) result = TRUE;\r
613         break;\r
614     case TwoMachinesPlay:\r
615         if( GetActiveEngineColor() != ENGINE_COLOR_UNKNOWN ) {\r
616             if( GetEngineColor( which ) != GetActiveEngineColor() ) result = TRUE;\r
617         }\r
618         break;\r
619     }\r
620 \r
621     return result;\r
622 }\r
623 \r
624 // back end\r
625 static void SetDisplayMode( int mode )\r
626 {\r
627     if( windowMode != mode ) {\r
628         windowMode = mode;\r
629 \r
630         ResizeWindowControls( engineOutputDialog, mode );\r
631     }\r
632 }\r
633 \r
634 // pure back end\r
635 static VerifyDisplayMode()\r
636 {\r
637     int mode;\r
638 \r
639     /* Get proper mode for current game */\r
640     switch( gameMode ) {\r
641     case IcsObserving:    // [HGM] ICS analyze\r
642         if(!appData.icsEngineAnalyze) return;\r
643     case AnalyzeMode:\r
644     case AnalyzeFile:\r
645     case MachinePlaysWhite:\r
646     case MachinePlaysBlack:\r
647         mode = 0;\r
648         break;\r
649     case IcsPlayingWhite:\r
650     case IcsPlayingBlack:\r
651         mode = appData.zippyPlay && opponentKibitzes; // [HGM] kibitz\r
652         break;\r
653     case TwoMachinesPlay:\r
654         mode = 1;\r
655         break;\r
656     default:\r
657         /* Do not change */\r
658         return;\r
659     }\r
660 \r
661     SetDisplayMode( mode );\r
662 }\r
663 \r
664 // back end. Determine what icon to se in the color-icon field, and print it\r
665 static void SetEngineColorIcon( int which )\r
666 {\r
667     char color = GetEngineColor(which);\r
668     int nicon = 0;\r
669 \r
670     if( color == ENGINE_COLOR_BLACK )\r
671         nicon = nColorBlack;\r
672     else if( color == ENGINE_COLOR_WHITE )\r
673         nicon = nColorWhite;\r
674     else\r
675         nicon = nColorUnknown;\r
676 \r
677     SetIcon( which, nColorIcon, nicon );\r
678 }\r
679 \r
680 #define MAX_NAME_LENGTH 32\r
681 \r
682 // pure back end, now SetWindowText is called via wrapper DoSetWindowText\r
683 static void UpdateControls( EngineOutputData * ed )\r
684 {\r
685     int isPondering = FALSE;\r
686 \r
687     char s_label[MAX_NAME_LENGTH + 32];\r
688     \r
689     char * name = ed->name;\r
690 \r
691     /* Label */\r
692     if( name == 0 || *name == '\0' ) {\r
693         name = "?";\r
694     }\r
695 \r
696     strncpy( s_label, name, MAX_NAME_LENGTH );\r
697     s_label[ MAX_NAME_LENGTH-1 ] = '\0';\r
698 \r
699 #ifdef SHOW_PONDERING\r
700     if( IsEnginePondering( ed->which ) ) {\r
701         char buf[8];\r
702 \r
703         buf[0] = '\0';\r
704 \r
705         if( ed->hint != 0 && *ed->hint != '\0' ) {\r
706             strncpy( buf, ed->hint, sizeof(buf) );\r
707             buf[sizeof(buf)-1] = '\0';\r
708         }\r
709         else if( ed->pv != 0 && *ed->pv != '\0' ) {\r
710             char * sep = strchr( ed->pv, ' ' );\r
711             int buflen = sizeof(buf);\r
712 \r
713             if( sep != NULL ) {\r
714                 buflen = sep - ed->pv + 1;\r
715                 if( buflen > sizeof(buf) ) buflen = sizeof(buf);\r
716             }\r
717 \r
718             strncpy( buf, ed->pv, buflen );\r
719             buf[ buflen-1 ] = '\0';\r
720         }\r
721 \r
722         SetEngineState( ed->which, STATE_PONDERING, buf );\r
723     }\r
724     else if( gameMode == TwoMachinesPlay ) {\r
725         SetEngineState( ed->which, STATE_THINKING, "" );\r
726     }\r
727     else if( gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
728           || gameMode == IcsObserving && appData.icsEngineAnalyze) { // [HGM] ICS-analyze\r
729         char buf[64];\r
730         int time_secs = ed->time / 100;\r
731         int time_mins = time_secs / 60;\r
732 \r
733         buf[0] = '\0';\r
734 \r
735         if( ed->an_move_index != 0 && ed->an_move_count != 0 && *ed->hint != '\0' ) {\r
736             char mov[16];\r
737 \r
738             strncpy( mov, ed->hint, sizeof(mov) );\r
739             mov[ sizeof(mov)-1 ] = '\0';\r
740 \r
741             sprintf( buf, "%d/%d: %s [%02d:%02d:%02d]", ed->an_move_index, ed->an_move_count, mov, time_mins / 60, time_mins % 60, time_secs % 60 );\r
742         }\r
743 \r
744         SetEngineState( ed->which, STATE_ANALYZING, buf );\r
745     }\r
746     else {\r
747         SetEngineState( ed->which, STATE_IDLE, "" );\r
748     }\r
749 #endif\r
750 \r
751     DoSetWindowText( ed->which, nLabel, s_label );\r
752 \r
753     s_label[0] = '\0';\r
754 \r
755     if( ed->time > 0 && ed->nodes > 0 ) {\r
756         unsigned long nps_100 = ed->nodes / ed->time;\r
757 \r
758         if( nps_100 < 100000 ) {\r
759             sprintf( s_label, "NPS: %lu", nps_100 * 100 );\r
760         }\r
761         else {\r
762             sprintf( s_label, "NPS: %.1fk", nps_100 / 10.0 );\r
763         }\r
764     }\r
765 \r
766     DoSetWindowText( ed->which, nLabelNPS, s_label );\r
767 \r
768     /* Memo */\r
769     if( ed->pv != 0 && *ed->pv != '\0' ) {\r
770         char s_nodes[24];\r
771         char s_score[16];\r
772         char s_time[24];\r
773         char buf[256];\r
774         int buflen;\r
775         int time_secs = ed->time / 100;\r
776         int time_cent = ed->time % 100;\r
777 \r
778         /* Nodes */\r
779         if( ed->nodes < 1000000 ) {\r
780             sprintf( s_nodes, u64Display, ed->nodes );\r
781         }\r
782         else {\r
783             sprintf( s_nodes, "%.1fM", u64ToDouble(ed->nodes) / 1000000.0 );\r
784         }\r
785 \r
786         /* Score */\r
787         if( ed->score > 0 ) {\r
788             sprintf( s_score, "+%.2f", ed->score / 100.0 );\r
789         }\r
790         else {\r
791             sprintf( s_score, "%.2f", ed->score / 100.0 );\r
792         }\r
793 \r
794         /* Time */\r
795         sprintf( s_time, "%d:%02d.%02d", time_secs / 60, time_secs % 60, time_cent );\r
796 \r
797         /* Put all together... */\r
798         sprintf( buf, "%3d\t%s\t%s\t%s\t", ed->depth, s_score, s_nodes, s_time );\r
799 \r
800         /* Add PV */\r
801         buflen = strlen(buf);\r
802 \r
803         strncpy( buf + buflen, ed->pv, sizeof(buf) - buflen );\r
804 \r
805         buf[ sizeof(buf) - 3 ] = '\0';\r
806 \r
807         strcat( buf + buflen, "\r\n" );\r
808 \r
809         /* Update memo */\r
810         InsertIntoMemo( ed->which, buf );\r
811     }\r
812 \r
813     /* Colors */\r
814     SetEngineColorIcon( ed->which );\r
815 }\r
816 \r
817 // back end\r
818 int EngineOutputIsUp()\r
819 {\r
820     return engineOutputDialogUp;\r
821 }\r
822 \r
823 // [HGM] kibitz: write kibitz line; split window for it if necessary\r
824 void OutputKibitz(int window, char *text)\r
825 {\r
826         if(!EngineOutputIsUp()) return;\r
827         if(!opponentKibitzes) { // on first kibitz of game, clear memos\r
828             DoClearMemo(1);\r
829             if(gameMode == IcsObserving) DoClearMemo(0);\r
830         }\r
831         opponentKibitzes = TRUE; // this causes split window DisplayMode in ICS modes.\r
832         VerifyDisplayMode();\r
833         if(gameMode == IcsObserving) {\r
834             DoSetWindowText(0, nLabel, gameInfo.white);\r
835             SetIcon( 0, nColorIcon,  nColorWhite);\r
836             SetIcon( 0, nStateIcon,  nClear);\r
837         }\r
838         DoSetWindowText(1, nLabel, gameMode == IcsPlayingBlack ? gameInfo.white : gameInfo.black); // opponent name\r
839         SetIcon( 1, nColorIcon,  gameMode == IcsPlayingBlack ? nColorWhite : nColorBlack);\r
840         SetIcon( 1, nStateIcon,  nClear);\r
841         InsertIntoMemo(window-1, text);\r
842 }\r