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