4 * Author: Alessandro Scotti (Dec 2005)
\r
6 * ------------------------------------------------------------------------
\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
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
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
21 *------------------------------------------------------------------------
\r
22 ** See the file ChangeLog for a revision history. */
\r
26 #include <windows.h> /* required for all Windows applications */
\r
27 #include <richedit.h>
\r
31 #include <commdlg.h>
\r
35 #include "winboard.h"
\r
36 #include "frontend.h"
\r
37 #include "backend.h"
\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
46 #define nPondering 5
\r
48 #define nAnalyzing 7
\r
50 HICON icons[8]; // [HGM] this front-end array translates back-end icon indicator to handle
\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
56 #define nStateData 4
\r
60 HWND outputField[2][7]; // [HGM] front-end array to translate output field to window handle
\r
62 void EngineOutputPopUp();
\r
63 void EngineOutputPopDown();
\r
64 int EngineOutputIsUp();
\r
66 #define SHOW_PONDERING
\r
68 /* Imports from backend.c */
\r
69 char * SavePart(char *str);
\r
70 extern int opponentKibitzes;
\r
72 /* Imports from winboard.c */
\r
73 extern HWND engineOutputDialog;
\r
74 extern int engineOutputDialogUp;
\r
76 extern HINSTANCE hInst;
\r
77 extern HWND hwndMain;
\r
79 extern WindowPlacement wpEngineOutput;
\r
81 extern BoardSize boardSize;
\r
83 /* Module variables */
\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
89 #define ICON_SIZE 14
\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
97 static int windowMode = 1;
\r
99 static int needInit = TRUE;
\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
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
122 } EngineOutputData;
\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
129 static HICON LoadIconEx( int id )
\r
131 return LoadImage( hInst, MAKEINTRESOURCE(id), IMAGE_ICON, ICON_SIZE, ICON_SIZE, 0 );
\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
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
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
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
169 static void SetControlPos( HWND hDlg, int id, int x, int y, int width, int height )
\r
171 HWND hControl = GetDlgItem( hDlg, id );
\r
173 SetWindowPos( hControl, HWND_TOP, x, y, width, height, SWP_NOZORDER );
\r
176 #define HIDDEN_X 20000
\r
177 #define HIDDEN_Y 20000
\r
180 static void HideControl( HWND hDlg, int id )
\r
182 HWND hControl = GetDlgItem( hDlg, id );
\r
185 GetWindowRect( hControl, &rc );
\r
188 Avoid hiding an already hidden control, because that causes many
\r
189 unnecessary WM_ERASEBKGND messages!
\r
191 if( rc.left != HIDDEN_X || rc.top != HIDDEN_Y ) {
\r
192 SetControlPos( hDlg, id, 20000, 20000, 100, 100 );
\r
196 // front end, although we might make GetWindowRect front end instead
\r
197 static int GetControlWidth( HWND hDlg, int id )
\r
201 GetWindowRect( GetDlgItem( hDlg, id ), &rc );
\r
203 return rc.right - rc.left;
\r
207 static int GetControlHeight( HWND hDlg, int id )
\r
211 GetWindowRect( GetDlgItem( hDlg, id ), &rc );
\r
213 return rc.bottom - rc.top;
\r
216 static int GetHeaderHeight()
\r
218 int result = GetControlHeight( engineOutputDialog, IDC_EngineLabel1 );
\r
220 if( result < ICON_SIZE ) result = ICON_SIZE;
\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
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
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
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
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
258 int maxControlWidth;
\r
261 /* Initialize variables */
\r
262 GetClientRect( hDlg, &rc );
\r
264 clientWidth = rc.right - rc.left;
\r
265 clientHeight = rc.bottom - rc.top;
\r
267 maxControlWidth = clientWidth - 2*H_MARGIN;
\r
269 npsWidth = GetControlWidth( hDlg, IDC_Engine1_NPS );
\r
271 /* Resize controls */
\r
274 PositionControlSet( hDlg, H_MARGIN, V_MARGIN,
\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
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
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
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
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
302 InvalidateRect( GetDlgItem(hDlg,IDC_EngineMemo1), NULL, FALSE );
\r
303 InvalidateRect( GetDlgItem(hDlg,IDC_EngineMemo2), NULL, FALSE );
\r
306 // front end. Actual printing of PV lines into the output field
\r
307 static void InsertIntoMemo( int which, char * text )
\r
309 SendMessage( outputField[which][nMemo], EM_SETSEL, 0, 0 );
\r
311 SendMessage( outputField[which][nMemo], EM_REPLACESEL, (WPARAM) FALSE, (LPARAM) text );
\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
320 SendMessage( outputField[which][field], STM_SETICON, (WPARAM) icons[nIcon], 0 );
\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
327 SetWindowText( outputField[which][field], s_label );
\r
330 // This seems pure front end
\r
331 LRESULT CALLBACK EngineOutputProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
\r
333 static SnapData sd;
\r
336 case WM_INITDIALOG:
\r
337 if( engineOutputDialog == NULL ) {
\r
338 engineOutputDialog = hDlg;
\r
340 RestoreWindowPlacement( hDlg, &wpEngineOutput ); /* Restore window placement */
\r
342 ResizeWindowControls( hDlg, windowMode );
\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
348 SetEngineState( 0, STATE_IDLE, "" );
\r
349 SetEngineState( 1, STATE_IDLE, "" );
\r
355 switch (LOWORD(wParam)) {
\r
357 EndDialog(hDlg, TRUE);
\r
361 EndDialog(hDlg, FALSE);
\r
370 case WM_GETMINMAXINFO:
\r
372 MINMAXINFO * mmi = (MINMAXINFO *) lParam;
\r
374 mmi->ptMinTrackSize.x = 100;
\r
375 mmi->ptMinTrackSize.y = 160;
\r
380 EngineOutputPopDown();
\r
384 ResizeWindowControls( hDlg, windowMode );
\r
387 case WM_ENTERSIZEMOVE:
\r
388 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
391 return OnSizing( &sd, hDlg, wParam, lParam );
\r
394 return OnMoving( &sd, hDlg, wParam, lParam );
\r
396 case WM_EXITSIZEMOVE:
\r
397 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
404 void EngineOutputPopUp()
\r
408 CheckMenuItem(GetMenu(hwndMain), IDM_ShowEngineOutput, MF_CHECKED);
\r
410 if( engineOutputDialog ) {
\r
411 SendMessage( engineOutputDialog, WM_INITDIALOG, 0, 0 );
\r
413 if( ! engineOutputDialogUp ) {
\r
414 ShowWindow(engineOutputDialog, SW_SHOW);
\r
418 lpProc = MakeProcInstance( (FARPROC) EngineOutputProc, hInst );
\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
423 FreeProcInstance(lpProc);
\r
426 // [HGM] displaced to after creation of dialog, to allow initialization of output fields
\r
428 InitializeEngineOutput();
\r
432 engineOutputDialogUp = TRUE;
\r
436 void EngineOutputPopDown()
\r
438 CheckMenuItem(GetMenu(hwndMain), IDM_ShowEngineOutput, MF_UNCHECKED);
\r
440 if( engineOutputDialog ) {
\r
441 ShowWindow(engineOutputDialog, SW_HIDE);
\r
444 engineOutputDialogUp = FALSE;
\r
447 // front end. [HGM] Takes handle of output control from table, so only number is passed
\r
448 void DoClearMemo(int which)
\r
450 SendMessage( outputField[which][nMemo], WM_SETTEXT, 0, (LPARAM) "" );
\r
453 //------------------------ pure back-end routines -------------------------------
\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
459 int x_which = 1 - which;
\r
461 if( engineState[ which ] != state ) {
\r
462 engineState[ which ] = 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
471 case STATE_PONDERING:
\r
472 SetIcon( which, nStateIcon, nPondering );
\r
474 case STATE_ANALYZING:
\r
475 SetIcon( which, nStateIcon, nAnalyzing );
\r
478 SetIcon( which, nStateIcon, nClear );
\r
483 if( state_data != 0 ) {
\r
484 DoSetWindowText( which, nStateData, state_data );
\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
491 EngineOutputData ed;
\r
492 int clearMemo = FALSE;
\r
497 SetEngineState( 0, STATE_IDLE, "" );
\r
498 SetEngineState( 1, STATE_IDLE, "" );
\r
502 if(gameMode == IcsObserving && !appData.icsEngineAnalyze)
\r
503 return; // [HGM] kibitz: shut up engine if we are observing an ICS game
\r
505 which = stats->which;
\r
506 depth = stats->depth;
\r
508 if( which < 0 || which > 1 || depth < 0 || stats->time < 0 || stats->pv == 0 ) {
\r
512 if( engineOutputDialog == NULL ) {
\r
516 VerifyDisplayMode();
\r
520 ed.nodes = stats->nodes;
\r
521 ed.score = stats->score;
\r
522 ed.time = stats->time;
\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
528 /* Get target control. [HGM] this is moved to front end, which get them from a table */
\r
530 ed.name = first.tidy;
\r
533 ed.name = second.tidy;
\r
536 /* Clear memo if needed */
\r
537 if( lastDepth[which] > depth || (lastDepth[which] == depth && depth <= 1) ) {
\r
541 if( lastForwardMostMove[which] != forwardMostMove ) {
\r
545 if( clearMemo ) DoClearMemo(which);
\r
548 lastDepth[which] = depth;
\r
549 lastForwardMostMove[which] = forwardMostMove;
\r
551 if( ed.pv != 0 && ed.pv[0] == ' ' ) {
\r
552 if( strncmp( ed.pv, " no PV", 6 ) == 0 ) { /* Hack on hack! :-O */
\r
557 UpdateControls( &ed );
\r
560 #define ENGINE_COLOR_WHITE 'w'
\r
561 #define ENGINE_COLOR_BLACK 'b'
\r
562 #define ENGINE_COLOR_UNKNOWN ' '
\r
565 char GetEngineColor( int which )
\r
567 char result = ENGINE_COLOR_UNKNOWN;
\r
569 if( which == 0 || which == 1 ) {
\r
570 ChessProgramState * cps;
\r
572 switch (gameMode) {
\r
573 case MachinePlaysBlack:
\r
574 case IcsPlayingBlack:
\r
575 result = ENGINE_COLOR_BLACK;
\r
577 case MachinePlaysWhite:
\r
578 case IcsPlayingWhite:
\r
579 result = ENGINE_COLOR_WHITE;
\r
583 result = WhiteOnMove(forwardMostMove) ? ENGINE_COLOR_WHITE : ENGINE_COLOR_BLACK;
\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
590 default: ; // does not happen, but suppresses pedantic warnings
\r
598 char GetActiveEngineColor()
\r
600 char result = ENGINE_COLOR_UNKNOWN;
\r
602 if( gameMode == TwoMachinesPlay ) {
\r
603 result = WhiteOnMove(forwardMostMove) ? ENGINE_COLOR_WHITE : ENGINE_COLOR_BLACK;
\r
610 static int IsEnginePondering( int which )
\r
612 int result = FALSE;
\r
614 switch (gameMode) {
\r
615 case MachinePlaysBlack:
\r
616 case IcsPlayingBlack:
\r
617 if( WhiteOnMove(forwardMostMove) ) result = TRUE;
\r
619 case MachinePlaysWhite:
\r
620 case IcsPlayingWhite:
\r
621 if( ! WhiteOnMove(forwardMostMove) ) result = TRUE;
\r
623 case TwoMachinesPlay:
\r
624 if( GetActiveEngineColor() != ENGINE_COLOR_UNKNOWN ) {
\r
625 if( GetEngineColor( which ) != GetActiveEngineColor() ) result = TRUE;
\r
628 default: ; // does not happen, but suppresses pedantic warnings
\r
635 static void SetDisplayMode( int mode )
\r
637 if( windowMode != mode ) {
\r
640 ResizeWindowControls( engineOutputDialog, mode );
\r
645 static void VerifyDisplayMode()
\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
655 case MachinePlaysWhite:
\r
656 case MachinePlaysBlack:
\r
659 case IcsPlayingWhite:
\r
660 case IcsPlayingBlack:
\r
661 mode = appData.zippyPlay && opponentKibitzes; // [HGM] kibitz
\r
663 case TwoMachinesPlay:
\r
667 /* Do not change */
\r
671 SetDisplayMode( mode );
\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
677 char color = GetEngineColor(which);
\r
680 if( color == ENGINE_COLOR_BLACK )
\r
681 nicon = nColorBlack;
\r
682 else if( color == ENGINE_COLOR_WHITE )
\r
683 nicon = nColorWhite;
\r
685 nicon = nColorUnknown;
\r
687 SetIcon( which, nColorIcon, nicon );
\r
690 #define MAX_NAME_LENGTH 32
\r
692 // pure back end, now SetWindowText is called via wrapper DoSetWindowText
\r
693 static void UpdateControls( EngineOutputData * ed )
\r
695 // int isPondering = FALSE;
\r
697 char s_label[MAX_NAME_LENGTH + 32];
\r
699 char * name = ed->name;
\r
702 if( name == 0 || *name == '\0' ) {
\r
706 strncpy( s_label, name, MAX_NAME_LENGTH );
\r
707 s_label[ MAX_NAME_LENGTH-1 ] = '\0';
\r
709 #ifdef SHOW_PONDERING
\r
710 if( IsEnginePondering( ed->which ) ) {
\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
719 else if( ed->pv != 0 && *ed->pv != '\0' ) {
\r
720 char * sep = strchr( ed->pv, ' ' );
\r
721 int buflen = sizeof(buf);
\r
723 if( sep != NULL ) {
\r
724 buflen = sep - ed->pv + 1;
\r
725 if( buflen > sizeof(buf) ) buflen = sizeof(buf);
\r
728 strncpy( buf, ed->pv, buflen );
\r
729 buf[ buflen-1 ] = '\0';
\r
732 SetEngineState( ed->which, STATE_PONDERING, buf );
\r
734 else if( gameMode == TwoMachinesPlay ) {
\r
735 SetEngineState( ed->which, STATE_THINKING, "" );
\r
737 else if( gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
738 || (gameMode == IcsObserving && appData.icsEngineAnalyze)) { // [HGM] ICS-analyze
\r
740 int time_secs = ed->time / 100;
\r
741 int time_mins = time_secs / 60;
\r
745 if( ed->an_move_index != 0 && ed->an_move_count != 0 && *ed->hint != '\0' ) {
\r
748 strncpy( mov, ed->hint, sizeof(mov) );
\r
749 mov[ sizeof(mov)-1 ] = '\0';
\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
754 SetEngineState( ed->which, STATE_ANALYZING, buf );
\r
757 SetEngineState( ed->which, STATE_IDLE, "" );
\r
761 DoSetWindowText( ed->which, nLabel, s_label );
\r
765 if( ed->time > 0 && ed->nodes > 0 ) {
\r
766 unsigned long nps_100 = ed->nodes / ed->time;
\r
768 if( nps_100 < 100000 ) {
\r
769 sprintf( s_label, "NPS: %lu", nps_100 * 100 );
\r
772 sprintf( s_label, "NPS: %.1fk", nps_100 / 10.0 );
\r
776 DoSetWindowText( ed->which, nLabelNPS, s_label );
\r
779 if( ed->pv != 0 && *ed->pv != '\0' ) {
\r
785 int time_secs = ed->time / 100;
\r
786 int time_cent = ed->time % 100;
\r
789 if( ed->nodes < 1000000 ) {
\r
790 sprintf( s_nodes, u64Display, ed->nodes );
\r
793 sprintf( s_nodes, "%.1fM", u64ToDouble(ed->nodes) / 1000000.0 );
\r
797 if( ed->score > 0 ) {
\r
798 sprintf( s_score, "+%.2f", ed->score / 100.0 );
\r
801 sprintf( s_score, "%.2f", ed->score / 100.0 );
\r
805 sprintf( s_time, "%d:%02d.%02d", time_secs / 60, time_secs % 60, time_cent );
\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
811 buflen = strlen(buf);
\r
813 strncpy( buf + buflen, ed->pv, sizeof(buf) - buflen );
\r
815 buf[ sizeof(buf) - 3 ] = '\0';
\r
817 strcat( buf + buflen, "\r\n" );
\r
820 InsertIntoMemo( ed->which, buf );
\r
824 SetEngineColorIcon( ed->which );
\r
828 int EngineOutputIsUp()
\r
830 return engineOutputDialogUp;
\r
833 // [HGM] kibitz: write kibitz line; split window for it if necessary
\r
834 void OutputKibitz(int window, char *text)
\r
836 if(!EngineOutputIsUp()) return;
\r
837 if(!opponentKibitzes) { // on first kibitz of game, clear memos
\r
839 if(gameMode == IcsObserving) DoClearMemo(0);
\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
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