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