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