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