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