4 * Author: Alessandro Scotti (Dec 2005)
\r
6 * Copyright 2005 Alessandro Scotti
\r
8 * ------------------------------------------------------------------------
\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
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
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
23 *------------------------------------------------------------------------
\r
24 ** See the file ChangeLog for a revision history. */
\r
28 #include <windows.h> /* required for all Windows applications */
\r
29 #include <richedit.h>
\r
33 #include <commdlg.h>
\r
37 #include "frontend.h"
\r
38 #include "backend.h"
\r
39 #include "winboard.h"
\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
48 #define nPondering 5
\r
50 #define nAnalyzing 7
\r
52 HICON icons[8]; // [HGM] this front-end array translates back-end icon indicator to handle
\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
58 #define nStateData 4
\r
62 HWND outputField[2][7]; // [HGM] front-end array to translate output field to window handle
\r
64 #define SHOW_PONDERING
\r
66 /* Module variables */
\r
69 #define LABEL_V_DISTANCE 1 /* Distance between label and memo */
\r
70 #define SPLITTER_SIZE 4 /* Distance between first memo and second label */
\r
72 #define ICON_SIZE 14
\r
74 #define STATE_UNKNOWN -1
\r
75 #define STATE_THINKING 0
\r
76 #define STATE_IDLE 1
\r
77 #define STATE_PONDERING 2
\r
78 #define STATE_ANALYZING 3
\r
80 static int windowMode = 1;
\r
81 static int needInit = TRUE;
\r
82 static int lastDepth[2] = { -1, -1 };
\r
83 static int lastForwardMostMove[2] = { -1, -1 };
\r
84 static int engineState[2] = { -1, -1 };
\r
85 static BOOLEAN engineOutputDialogUp = FALSE;
\r
88 // HWND hColorIcon; // [HGM] the output-control handles are no loger passed,
\r
89 // HWND hLabel; // to give better front-end / back-end separation
\r
90 // HWND hStateIcon; // the front-end routines now get them from a (front-end)
\r
91 // HWND hStateData; // table, indexed by output-field indicators.
\r
104 } EngineOutputData;
\r
106 static void VerifyDisplayMode();
\r
107 static void UpdateControls( EngineOutputData * ed );
\r
108 static void SetEngineState( int which, int state, char * state_data );
\r
111 static HICON LoadIconEx( int id )
\r
113 return LoadImage( hInst, MAKEINTRESOURCE(id), IMAGE_ICON, ICON_SIZE, ICON_SIZE, 0 );
\r
116 // [HGM] the platform-dependent way of indicating where output should go is now all
\r
117 // concentrated here, where a table of platform-dependent handles are initialized.
\r
118 // This cleanses most other routines of front-end stuff, so they can go into the back end.
\r
119 static void InitializeEngineOutput()
\r
121 // if( needInit ) { // needInit was already tested before call
\r
122 // [HGM] made this into a table, rather than separate global variables
\r
123 icons[nColorBlack] = LoadIconEx( IDI_BLACK_14 );
\r
124 icons[nColorWhite] = LoadIconEx( IDI_WHITE_14 );
\r
125 icons[nColorUnknown] = LoadIconEx( IDI_UNKNOWN_14 );
\r
126 icons[nClear] = LoadIconEx( IDI_TRANS_14 );
\r
127 icons[nPondering] = LoadIconEx( IDI_PONDER_14 );
\r
128 icons[nThinking] = LoadIconEx( IDI_CLOCK_14 );
\r
129 icons[nAnalyzing] = LoadIconEx( IDI_ANALYZE2_14 );
\r
131 // [HGM] also make a table of handles to output controls
\r
132 // Note that engineOutputDialog must be defined first!
\r
133 outputField[0][nColorIcon] = GetDlgItem( engineOutputDialog, IDC_Color1 );
\r
134 outputField[0][nLabel] = GetDlgItem( engineOutputDialog, IDC_EngineLabel1 );
\r
135 outputField[0][nStateIcon] = GetDlgItem( engineOutputDialog, IDC_StateIcon1 );
\r
136 outputField[0][nStateData] = GetDlgItem( engineOutputDialog, IDC_StateData1 );
\r
137 outputField[0][nLabelNPS] = GetDlgItem( engineOutputDialog, IDC_Engine1_NPS );
\r
138 outputField[0][nMemo] = GetDlgItem( engineOutputDialog, IDC_EngineMemo1 );
\r
140 outputField[1][nColorIcon] = GetDlgItem( engineOutputDialog, IDC_Color2 );
\r
141 outputField[1][nLabel] = GetDlgItem( engineOutputDialog, IDC_EngineLabel2 );
\r
142 outputField[1][nStateIcon] = GetDlgItem( engineOutputDialog, IDC_StateIcon2 );
\r
143 outputField[1][nStateData] = GetDlgItem( engineOutputDialog, IDC_StateData2 );
\r
144 outputField[1][nLabelNPS] = GetDlgItem( engineOutputDialog, IDC_Engine2_NPS );
\r
145 outputField[1][nMemo] = GetDlgItem( engineOutputDialog, IDC_EngineMemo2 );
\r
146 // needInit = FALSE;
\r
151 static void SetControlPos( HWND hDlg, int id, int x, int y, int width, int height )
\r
153 HWND hControl = GetDlgItem( hDlg, id );
\r
155 SetWindowPos( hControl, HWND_TOP, x, y, width, height, SWP_NOZORDER );
\r
158 #define HIDDEN_X 20000
\r
159 #define HIDDEN_Y 20000
\r
162 static void HideControl( HWND hDlg, int id )
\r
164 HWND hControl = GetDlgItem( hDlg, id );
\r
167 GetWindowRect( hControl, &rc );
\r
170 Avoid hiding an already hidden control, because that causes many
\r
171 unnecessary WM_ERASEBKGND messages!
\r
173 if( rc.left != HIDDEN_X || rc.top != HIDDEN_Y ) {
\r
174 SetControlPos( hDlg, id, 20000, 20000, 100, 100 );
\r
178 // front end, although we might make GetWindowRect front end instead
\r
179 static int GetControlWidth( HWND hDlg, int id )
\r
183 GetWindowRect( GetDlgItem( hDlg, id ), &rc );
\r
185 return rc.right - rc.left;
\r
189 static int GetControlHeight( HWND hDlg, int id )
\r
193 GetWindowRect( GetDlgItem( hDlg, id ), &rc );
\r
195 return rc.bottom - rc.top;
\r
198 static int GetHeaderHeight()
\r
200 int result = GetControlHeight( engineOutputDialog, IDC_EngineLabel1 );
\r
202 if( result < ICON_SIZE ) result = ICON_SIZE;
\r
207 // The size calculations should be backend? If setControlPos is a platform-dependent way of doing things,
\r
208 // a platform-independent wrapper for it should be supplied.
\r
209 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
211 int label_x = x + ICON_SIZE + H_MARGIN;
\r
212 int label_h = GetControlHeight( hDlg, IDC_EngineLabel1 );
\r
213 int label_y = y + ICON_SIZE - label_h;
\r
214 int nps_w = GetControlWidth( hDlg, IDC_Engine1_NPS );
\r
215 int nps_x = clientWidth - H_MARGIN - nps_w;
\r
216 int state_data_w = GetControlWidth( hDlg, IDC_StateData1 );
\r
217 int state_data_x = nps_x - H_MARGIN - state_data_w;
\r
218 int state_icon_x = state_data_x - ICON_SIZE - 2;
\r
219 int max_w = clientWidth - 2*H_MARGIN;
\r
220 int memo_y = y + ICON_SIZE + LABEL_V_DISTANCE;
\r
222 SetControlPos( hDlg, idColor, x, y, ICON_SIZE, ICON_SIZE );
\r
223 SetControlPos( hDlg, idEngineLabel, label_x, label_y, state_icon_x - label_x, label_h );
\r
224 SetControlPos( hDlg, idStateIcon, state_icon_x, y, ICON_SIZE, ICON_SIZE );
\r
225 SetControlPos( hDlg, idStateData, state_data_x, label_y, state_data_w, label_h );
\r
226 SetControlPos( hDlg, idNPS, nps_x, label_y, nps_w, label_h );
\r
227 SetControlPos( hDlg, idMemo, x, memo_y, max_w, memoHeight );
\r
230 // Also here some of the size calculations should go to the back end, and their actual application to a front-end routine
\r
231 static void ResizeWindowControls( HWND hDlg, int mode )
\r
234 int headerHeight = GetHeaderHeight();
\r
235 // int labelHeight = GetControlHeight( hDlg, IDC_EngineLabel1 );
\r
236 // int labelOffset = H_MARGIN + ICON_SIZE + H_MARGIN;
\r
237 // int labelDeltaY = ICON_SIZE - labelHeight;
\r
240 int maxControlWidth;
\r
243 /* Initialize variables */
\r
244 GetClientRect( hDlg, &rc );
\r
246 clientWidth = rc.right - rc.left;
\r
247 clientHeight = rc.bottom - rc.top;
\r
249 maxControlWidth = clientWidth - 2*H_MARGIN;
\r
251 npsWidth = GetControlWidth( hDlg, IDC_Engine1_NPS );
\r
253 /* Resize controls */
\r
256 PositionControlSet( hDlg, H_MARGIN, V_MARGIN,
\r
258 clientHeight - V_MARGIN - LABEL_V_DISTANCE - headerHeight- V_MARGIN,
\r
259 IDC_Color1, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineMemo1, IDC_StateIcon1, IDC_StateData1 );
\r
261 /* Hide controls for the second engine */
\r
262 HideControl( hDlg, IDC_Color2 );
\r
263 HideControl( hDlg, IDC_EngineLabel2 );
\r
264 HideControl( hDlg, IDC_StateIcon2 );
\r
265 HideControl( hDlg, IDC_StateData2 );
\r
266 HideControl( hDlg, IDC_Engine2_NPS );
\r
267 HideControl( hDlg, IDC_EngineMemo2 );
\r
268 SendDlgItemMessage( hDlg, IDC_EngineMemo2, WM_SETTEXT, 0, (LPARAM) "" );
\r
269 /* TODO: we should also hide/disable them!!! what about tab stops?!?! */
\r
273 int memo_h = (clientHeight - headerHeight*2 - V_MARGIN*2 - LABEL_V_DISTANCE*2 - SPLITTER_SIZE) / 2;
\r
274 int header1_y = V_MARGIN;
\r
275 int header2_y = V_MARGIN + headerHeight + LABEL_V_DISTANCE + memo_h + SPLITTER_SIZE;
\r
277 PositionControlSet( hDlg, H_MARGIN, header1_y, clientWidth, memo_h,
\r
278 IDC_Color1, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineMemo1, IDC_StateIcon1, IDC_StateData1 );
\r
280 PositionControlSet( hDlg, H_MARGIN, header2_y, clientWidth, memo_h,
\r
281 IDC_Color2, IDC_EngineLabel2, IDC_Engine2_NPS, IDC_EngineMemo2, IDC_StateIcon2, IDC_StateData2 );
\r
284 InvalidateRect( GetDlgItem(hDlg,IDC_EngineMemo1), NULL, FALSE );
\r
285 InvalidateRect( GetDlgItem(hDlg,IDC_EngineMemo2), NULL, FALSE );
\r
288 // front end. Actual printing of PV lines into the output field
\r
289 static void InsertIntoMemo( int which, char * text, int where )
\r
291 SendMessage( outputField[which][nMemo], EM_SETSEL, where, where ); // [HGM] multivar: choose insertion point
\r
293 SendMessage( outputField[which][nMemo], EM_REPLACESEL, (WPARAM) FALSE, (LPARAM) text );
\r
296 // front end. Associates an icon with an output field ("control" in Windows jargon).
\r
297 // [HGM] let it find out the output field from the 'which' number by itself
\r
298 static void SetIcon( int which, int field, int nIcon )
\r
302 SendMessage( outputField[which][field], STM_SETICON, (WPARAM) icons[nIcon], 0 );
\r
306 // front end wrapper for SetWindowText, taking control number in stead of handle
\r
307 void DoSetWindowText(int which, int field, char *s_label)
\r
309 SetWindowText( outputField[which][field], s_label );
\r
312 // This seems pure front end
\r
313 LRESULT CALLBACK EngineOutputProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
\r
315 static SnapData sd;
\r
318 case WM_INITDIALOG:
\r
319 if( engineOutputDialog == NULL ) {
\r
320 engineOutputDialog = hDlg;
\r
322 RestoreWindowPlacement( hDlg, &wpEngineOutput ); /* Restore window placement */
\r
324 ResizeWindowControls( hDlg, windowMode );
\r
327 SendDlgItemMessage( engineOutputDialog, IDC_EngineMemo1, WM_SETFONT, (WPARAM)font[boardSize][MOVEHISTORY_FONT]->hf, MAKELPARAM(TRUE, 0 ));
\r
328 SendDlgItemMessage( engineOutputDialog, IDC_EngineMemo2, WM_SETFONT, (WPARAM)font[boardSize][MOVEHISTORY_FONT]->hf, MAKELPARAM(TRUE, 0 ));
\r
330 SetEngineState( 0, STATE_IDLE, "" );
\r
331 SetEngineState( 1, STATE_IDLE, "" );
\r
337 switch (LOWORD(wParam)) {
\r
339 EndDialog(hDlg, TRUE);
\r
343 EndDialog(hDlg, FALSE);
\r
352 case WM_GETMINMAXINFO:
\r
354 MINMAXINFO * mmi = (MINMAXINFO *) lParam;
\r
356 mmi->ptMinTrackSize.x = 100;
\r
357 mmi->ptMinTrackSize.y = 160;
\r
362 EngineOutputPopDown();
\r
366 ResizeWindowControls( hDlg, windowMode );
\r
369 case WM_ENTERSIZEMOVE:
\r
370 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
373 return OnSizing( &sd, hDlg, wParam, lParam );
\r
376 return OnMoving( &sd, hDlg, wParam, lParam );
\r
378 case WM_EXITSIZEMOVE:
\r
379 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
386 void EngineOutputPopUp()
\r
390 CheckMenuItem(GetMenu(hwndMain), IDM_ShowEngineOutput, MF_CHECKED);
\r
392 if( engineOutputDialog ) {
\r
393 SendMessage( engineOutputDialog, WM_INITDIALOG, 0, 0 );
\r
395 if( ! engineOutputDialogUp ) {
\r
396 ShowWindow(engineOutputDialog, SW_SHOW);
\r
400 lpProc = MakeProcInstance( (FARPROC) EngineOutputProc, hInst );
\r
402 /* Note to self: dialog must have the WS_VISIBLE style set, otherwise it's not shown! */
\r
403 CreateDialog( hInst, MAKEINTRESOURCE(DLG_EngineOutput), hwndMain, (DLGPROC)lpProc );
\r
405 FreeProcInstance(lpProc);
\r
408 // [HGM] displaced to after creation of dialog, to allow initialization of output fields
\r
410 InitializeEngineOutput();
\r
414 engineOutputDialogUp = TRUE;
\r
418 void EngineOutputPopDown()
\r
420 CheckMenuItem(GetMenu(hwndMain), IDM_ShowEngineOutput, MF_UNCHECKED);
\r
422 if( engineOutputDialog ) {
\r
423 ShowWindow(engineOutputDialog, SW_HIDE);
\r
426 engineOutputDialogUp = FALSE;
\r
429 // front end. [HGM] Takes handle of output control from table, so only number is passed
\r
430 void DoClearMemo(int which)
\r
432 SendMessage( outputField[which][nMemo], WM_SETTEXT, 0, (LPARAM) "" );
\r
435 //------------------------ pure back-end routines -------------------------------
\r
437 #define MAX_VAR 400
\r
438 static int scores[MAX_VAR], textEnd[MAX_VAR], curDepth[2], nrVariations[2];
\r
440 // back end, due to front-end wrapper for SetWindowText, and new SetIcon arguments
\r
441 static void SetEngineState( int which, int state, char * state_data )
\r
443 int x_which = 1 - which;
\r
445 if( engineState[ which ] != state ) {
\r
446 engineState[ which ] = state;
\r
449 case STATE_THINKING:
\r
450 SetIcon( which, nStateIcon, nThinking );
\r
451 if( engineState[ x_which ] == STATE_THINKING ) {
\r
452 SetEngineState( x_which, STATE_IDLE, "" );
\r
455 case STATE_PONDERING:
\r
456 SetIcon( which, nStateIcon, nPondering );
\r
458 case STATE_ANALYZING:
\r
459 SetIcon( which, nStateIcon, nAnalyzing );
\r
462 SetIcon( which, nStateIcon, nClear );
\r
467 if( state_data != 0 ) {
\r
468 DoSetWindowText( which, nStateData, state_data );
\r
472 // back end, now the front-end wrapper ClearMemo is used, and ed no longer contains handles.
\r
473 void EngineOutputUpdate( FrontEndProgramStats * stats )
\r
475 EngineOutputData ed;
\r
476 int clearMemo = FALSE;
\r
481 SetEngineState( 0, STATE_IDLE, "" );
\r
482 SetEngineState( 1, STATE_IDLE, "" );
\r
486 if(gameMode == IcsObserving && !appData.icsEngineAnalyze)
\r
487 return; // [HGM] kibitz: shut up engine if we are observing an ICS game
\r
489 which = stats->which;
\r
490 depth = stats->depth;
\r
492 if( which < 0 || which > 1 || depth < 0 || stats->time < 0 || stats->pv == 0 ) {
\r
496 if( engineOutputDialog == NULL ) {
\r
500 VerifyDisplayMode();
\r
504 ed.nodes = stats->nodes;
\r
505 ed.score = stats->score;
\r
506 ed.time = stats->time;
\r
508 ed.hint = stats->hint;
\r
509 ed.an_move_index = stats->an_move_index;
\r
510 ed.an_move_count = stats->an_move_count;
\r
512 /* Get target control. [HGM] this is moved to front end, which get them from a table */
\r
514 ed.name = first.tidy;
\r
517 ed.name = second.tidy;
\r
520 /* Clear memo if needed */
\r
521 if( lastDepth[which] > depth || (lastDepth[which] == depth && depth <= 1) ) {
\r
525 if( lastForwardMostMove[which] != forwardMostMove ) {
\r
529 if( clearMemo ) { DoClearMemo(which); nrVariations[which] = 0; }
\r
532 lastDepth[which] = depth == 1 && ed.nodes == 0 ? 0 : depth; // [HGM] info-line kudge
\r
533 lastForwardMostMove[which] = forwardMostMove;
\r
535 if( ed.pv != 0 && ed.pv[0] == ' ' ) {
\r
536 if( strncmp( ed.pv, " no PV", 6 ) == 0 ) { /* Hack on hack! :-O */
\r
541 UpdateControls( &ed );
\r
544 #define ENGINE_COLOR_WHITE 'w'
\r
545 #define ENGINE_COLOR_BLACK 'b'
\r
546 #define ENGINE_COLOR_UNKNOWN ' '
\r
549 char GetEngineColor( int which )
\r
551 char result = ENGINE_COLOR_UNKNOWN;
\r
553 if( which == 0 || which == 1 ) {
\r
554 ChessProgramState * cps;
\r
556 switch (gameMode) {
\r
557 case MachinePlaysBlack:
\r
558 case IcsPlayingBlack:
\r
559 result = ENGINE_COLOR_BLACK;
\r
561 case MachinePlaysWhite:
\r
562 case IcsPlayingWhite:
\r
563 result = ENGINE_COLOR_WHITE;
\r
567 result = WhiteOnMove(forwardMostMove) ? ENGINE_COLOR_WHITE : ENGINE_COLOR_BLACK;
\r
569 case TwoMachinesPlay:
\r
570 cps = (which == 0) ? &first : &second;
\r
571 result = cps->twoMachinesColor[0];
\r
572 result = result == 'w' ? ENGINE_COLOR_WHITE : ENGINE_COLOR_BLACK;
\r
574 default: ; // does not happen, but suppresses pedantic warnings
\r
582 char GetActiveEngineColor()
\r
584 char result = ENGINE_COLOR_UNKNOWN;
\r
586 if( gameMode == TwoMachinesPlay ) {
\r
587 result = WhiteOnMove(forwardMostMove) ? ENGINE_COLOR_WHITE : ENGINE_COLOR_BLACK;
\r
594 static int IsEnginePondering( int which )
\r
596 int result = FALSE;
\r
598 switch (gameMode) {
\r
599 case MachinePlaysBlack:
\r
600 case IcsPlayingBlack:
\r
601 if( WhiteOnMove(forwardMostMove) ) result = TRUE;
\r
603 case MachinePlaysWhite:
\r
604 case IcsPlayingWhite:
\r
605 if( ! WhiteOnMove(forwardMostMove) ) result = TRUE;
\r
607 case TwoMachinesPlay:
\r
608 if( GetActiveEngineColor() != ENGINE_COLOR_UNKNOWN ) {
\r
609 if( GetEngineColor( which ) != GetActiveEngineColor() ) result = TRUE;
\r
612 default: ; // does not happen, but suppresses pedantic warnings
\r
619 static void SetDisplayMode( int mode )
\r
621 if( windowMode != mode ) {
\r
624 ResizeWindowControls( engineOutputDialog, mode );
\r
629 static void VerifyDisplayMode()
\r
633 /* Get proper mode for current game */
\r
634 switch( gameMode ) {
\r
635 case IcsObserving: // [HGM] ICS analyze
\r
636 if(!appData.icsEngineAnalyze) return;
\r
639 case MachinePlaysWhite:
\r
640 case MachinePlaysBlack:
\r
643 case IcsPlayingWhite:
\r
644 case IcsPlayingBlack:
\r
645 mode = appData.zippyPlay && opponentKibitzes; // [HGM] kibitz
\r
647 case TwoMachinesPlay:
\r
651 /* Do not change */
\r
655 SetDisplayMode( mode );
\r
658 // back end. Determine what icon to se in the color-icon field, and print it
\r
659 static void SetEngineColorIcon( int which )
\r
661 char color = GetEngineColor(which);
\r
664 if( color == ENGINE_COLOR_BLACK )
\r
665 nicon = nColorBlack;
\r
666 else if( color == ENGINE_COLOR_WHITE )
\r
667 nicon = nColorWhite;
\r
669 nicon = nColorUnknown;
\r
671 SetIcon( which, nColorIcon, nicon );
\r
674 #define MAX_NAME_LENGTH 32
\r
676 // [HGM] multivar: sort Thinking Output within one depth on score
\r
678 static int InsertionPoint( int len, EngineOutputData * ed )
\r
680 int i, offs = 0, newScore = ed->score, n = ed->which;
\r
682 if(ed->nodes == 0 && ed->score == 0 && ed->time == 0)
\r
683 newScore = 1e6; // info lines inserted on top
\r
684 if(ed->depth != curDepth[n]) { // depth has changed
\r
685 curDepth[n] = ed->depth;
\r
686 nrVariations[n] = 0; // throw away everything we had
\r
688 // loop through all lines. Note even / odd used for different panes
\r
689 for(i=nrVariations[n]-2; i>=0; i-=2) {
\r
690 // put new item behind those we haven't looked at
\r
691 offs = textEnd[i+n];
\r
692 textEnd[i+n+2] = offs + len;
\r
693 scores[i+n+2] = newScore;
\r
694 if(newScore < scores[i+n]) break;
\r
695 // if it had higher score as previous, move previous in stead
\r
696 scores[i+n+2] = scores[i+n];
\r
697 textEnd[i+n+2] = textEnd[i+n] + len;
\r
701 textEnd[n] = offs + len;
\r
702 scores[n] = newScore;
\r
704 nrVariations[n] += 2;
\r
708 // pure back end, now SetWindowText is called via wrapper DoSetWindowText
\r
709 static void UpdateControls( EngineOutputData * ed )
\r
711 // int isPondering = FALSE;
\r
713 char s_label[MAX_NAME_LENGTH + 32];
\r
715 char * name = ed->name;
\r
718 if( name == 0 || *name == '\0' ) {
\r
722 strncpy( s_label, name, MAX_NAME_LENGTH );
\r
723 s_label[ MAX_NAME_LENGTH-1 ] = '\0';
\r
725 #ifdef SHOW_PONDERING
\r
726 if( IsEnginePondering( ed->which ) ) {
\r
731 if( ed->hint != 0 && *ed->hint != '\0' ) {
\r
732 strncpy( buf, ed->hint, sizeof(buf) );
\r
733 buf[sizeof(buf)-1] = '\0';
\r
735 else if( ed->pv != 0 && *ed->pv != '\0' ) {
\r
736 char * sep = strchr( ed->pv, ' ' );
\r
737 int buflen = sizeof(buf);
\r
739 if( sep != NULL ) {
\r
740 buflen = sep - ed->pv + 1;
\r
741 if( buflen > sizeof(buf) ) buflen = sizeof(buf);
\r
744 strncpy( buf, ed->pv, buflen );
\r
745 buf[ buflen-1 ] = '\0';
\r
748 SetEngineState( ed->which, STATE_PONDERING, buf );
\r
750 else if( gameMode == TwoMachinesPlay ) {
\r
751 SetEngineState( ed->which, STATE_THINKING, "" );
\r
753 else if( gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
754 || (gameMode == IcsObserving && appData.icsEngineAnalyze)) { // [HGM] ICS-analyze
\r
756 int time_secs = ed->time / 100;
\r
757 int time_mins = time_secs / 60;
\r
761 if( ed->an_move_index != 0 && ed->an_move_count != 0 && *ed->hint != '\0' ) {
\r
764 strncpy( mov, ed->hint, sizeof(mov) );
\r
765 mov[ sizeof(mov)-1 ] = '\0';
\r
767 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
770 SetEngineState( ed->which, STATE_ANALYZING, buf );
\r
773 SetEngineState( ed->which, STATE_IDLE, "" );
\r
777 DoSetWindowText( ed->which, nLabel, s_label );
\r
781 if( ed->time > 0 && ed->nodes > 0 ) {
\r
782 unsigned long nps_100 = ed->nodes / ed->time;
\r
784 if( nps_100 < 100000 ) {
\r
785 sprintf( s_label, "NPS: %lu", nps_100 * 100 );
\r
788 sprintf( s_label, "NPS: %.1fk", nps_100 / 10.0 );
\r
792 DoSetWindowText( ed->which, nLabelNPS, s_label );
\r
795 if( ed->pv != 0 && *ed->pv != '\0' ) {
\r
801 int time_secs = ed->time / 100;
\r
802 int time_cent = ed->time % 100;
\r
805 if( ed->nodes < 1000000 ) {
\r
806 sprintf( s_nodes, u64Display, ed->nodes );
\r
809 sprintf( s_nodes, "%.1fM", u64ToDouble(ed->nodes) / 1000000.0 );
\r
813 if( ed->score > 0 ) {
\r
814 sprintf( s_score, "+%.2f", ed->score / 100.0 );
\r
817 sprintf( s_score, "%.2f", ed->score / 100.0 );
\r
821 sprintf( s_time, "%d:%02d.%02d", time_secs / 60, time_secs % 60, time_cent );
\r
823 /* Put all together... */
\r
824 if(ed->nodes == 0 && ed->score == 0 && ed->time == 0) sprintf( buf, "%3d\t", ed->depth ); else
\r
825 sprintf( buf, "%3d\t%s\t%s\t%s\t", ed->depth, s_score, s_nodes, s_time );
\r
828 buflen = strlen(buf);
\r
830 strncpy( buf + buflen, ed->pv, sizeof(buf) - buflen );
\r
832 buf[ sizeof(buf) - 3 ] = '\0';
\r
834 strcat( buf + buflen, "\r\n" );
\r
837 InsertIntoMemo( ed->which, buf, InsertionPoint(strlen(buf), ed) );
\r
841 SetEngineColorIcon( ed->which );
\r
845 int EngineOutputIsUp()
\r
847 return engineOutputDialogUp;
\r
850 // [HGM] kibitz: write kibitz line; split window for it if necessary
\r
851 void OutputKibitz(int window, char *text)
\r
853 if(!EngineOutputIsUp()) return;
\r
854 if(!opponentKibitzes) { // on first kibitz of game, clear memos
\r
856 if(gameMode == IcsObserving) DoClearMemo(0);
\r
858 opponentKibitzes = TRUE; // this causes split window DisplayMode in ICS modes.
\r
859 VerifyDisplayMode();
\r
860 if(gameMode == IcsObserving) {
\r
861 DoSetWindowText(0, nLabel, gameInfo.white);
\r
862 SetIcon( 0, nColorIcon, nColorWhite);
\r
863 SetIcon( 0, nStateIcon, nClear);
\r
865 DoSetWindowText(1, nLabel, gameMode == IcsPlayingBlack ? gameInfo.white : gameInfo.black); // opponent name
\r
866 SetIcon( 1, nColorIcon, gameMode == IcsPlayingBlack ? nColorWhite : nColorBlack);
\r
867 SetIcon( 1, nStateIcon, nClear);
\r
868 InsertIntoMemo(window-1, text, 0); // [HGM] multivar: always at top
\r