0eb75364cdf63fdbdf49ce2bf0bdfebcdbe98d16
[xboard.git] / winboard / wengineoutput.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 VOID EngineOutputPopUp();
40 VOID EngineOutputPopDown();
41 BOOL EngineOutputIsUp();
42
43 #define SHOW_PONDERING
44
45 /* Imports from backend.c */
46 char * SavePart(char *str);
47
48 /* Imports from winboard.c */
49 extern HWND engineOutputDialog;
50 extern BOOLEAN engineOutputDialogUp;
51
52 extern HINSTANCE hInst;
53 extern HWND hwndMain;
54
55 extern WindowPlacement wpEngineOutput;
56
57 /* Module variables */
58 #define H_MARGIN            2
59 #define V_MARGIN            2
60 #define LABEL_V_DISTANCE    1   /* Distance between label and memo */
61 #define SPLITTER_SIZE       4   /* Distance between first memo and second label */
62
63 #define ICON_SIZE           14
64
65 #define STATE_UNKNOWN   -1
66 #define STATE_THINKING   0
67 #define STATE_IDLE       1
68 #define STATE_PONDERING  2
69 #define STATE_ANALYZING  3
70
71 static int  windowMode = 1;
72
73 static BOOL needInit = TRUE;
74
75 static HICON hiColorBlack = NULL;
76 static HICON hiColorWhite = NULL;
77 static HICON hiColorUnknown = NULL;
78 static HICON hiClear = NULL;
79 static HICON hiPondering = NULL;
80 static HICON hiThinking = NULL;
81 static HICON hiAnalyzing = NULL;
82
83 static int  lastDepth[2] = { -1, -1 };
84 static int  lastForwardMostMove[2] = { -1, -1 };
85 static int  engineState[2] = { -1, -1 };
86
87 typedef struct {
88     HWND hColorIcon;
89     HWND hLabel;
90     HWND hStateIcon;
91     HWND hStateData;
92     HWND hLabelNPS;
93     HWND hMemo;
94     char * name;
95     int which;
96     int depth;
97     unsigned long nodes;
98     int score;
99     int time;
100     char * pv;
101     char * hint;
102     int an_move_index;
103     int an_move_count;
104 } EngineOutputData;
105
106 static HICON LoadIconEx( int id )
107 {
108     return LoadImage( hInst, MAKEINTRESOURCE(id), IMAGE_ICON, ICON_SIZE, ICON_SIZE, 0 );
109 }
110
111 static VOID InitializeEngineOutput()
112 {
113     if( needInit ) {
114         hiColorBlack = LoadIconEx( IDI_BLACK_14 );
115         hiColorWhite = LoadIconEx( IDI_WHITE_14 );
116         hiColorUnknown = LoadIconEx( IDI_UNKNOWN_14 );
117         hiClear = LoadIconEx( IDI_TRANS_14 );
118         hiPondering = LoadIconEx( IDI_PONDER_14 );
119         hiThinking = LoadIconEx( IDI_CLOCK_14 );
120         hiAnalyzing = LoadIconEx( IDI_ANALYZE2_14 );
121         needInit = FALSE;
122     }
123 }
124
125 static VOID SetControlPos( HWND hDlg, int id, int x, int y, int width, int height )
126 {
127     HWND hControl = GetDlgItem( hDlg, id );
128
129     SetWindowPos( hControl, HWND_TOP, x, y, width, height, SWP_NOZORDER );
130 }
131
132 #define HIDDEN_X    20000
133 #define HIDDEN_Y    20000
134
135 static VOID HideControl( HWND hDlg, int id )
136 {
137     HWND hControl = GetDlgItem( hDlg, id );
138     RECT rc;
139
140     GetWindowRect( hControl, &rc );
141
142     /*
143         Avoid hiding an already hidden control, because that causes many
144         unnecessary WM_ERASEBKGND messages!
145     */
146     if( rc.left != HIDDEN_X || rc.top != HIDDEN_Y ) {
147     SetControlPos( hDlg, id, 20000, 20000, 100, 100 );
148     }
149 }
150
151 static int GetControlWidth( HWND hDlg, int id )
152 {
153     RECT rc;
154
155     GetWindowRect( GetDlgItem( hDlg, id ), &rc );
156
157     return rc.right - rc.left;
158 }
159
160 static int GetControlHeight( HWND hDlg, int id )
161 {
162     RECT rc;
163
164     GetWindowRect( GetDlgItem( hDlg, id ), &rc );
165
166     return rc.bottom - rc.top;
167 }
168
169 static int GetHeaderHeight()
170 {
171     int result = GetControlHeight( engineOutputDialog, IDC_EngineLabel1 );
172
173     if( result < ICON_SIZE ) result = ICON_SIZE;
174
175     return result;
176 }
177
178 #define ENGINE_COLOR_WHITE      'w'
179 #define ENGINE_COLOR_BLACK      'b'
180 #define ENGINE_COLOR_UNKNOWN    ' '
181
182 char GetEngineColor( int which )
183 {
184     char result = ENGINE_COLOR_UNKNOWN;
185
186     if( which == 0 || which == 1 ) {
187         ChessProgramState * cps;
188
189         switch (gameMode) {
190         case MachinePlaysBlack:
191         case IcsPlayingBlack:
192             result = ENGINE_COLOR_BLACK;
193             break;
194         case MachinePlaysWhite:
195         case IcsPlayingWhite:
196             result = ENGINE_COLOR_WHITE;
197             break;
198         case AnalyzeMode:
199         case AnalyzeFile:
200             result = WhiteOnMove(forwardMostMove) ? ENGINE_COLOR_WHITE : ENGINE_COLOR_BLACK;
201             break;
202         case TwoMachinesPlay:
203             cps = (which == 0) ? &first : &second;
204             result = cps->twoMachinesColor[0];
205             result = result == 'w' ? ENGINE_COLOR_WHITE : ENGINE_COLOR_BLACK;
206             break;
207         }
208     }
209
210     return result;
211 }
212
213 char GetActiveEngineColor()
214 {
215     char result = ENGINE_COLOR_UNKNOWN;
216
217     if( gameMode == TwoMachinesPlay ) {
218         result = WhiteOnMove(forwardMostMove) ? ENGINE_COLOR_WHITE : ENGINE_COLOR_BLACK;
219     }
220
221     return result;
222 }
223
224 static int IsEnginePondering( int which )
225 {
226     int result = FALSE;
227
228     switch (gameMode) {
229     case MachinePlaysBlack:
230     case IcsPlayingBlack:
231         if( WhiteOnMove(forwardMostMove) ) result = TRUE;
232         break;
233     case MachinePlaysWhite:
234     case IcsPlayingWhite:
235         if( ! WhiteOnMove(forwardMostMove) ) result = TRUE;
236         break;
237     case TwoMachinesPlay:
238         if( GetActiveEngineColor() != ENGINE_COLOR_UNKNOWN ) {
239             if( GetEngineColor( which ) != GetActiveEngineColor() ) result = TRUE;
240         }
241         break;
242     }
243
244     return result;
245 }
246
247 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 )
248 {
249     int label_x = x + ICON_SIZE + H_MARGIN;
250     int label_h = GetControlHeight( hDlg, IDC_EngineLabel1 );
251     int label_y = y + ICON_SIZE - label_h;
252     int nps_w = GetControlWidth( hDlg, IDC_Engine1_NPS );
253     int nps_x = clientWidth - H_MARGIN - nps_w;
254     int state_data_w = GetControlWidth( hDlg, IDC_StateData1 );
255     int state_data_x = nps_x - H_MARGIN - state_data_w;
256     int state_icon_x = state_data_x - ICON_SIZE - 2;
257     int max_w = clientWidth - 2*H_MARGIN;
258     int memo_y = y + ICON_SIZE + LABEL_V_DISTANCE;
259
260     SetControlPos( hDlg, idColor, x, y, ICON_SIZE, ICON_SIZE );
261     SetControlPos( hDlg, idEngineLabel, label_x, label_y, state_icon_x - label_x, label_h );
262     SetControlPos( hDlg, idStateIcon, state_icon_x, y, ICON_SIZE, ICON_SIZE );
263     SetControlPos( hDlg, idStateData, state_data_x, label_y, state_data_w, label_h );
264     SetControlPos( hDlg, idNPS, nps_x, label_y, nps_w, label_h );
265     SetControlPos( hDlg, idMemo, x, memo_y, max_w, memoHeight );
266 }
267
268 static VOID ResizeWindowControls( HWND hDlg, int mode )
269 {
270     RECT rc;
271     int headerHeight = GetHeaderHeight();
272     int labelHeight = GetControlHeight( hDlg, IDC_EngineLabel1 );
273     int labelOffset = H_MARGIN + ICON_SIZE + H_MARGIN;
274     int labelDeltaY = ICON_SIZE - labelHeight;
275     int clientWidth;
276     int clientHeight;
277     int maxControlWidth;
278     int npsWidth;
279
280     /* Initialize variables */
281     GetClientRect( hDlg, &rc );
282
283     clientWidth = rc.right - rc.left;
284     clientHeight = rc.bottom - rc.top;
285
286     maxControlWidth = clientWidth - 2*H_MARGIN;
287
288     npsWidth = GetControlWidth( hDlg, IDC_Engine1_NPS );
289
290     /* Resize controls */
291     if( mode == 0 ) {
292         /* One engine */
293         PositionControlSet( hDlg, H_MARGIN, V_MARGIN,
294             clientWidth,
295             clientHeight - V_MARGIN - LABEL_V_DISTANCE - headerHeight- V_MARGIN,
296             IDC_Color1, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineMemo1, IDC_StateIcon1, IDC_StateData1 );
297
298         /* Hide controls for the second engine */
299         HideControl( hDlg, IDC_Color2 );
300         HideControl( hDlg, IDC_EngineLabel2 );
301         HideControl( hDlg, IDC_StateIcon2 );
302         HideControl( hDlg, IDC_StateData2 );
303         HideControl( hDlg, IDC_Engine2_NPS );
304         HideControl( hDlg, IDC_EngineMemo2 );
305         SendDlgItemMessage( hDlg, IDC_EngineMemo2, WM_SETTEXT, 0, (LPARAM) "" );
306         /* TODO: we should also hide/disable them!!! what about tab stops?!?! */
307     }
308     else {
309         /* Two engines */
310         int memo_h = (clientHeight - headerHeight*2 - V_MARGIN*2 - LABEL_V_DISTANCE*2 - SPLITTER_SIZE) / 2;
311         int header1_y = V_MARGIN;
312         int header2_y = V_MARGIN + headerHeight + LABEL_V_DISTANCE + memo_h + SPLITTER_SIZE;
313
314         PositionControlSet( hDlg, H_MARGIN, header1_y, clientWidth, memo_h,
315             IDC_Color1, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineMemo1, IDC_StateIcon1, IDC_StateData1 );
316
317         PositionControlSet( hDlg, H_MARGIN, header2_y, clientWidth, memo_h,
318             IDC_Color2, IDC_EngineLabel2, IDC_Engine2_NPS, IDC_EngineMemo2, IDC_StateIcon2, IDC_StateData2 );
319     }
320
321     InvalidateRect( GetDlgItem(hDlg,IDC_EngineMemo1), NULL, FALSE );
322     InvalidateRect( GetDlgItem(hDlg,IDC_EngineMemo2), NULL, FALSE );
323 }
324
325 static VOID SetDisplayMode( int mode )
326 {
327     if( windowMode != mode ) {
328         windowMode = mode;
329
330         ResizeWindowControls( engineOutputDialog, mode );
331     }
332 }
333
334 static VOID VerifyDisplayMode()
335 {
336     int mode;
337
338     /* Get proper mode for current game */
339     switch( gameMode ) {
340     case AnalyzeMode:
341     case AnalyzeFile:
342     case MachinePlaysWhite:
343     case MachinePlaysBlack:
344     case IcsPlayingWhite:
345     case IcsPlayingBlack:
346         mode = 0;
347         break;
348     case TwoMachinesPlay:
349         mode = 1;
350         break;
351     default:
352         /* Do not change */
353         return;
354     }
355
356     SetDisplayMode( mode );
357 }
358
359 static VOID InsertIntoMemo( HWND hMemo, char * text )
360 {
361     SendMessage( hMemo, EM_SETSEL, 0, 0 );
362
363     SendMessage( hMemo, EM_REPLACESEL, (WPARAM) FALSE, (LPARAM) text );
364 }
365
366 static VOID SetIcon( HWND hControl, HICON hIcon )
367 {
368     if( hIcon != NULL ) {
369         SendMessage( hControl, STM_SETICON, (WPARAM) hIcon, 0 );
370     }
371 }
372
373 static VOID SetEngineColorIcon( HWND hControl, int which )
374 {
375     char color = GetEngineColor(which);
376     HICON hicon = NULL;
377
378     if( color == ENGINE_COLOR_BLACK )
379         hicon = hiColorBlack;
380     else if( color == ENGINE_COLOR_WHITE )
381         hicon = hiColorWhite;
382     else
383         hicon = hiColorUnknown;
384
385     SetIcon( hControl, hicon );
386 }
387
388 static SetEngineState( int which, int state, char * state_data )
389 {
390     int x_which = 1 - which;
391     HWND hStateIcon = GetDlgItem( engineOutputDialog, which == 0 ? IDC_StateIcon1 : IDC_StateIcon2 );
392     HWND hStateData = GetDlgItem( engineOutputDialog, which == 0 ? IDC_StateData1 : IDC_StateData2 );
393
394     if( engineState[ which ] != state ) {
395         engineState[ which ] = state;
396
397         switch( state ) {
398         case STATE_THINKING:
399             SetIcon( hStateIcon, hiThinking );
400             if( engineState[ x_which ] == STATE_THINKING ) {
401                 SetEngineState( x_which, STATE_IDLE, "" );
402             }
403             break;
404         case STATE_PONDERING:
405             SetIcon( hStateIcon, hiPondering );
406             break;
407         case STATE_ANALYZING:
408             SetIcon( hStateIcon, hiAnalyzing );
409             break;
410         default:
411             SetIcon( hStateIcon, hiClear );
412             break;
413         }
414     }
415
416     if( state_data != 0 ) {
417         SetWindowText( hStateData, state_data );
418     }
419 }
420
421 #define MAX_NAME_LENGTH 32
422
423 static VOID UpdateControls( EngineOutputData * ed )
424 {
425     BOOL isPondering = FALSE;
426
427     char s_label[MAX_NAME_LENGTH + 32];
428
429     char * name = ed->name;
430
431     /* Label */
432     if( name == 0 || *name == '\0' ) {
433         name = "?";
434     }
435
436     strncpy( s_label, name, MAX_NAME_LENGTH );
437     s_label[ MAX_NAME_LENGTH-1 ] = '\0';
438
439 #ifdef SHOW_PONDERING
440     if( IsEnginePondering( ed->which ) ) {
441             char buf[8];
442
443             buf[0] = '\0';
444
445             if( ed->hint != 0 && *ed->hint != '\0' ) {
446                 strncpy( buf, ed->hint, sizeof(buf) );
447                 buf[sizeof(buf)-1] = '\0';
448             }
449             else if( ed->pv != 0 && *ed->pv != '\0' ) {
450                 char * sep = strchr( ed->pv, ' ' );
451                 int buflen = sizeof(buf);
452
453                 if( sep != NULL ) {
454                     buflen = sep - ed->pv + 1;
455                     if( buflen > sizeof(buf) ) buflen = sizeof(buf);
456                 }
457
458                 strncpy( buf, ed->pv, buflen );
459                 buf[ buflen-1 ] = '\0';
460             }
461
462             SetEngineState( ed->which, STATE_PONDERING, buf );
463         }
464     else if( gameMode == TwoMachinesPlay ) {
465             SetEngineState( ed->which, STATE_THINKING, "" );
466         }
467     else if( gameMode == AnalyzeMode || gameMode == AnalyzeFile ) {
468         char buf[64];
469         int time_secs = ed->time / 100;
470         int time_mins = time_secs / 60;
471
472         buf[0] = '\0';
473
474         if( ed->an_move_index != 0 && ed->an_move_count != 0 && *ed->hint != '\0' ) {
475             char mov[16];
476
477             strncpy( mov, ed->hint, sizeof(mov) );
478             mov[ sizeof(mov)-1 ] = '\0';
479
480             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 );
481         }
482
483         SetEngineState( ed->which, STATE_ANALYZING, buf );
484     }
485     else {
486         SetEngineState( ed->which, STATE_IDLE, "" );
487     }
488 #endif
489
490     SetWindowText( ed->hLabel, s_label );
491
492     s_label[0] = '\0';
493
494     if( ed->time > 0 && ed->nodes > 0 ) {
495         unsigned long nps_100 = ed->nodes / ed->time;
496
497         if( nps_100 < 100000 ) {
498             sprintf( s_label, "NPS: %lu", nps_100 * 100 );
499         }
500         else {
501             sprintf( s_label, "NPS: %.1fk", nps_100 / 10.0 );
502         }
503     }
504
505     SetWindowText( ed->hLabelNPS, s_label );
506
507     /* Memo */
508     if( ed->pv != 0 && *ed->pv != '\0' ) {
509         char s_nodes[24];
510         char s_score[16];
511         char s_time[24];
512         char buf[256];
513         int buflen;
514         int time_secs = ed->time / 100;
515         int time_cent = ed->time % 100;
516
517         /* Nodes */
518         if( ed->nodes < 1000000 ) {
519             sprintf( s_nodes, "%lu", ed->nodes );
520         }
521         else {
522             sprintf( s_nodes, "%.1fM", ed->nodes / 1000000.0 );
523         }
524
525         /* Score */
526         if( ed->score > 0 ) {
527             sprintf( s_score, "+%.2f", ed->score / 100.0 );
528         }
529         else {
530             sprintf( s_score, "%.2f", ed->score / 100.0 );
531         }
532
533         /* Time */
534         sprintf( s_time, "%d:%02d.%02d", time_secs / 60, time_secs % 60, time_cent );
535
536         /* Put all together... */
537         sprintf( buf, "%3d\t%s\t%s\t%s\t", ed->depth, s_score, s_nodes, s_time );
538
539         /* Add PV */
540         buflen = strlen(buf);
541
542         strncpy( buf + buflen, ed->pv, sizeof(buf) - buflen );
543
544         buf[ sizeof(buf) - 3 ] = '\0';
545
546         strcat( buf + buflen, "\r\n" );
547
548         /* Update memo */
549         InsertIntoMemo( ed->hMemo, buf );
550     }
551
552     /* Colors */
553     SetEngineColorIcon( ed->hColorIcon, ed->which );
554 }
555
556 LRESULT CALLBACK EngineOutputProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
557 {
558     static SnapData sd;
559
560     switch (message) {
561     case WM_INITDIALOG:
562         if( engineOutputDialog == NULL ) {
563             engineOutputDialog = hDlg;
564
565             RestoreWindowPlacement( hDlg, &wpEngineOutput ); /* Restore window placement */
566
567             ResizeWindowControls( hDlg, windowMode );
568
569             SetEngineState( 0, STATE_IDLE, "" );
570             SetEngineState( 1, STATE_IDLE, "" );
571         }
572
573         return FALSE;
574
575     case WM_COMMAND:
576         switch (LOWORD(wParam)) {
577         case IDOK:
578           EndDialog(hDlg, TRUE);
579           return TRUE;
580
581         case IDCANCEL:
582           EndDialog(hDlg, FALSE);
583           return TRUE;
584
585         default:
586           break;
587         }
588
589         break;
590
591     case WM_GETMINMAXINFO:
592         {
593             MINMAXINFO * mmi = (MINMAXINFO *) lParam;
594
595             mmi->ptMinTrackSize.x = 100;
596             mmi->ptMinTrackSize.y = 160;
597         }
598         break;
599
600     case WM_CLOSE:
601         EngineOutputPopDown();
602         break;
603
604     case WM_SIZE:
605         ResizeWindowControls( hDlg, windowMode );
606         break;
607
608     case WM_ENTERSIZEMOVE:
609         return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
610
611     case WM_SIZING:
612         return OnSizing( &sd, hDlg, wParam, lParam );
613
614     case WM_MOVING:
615         return OnMoving( &sd, hDlg, wParam, lParam );
616
617     case WM_EXITSIZEMOVE:
618         return OnExitSizeMove( &sd, hDlg, wParam, lParam );
619     }
620
621     return FALSE;
622 }
623
624 VOID EngineOutputPopUp()
625 {
626   FARPROC lpProc;
627
628   if( needInit ) {
629       InitializeEngineOutput();
630   }
631
632   CheckMenuItem(GetMenu(hwndMain), IDM_ShowEngineOutput, MF_CHECKED);
633
634   if( engineOutputDialog ) {
635     SendMessage( engineOutputDialog, WM_INITDIALOG, 0, 0 );
636
637     if( ! engineOutputDialogUp ) {
638         ShowWindow(engineOutputDialog, SW_SHOW);
639     }
640   }
641   else {
642     lpProc = MakeProcInstance( (FARPROC) EngineOutputProc, hInst );
643
644     /* Note to self: dialog must have the WS_VISIBLE style set, otherwise it's not shown! */
645     CreateDialog( hInst, MAKEINTRESOURCE(DLG_EngineOutput), hwndMain, (DLGPROC)lpProc );
646
647     FreeProcInstance(lpProc);
648   }
649
650   engineOutputDialogUp = TRUE;
651 }
652
653 VOID EngineOutputPopDown()
654 {
655   CheckMenuItem(GetMenu(hwndMain), IDM_ShowEngineOutput, MF_UNCHECKED);
656
657   if( engineOutputDialog ) {
658       ShowWindow(engineOutputDialog, SW_HIDE);
659   }
660
661   engineOutputDialogUp = FALSE;
662 }
663
664 BOOL EngineOutputIsUp()
665 {
666     return engineOutputDialogUp;
667 }
668
669 VOID EngineOutputUpdate( FrontEndProgramStats * stats )
670 {
671     EngineOutputData ed;
672     BOOL clearMemo = FALSE;
673     int which;
674     int depth;
675
676     if( stats == 0 ) {
677         SetEngineState( 0, STATE_IDLE, "" );
678         SetEngineState( 1, STATE_IDLE, "" );
679         return;
680     }
681
682     which = stats->which;
683     depth = stats->depth;
684
685     if( which < 0 || which > 1 || depth < 0 || stats->time < 0 || stats->pv == 0 ) {
686         return;
687     }
688
689     if( engineOutputDialog == NULL ) {
690         return;
691     }
692
693     VerifyDisplayMode();
694
695     ed.which = which;
696     ed.depth = depth;
697     ed.nodes = stats->nodes;
698     ed.score = stats->score;
699     ed.time = stats->time;
700     ed.pv = stats->pv;
701     ed.hint = stats->hint;
702     ed.an_move_index = stats->an_move_index;
703     ed.an_move_count = stats->an_move_count;
704
705     /* Get target control */
706     if( which == 0 ) {
707         ed.hColorIcon = GetDlgItem( engineOutputDialog, IDC_Color1 );
708         ed.hLabel = GetDlgItem( engineOutputDialog, IDC_EngineLabel1 );
709         ed.hStateIcon = GetDlgItem( engineOutputDialog, IDC_StateIcon1 );
710         ed.hStateData = GetDlgItem( engineOutputDialog, IDC_StateData1 );
711         ed.hLabelNPS = GetDlgItem( engineOutputDialog, IDC_Engine1_NPS );
712         ed.hMemo  = GetDlgItem( engineOutputDialog, IDC_EngineMemo1 );
713         ed.name = first.tidy;
714     }
715     else {
716         ed.hColorIcon = GetDlgItem( engineOutputDialog, IDC_Color2 );
717         ed.hLabel = GetDlgItem( engineOutputDialog, IDC_EngineLabel2 );
718         ed.hStateIcon = GetDlgItem( engineOutputDialog, IDC_StateIcon2 );
719         ed.hStateData = GetDlgItem( engineOutputDialog, IDC_StateData2 );
720         ed.hLabelNPS = GetDlgItem( engineOutputDialog, IDC_Engine2_NPS );
721         ed.hMemo  = GetDlgItem( engineOutputDialog, IDC_EngineMemo2 );
722         ed.name = second.tidy;
723     }
724
725     /* Clear memo if needed */
726     if( lastDepth[which] > depth || (lastDepth[which] == depth && depth <= 1) ) {
727         clearMemo = TRUE;
728     }
729
730     if( lastForwardMostMove[which] != forwardMostMove ) {
731         clearMemo = TRUE;
732     }
733
734     if( clearMemo ) {
735         SendMessage( ed.hMemo, WM_SETTEXT, 0, (LPARAM) "" );
736     }
737
738     /* Update */
739     lastDepth[which] = depth;
740     lastForwardMostMove[which] = forwardMostMove;
741
742     if( ed.pv != 0 && ed.pv[0] == ' ' ) {
743         if( strncmp( ed.pv, " no PV", 6 ) == 0 ) { /* Hack on hack! :-O */
744             ed.pv = "";
745         }
746     }
747
748     UpdateControls( &ed );
749 }