changes from Alessandro Scotti from 20060112
[xboard.git] / winboard / wengineoutput.c
index 6486508..cac4744 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Engine output (PV)
  *
- * Author: Alessandro Scotti
+ * Author: Alessandro Scotti (Dec 2005)
  *
  * ------------------------------------------------------------------------
  * This program is free software; you can redistribute it and/or modify
@@ -40,6 +40,8 @@ VOID EngineOutputPopUp();
 VOID EngineOutputPopDown();
 BOOL EngineOutputIsUp();
 
+#define SHOW_PONDERING
+
 /* Imports from backend.c */
 char * SavePart(char *str);
 
@@ -58,9 +60,62 @@ extern WindowPlacement wpEngineOutput;
 #define LABEL_V_DISTANCE    1   /* Distance between label and memo */
 #define SPLITTER_SIZE       4   /* Distance between first memo and second label */
 
+#define ICON_SIZE           14
+
+#define STATE_UNKNOWN   -1
+#define STATE_THINKING   0
+#define STATE_IDLE       1
+#define STATE_PONDERING  2
+
 static int  windowMode = 1;
 
+static BOOL needInit = TRUE;
+
+static HICON hiColorBlack = NULL;
+static HICON hiColorWhite = NULL;
+static HICON hiColorUnknown = NULL;
+static HICON hiClear = NULL;
+static HICON hiPondering = NULL;
+static HICON hiThinking = NULL;
+
 static int  lastDepth[2] = { -1, -1 };
+static int  lastForwardMostMove[2] = { -1, -1 };
+static int  engineState[2] = { -1, -1 };
+
+typedef struct {
+    int which;
+    HWND hColorIcon;
+    HWND hLabel;
+    HWND hStateIcon;
+    HWND hStateData;
+    HWND hLabelNPS;
+    HWND hMemo;
+    char * name;
+    int depth;
+    unsigned long nodes;
+    int score;
+    int time;
+    char * pv;
+    char * hint;
+} EngineOutputData;
+
+static HICON LoadIconEx( int id )
+{
+    return LoadImage( hInst, MAKEINTRESOURCE(id), IMAGE_ICON, ICON_SIZE, ICON_SIZE, 0 );
+}
+
+static VOID InitializeEngineOutput()
+{
+    if( needInit ) {
+        hiColorBlack = LoadIconEx( IDI_BLACK_14 );
+        hiColorWhite = LoadIconEx( IDI_WHITE_14 );
+        hiColorUnknown = LoadIconEx( IDI_UNKNOWN_14 );
+        hiClear = LoadIconEx( IDI_TRANS_14 );
+        hiPondering = LoadIconEx( IDI_PONDER_14 );
+        hiThinking = LoadIconEx( IDI_CLOCK_14 );
+        needInit = FALSE;
+    }
+}
 
 static VOID SetControlPos( HWND hDlg, int id, int x, int y, int width, int height )
 {
@@ -69,35 +124,132 @@ static VOID SetControlPos( HWND hDlg, int id, int x, int y, int width, int heigh
     SetWindowPos( hControl, HWND_TOP, x, y, width, height, SWP_NOZORDER );
 }
 
+#define HIDDEN_X    20000
+#define HIDDEN_Y    20000
+
 static VOID HideControl( HWND hDlg, int id )
 {
-    /* TODO: we should also hide/disable it!!! what about tab stops?!?! */
+    HWND hControl = GetDlgItem( hDlg, id );
+    RECT rc;
+
+    GetWindowRect( hControl, &rc );
+
+    /*
+        Avoid hiding an already hidden control, because that causes many
+        unnecessary WM_ERASEBKGND messages!
+    */
+    if( rc.left != HIDDEN_X || rc.top != HIDDEN_Y ) {
     SetControlPos( hDlg, id, 20000, 20000, 100, 100 );
+    }
 }
 
 static int GetControlWidth( HWND hDlg, int id )
 {
     RECT rc;
 
-    GetWindowRect( GetDlgItem( hDlg, IDC_EngineLabel1 ), &rc );
+    GetWindowRect( GetDlgItem( hDlg, id ), &rc );
 
     return rc.right - rc.left;
 }
 
+static int GetControlHeight( HWND hDlg, int id )
+{
+    RECT rc;
+
+    GetWindowRect( GetDlgItem( hDlg, id ), &rc );
+
+    return rc.bottom - rc.top;
+}
+
+static int GetHeaderHeight()
+{
+    int result = GetControlHeight( engineOutputDialog, IDC_EngineLabel1 );
+
+    if( result < ICON_SIZE ) result = ICON_SIZE;
+
+    return result;
+}
+
+#define ENGINE_COLOR_WHITE      'w'
+#define ENGINE_COLOR_BLACK      'b'
+#define ENGINE_COLOR_UNKNOWN    ' '
+
+char GetEngineColor( int which )
+{
+    char result = ENGINE_COLOR_UNKNOWN;
+
+    if( which == 0 || which == 1 ) {
+        ChessProgramState * cps;
+
+        switch (gameMode) {
+        case MachinePlaysBlack:
+        case IcsPlayingBlack:
+            result = ENGINE_COLOR_BLACK;
+            break;
+        case MachinePlaysWhite:
+        case IcsPlayingWhite:
+            result = ENGINE_COLOR_WHITE;
+            break;
+        case AnalyzeMode:
+        case AnalyzeFile:
+            result = WhiteOnMove(forwardMostMove) ? ENGINE_COLOR_WHITE : ENGINE_COLOR_BLACK;
+            break;
+        case TwoMachinesPlay:
+            cps = (which == 0) ? &first : &second;
+            result = cps->twoMachinesColor[0];
+            result = result == 'w' ? ENGINE_COLOR_WHITE : ENGINE_COLOR_BLACK;
+            break;
+        }
+    }
+
+    return result;
+}
+
+char GetActiveEngineColor()
+{
+    char result = ENGINE_COLOR_UNKNOWN;
+
+    if( gameMode == TwoMachinesPlay ) {
+        result = WhiteOnMove(forwardMostMove) ? ENGINE_COLOR_WHITE : ENGINE_COLOR_BLACK;
+    }
+
+    return result;
+}
+
+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 )
+{
+    int label_x = x + ICON_SIZE + H_MARGIN;
+    int label_h = GetControlHeight( hDlg, IDC_EngineLabel1 );
+    int label_y = y + ICON_SIZE - label_h;
+    int nps_w = GetControlWidth( hDlg, IDC_Engine1_NPS );
+    int nps_x = clientWidth - H_MARGIN - nps_w;
+    int state_data_w = GetControlWidth( hDlg, IDC_StateData1 );
+    int state_data_x = nps_x - H_MARGIN - state_data_w;
+    int state_icon_x = state_data_x - ICON_SIZE - 2;
+    int max_w = clientWidth - 2*H_MARGIN;
+    int memo_y = y + ICON_SIZE + LABEL_V_DISTANCE;
+
+    SetControlPos( hDlg, idColor, x, y, ICON_SIZE, ICON_SIZE );
+    SetControlPos( hDlg, idEngineLabel, label_x, label_y, max_w / 2, label_h );
+    SetControlPos( hDlg, idStateIcon, state_icon_x, y, ICON_SIZE, ICON_SIZE );
+    SetControlPos( hDlg, idStateData, state_data_x, label_y, state_data_w, label_h );
+    SetControlPos( hDlg, idNPS, nps_x, label_y, nps_w, label_h );
+    SetControlPos( hDlg, idMemo, x, memo_y, max_w, memoHeight );
+}
+
 static VOID ResizeWindowControls( HWND hDlg, int mode )
 {
     RECT rc;
-    int labelHeight;
+    int headerHeight = GetHeaderHeight();
+    int labelHeight = GetControlHeight( hDlg, IDC_EngineLabel1 );
+    int labelOffset = H_MARGIN + ICON_SIZE + H_MARGIN;
+    int labelDeltaY = ICON_SIZE - labelHeight;
     int clientWidth;
     int clientHeight;
     int maxControlWidth;
     int npsWidth;
 
     /* Initialize variables */
-    GetWindowRect( GetDlgItem( hDlg, IDC_EngineLabel1 ), &rc );
-
-    labelHeight = rc.bottom - rc.top;
-
     GetClientRect( hDlg, &rc );
 
     clientWidth = rc.right - rc.left;
@@ -110,33 +262,32 @@ static VOID ResizeWindowControls( HWND hDlg, int mode )
     /* Resize controls */
     if( mode == 0 ) {
         /* One engine */
-        int memo_y = V_MARGIN + labelHeight + LABEL_V_DISTANCE;
-        int memo_h = clientHeight - memo_y - V_MARGIN;
-
-        SetControlPos( hDlg, IDC_EngineLabel1, H_MARGIN, V_MARGIN, maxControlWidth / 2, labelHeight );
-        SetControlPos( hDlg, IDC_EngineMemo1, H_MARGIN, memo_y, maxControlWidth, memo_h );
+        PositionControlSet( hDlg, H_MARGIN, V_MARGIN,
+            clientWidth,
+            clientHeight - V_MARGIN - LABEL_V_DISTANCE - headerHeight- V_MARGIN,
+            IDC_Color1, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineMemo1, IDC_StateIcon1, IDC_StateData1 );
 
         /* Hide controls for the second engine */
+        HideControl( hDlg, IDC_Color2 );
         HideControl( hDlg, IDC_EngineLabel2 );
+        HideControl( hDlg, IDC_StateIcon2 );
+        HideControl( hDlg, IDC_StateData2 );
         HideControl( hDlg, IDC_Engine2_NPS );
         HideControl( hDlg, IDC_EngineMemo2 );
+        SendDlgItemMessage( hDlg, IDC_EngineMemo2, WM_SETTEXT, 0, (LPARAM) "" );
         /* TODO: we should also hide/disable them!!! what about tab stops?!?! */
     }
     else {
         /* Two engines */
-        int memo1_y = V_MARGIN + labelHeight + LABEL_V_DISTANCE;
-        int memo_h = (clientHeight - memo1_y - V_MARGIN - labelHeight - LABEL_V_DISTANCE - SPLITTER_SIZE) / 2;
-        int label2_y = memo1_y + memo_h + SPLITTER_SIZE;
-        int memo2_y = label2_y + labelHeight + LABEL_V_DISTANCE;
-        int nps_x = clientWidth - H_MARGIN - npsWidth;
-
-        SetControlPos( hDlg, IDC_EngineLabel1, H_MARGIN, V_MARGIN, maxControlWidth / 2, labelHeight );
-        SetControlPos( hDlg, IDC_Engine1_NPS, nps_x, V_MARGIN, npsWidth, labelHeight );
-        SetControlPos( hDlg, IDC_EngineMemo1, H_MARGIN, memo1_y, maxControlWidth, memo_h );
-
-        SetControlPos( hDlg, IDC_EngineLabel2, H_MARGIN, label2_y, maxControlWidth / 2, labelHeight );
-        SetControlPos( hDlg, IDC_Engine2_NPS, nps_x, label2_y, npsWidth, labelHeight );
-        SetControlPos( hDlg, IDC_EngineMemo2, H_MARGIN, memo2_y, maxControlWidth, memo_h );
+        int memo_h = (clientHeight - headerHeight*2 - V_MARGIN*2 - LABEL_V_DISTANCE*2 - SPLITTER_SIZE) / 2;
+        int header1_y = V_MARGIN;
+        int header2_y = V_MARGIN + headerHeight + LABEL_V_DISTANCE + memo_h + SPLITTER_SIZE;
+
+        PositionControlSet( hDlg, H_MARGIN, header1_y, clientWidth, memo_h,
+            IDC_Color1, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineMemo1, IDC_StateIcon1, IDC_StateData1 );
+
+        PositionControlSet( hDlg, H_MARGIN, header2_y, clientWidth, memo_h,
+            IDC_Color2, IDC_EngineLabel2, IDC_Engine2_NPS, IDC_EngineMemo2, IDC_StateIcon2, IDC_StateData2 );
     }
 
     InvalidateRect( GetDlgItem(hDlg,IDC_EngineMemo1), NULL, FALSE );
@@ -162,6 +313,8 @@ static VOID VerifyDisplayMode()
     case AnalyzeFile:
     case MachinePlaysWhite:
     case MachinePlaysBlack:
+    case IcsPlayingWhite:
+    case IcsPlayingBlack:
         mode = 0;
         break;
     case TwoMachinesPlay:
@@ -182,11 +335,65 @@ static VOID InsertIntoMemo( HWND hMemo, char * text )
     SendMessage( hMemo, EM_REPLACESEL, (WPARAM) FALSE, (LPARAM) text );
 }
 
+static VOID SetIcon( HWND hControl, HICON hIcon )
+{
+    if( hIcon != NULL ) {
+        SendMessage( hControl, STM_SETICON, (WPARAM) hIcon, 0 );
+    }
+}
+
+static VOID SetEngineColorIcon( HWND hControl, int which )
+{
+    char color = GetEngineColor(which);
+    HICON hicon = NULL;
+
+    if( color == ENGINE_COLOR_BLACK )
+        hicon = hiColorBlack;
+    else if( color == ENGINE_COLOR_WHITE )
+        hicon = hiColorWhite;
+    else
+        hicon = hiColorUnknown;
+
+    SetIcon( hControl, hicon );
+}
+
+static SetEngineState( int which, int state, char * state_data )
+{
+    int x_which = 1 - which;
+    HWND hStateIcon = GetDlgItem( engineOutputDialog, which == 0 ? IDC_StateIcon1 : IDC_StateIcon2 );
+    HWND hStateData = GetDlgItem( engineOutputDialog, which == 0 ? IDC_StateData1 : IDC_StateData2 );
+
+    if( engineState[ which ] != state ) {
+        engineState[ which ] = state;
+
+        switch( state ) {
+        case STATE_THINKING:
+            SetIcon( hStateIcon, hiThinking );
+            if( engineState[ x_which ] == STATE_THINKING ) {
+                SetEngineState( x_which, STATE_IDLE, "" );
+            }
+            break;
+        case STATE_PONDERING:
+            SetIcon( hStateIcon, hiPondering );
+            break;
+        default:
+            SetIcon( hStateIcon, hiClear );
+            break;
+        }
+    }
+
+    if( state_data != 0 ) {
+        SetWindowText( hStateData, state_data );
+    }
+}
+
 #define MAX_NAME_LENGTH 32
 
-static VOID UpdateControls( HWND hLabel, HWND hLabelNPS, HWND hMemo, char * name, int depth, unsigned long nodes, int score, int time, char * pv )
+static VOID UpdateControls( EngineOutputData * ed )
 {
-    char s_label[MAX_NAME_LENGTH + 64];
+    char s_label[MAX_NAME_LENGTH + 32];
+
+    char * name = ed->name;
 
     /* Label */
     if( name == 0 || *name == '\0' ) {
@@ -196,12 +403,47 @@ static VOID UpdateControls( HWND hLabel, HWND hLabelNPS, HWND hMemo, char * name
     strncpy( s_label, name, MAX_NAME_LENGTH );
     s_label[ MAX_NAME_LENGTH-1 ] = '\0';
 
-    SetWindowText( hLabel, s_label );
+#ifdef SHOW_PONDERING
+    if( GetActiveEngineColor() != ENGINE_COLOR_UNKNOWN ) {
+        if( GetEngineColor(ed->which) != GetActiveEngineColor() ) {
+            char buf[8];
+
+            buf[0] = '\0';
+
+            if( ed->hint != 0 && *ed->hint != '\0' ) {
+                strncpy( buf, ed->hint, sizeof(buf) );
+                buf[sizeof(buf)-1] = '\0';
+            }
+            else if( ed->pv != 0 && *ed->pv != '\0' ) {
+                char * sep = strchr( ed->pv, ' ' );
+                int buflen = sizeof(buf);
+
+                if( sep != NULL ) {
+                    buflen = sep - ed->pv + 1;
+                    if( buflen > sizeof(buf) ) buflen = sizeof(buf);
+                }
+
+                strncpy( buf, ed->pv, buflen );
+                buf[ buflen-1 ] = '\0';
+            }
+
+            SetEngineState( ed->which, STATE_PONDERING, buf );
+        }
+        else {
+            SetEngineState( ed->which, STATE_THINKING, "" );
+        }
+    }
+    else {
+        SetEngineState( ed->which, STATE_IDLE, "" );
+    }
+#endif
+
+    SetWindowText( ed->hLabel, s_label );
 
     s_label[0] = '\0';
 
-    if( time > 0 && nodes > 0 ) {
-        unsigned long nps_100 = nodes / time;
+    if( ed->time > 0 && ed->nodes > 0 ) {
+        unsigned long nps_100 = ed->nodes / ed->time;
 
         if( nps_100 < 100000 ) {
             sprintf( s_label, "NPS: %lu", nps_100 * 100 );
@@ -211,52 +453,55 @@ static VOID UpdateControls( HWND hLabel, HWND hLabelNPS, HWND hMemo, char * name
         }
     }
 
-    SetWindowText( hLabelNPS, s_label );
+    SetWindowText( ed->hLabelNPS, s_label );
 
     /* Memo */
-    if( pv != 0 && *pv != '\0' ) {
+    if( ed->pv != 0 && *ed->pv != '\0' ) {
         char s_nodes[24];
         char s_score[16];
         char s_time[24];
         char buf[256];
         int buflen;
-        int time_secs = time / 100;
-        int time_cent = time % 100;
+        int time_secs = ed->time / 100;
+        int time_cent = ed->time % 100;
 
         /* Nodes */
-        if( nodes < 1000000 ) {
-            sprintf( s_nodes, "%lu", nodes );
+        if( ed->nodes < 1000000 ) {
+            sprintf( s_nodes, "%lu", ed->nodes );
         }
         else {
-            sprintf( s_nodes, "%.1fM", nodes / 1000000.0 );
+            sprintf( s_nodes, "%.1fM", ed->nodes / 1000000.0 );
         }
 
         /* Score */
-        if( score > 0 ) {
-            sprintf( s_score, "+%.2f", score / 100.0 );
+        if( ed->score > 0 ) {
+            sprintf( s_score, "+%.2f", ed->score / 100.0 );
         }
         else {
-            sprintf( s_score, "%.2f", score / 100.0 );
+            sprintf( s_score, "%.2f", ed->score / 100.0 );
         }
 
         /* Time */
         sprintf( s_time, "%d:%02d.%02d", time_secs / 60, time_secs % 60, time_cent );
 
         /* Put all together... */
-        sprintf( buf, "%3d\t%s\t%s\t%s\t", depth, s_score, s_nodes, s_time );
+        sprintf( buf, "%3d\t%s\t%s\t%s\t", ed->depth, s_score, s_nodes, s_time );
 
         /* Add PV */
         buflen = strlen(buf);
 
-        strncpy( buf + buflen, pv, sizeof(buf) - buflen );
+        strncpy( buf + buflen, ed->pv, sizeof(buf) - buflen );
 
         buf[ sizeof(buf) - 3 ] = '\0';
 
         strcat( buf + buflen, "\r\n" );
 
         /* Update memo */
-        InsertIntoMemo( hMemo, buf );
+        InsertIntoMemo( ed->hMemo, buf );
     }
+
+    /* Colors */
+    SetEngineColorIcon( ed->hColorIcon, ed->which );
 }
 
 LRESULT CALLBACK EngineOutputProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
@@ -271,6 +516,9 @@ LRESULT CALLBACK EngineOutputProc( HWND hDlg, UINT message, WPARAM wParam, LPARA
             RestoreWindowPlacement( hDlg, &wpEngineOutput ); /* Restore window placement */
 
             ResizeWindowControls( hDlg, windowMode );
+
+            SetEngineState( 0, STATE_IDLE, "" );
+            SetEngineState( 1, STATE_IDLE, "" );
         }
 
         return FALSE;
@@ -328,6 +576,10 @@ VOID EngineOutputPopUp()
 {
   FARPROC lpProc;
 
+  if( needInit ) {
+      InitializeEngineOutput();
+  }
+
   CheckMenuItem(GetMenu(hwndMain), IDM_ShowEngineOutput, MF_CHECKED);
 
   if( engineOutputDialog ) {
@@ -365,12 +617,10 @@ BOOL EngineOutputIsUp()
     return engineOutputDialogUp;
 }
 
-VOID EngineOutputUpdate( int which, int depth, unsigned long nodes, int score, int time, char * pv )
+VOID EngineOutputUpdate( int which, int depth, unsigned long nodes, int score, int time, char * pv, char * hint )
 {
-    HWND hLabel;
-    HWND hLabelNPS;
-    HWND hMemo;
-    char * name;
+    EngineOutputData ed;
+    BOOL clearMemo = FALSE;
 
     if( which < 0 || which > 1 || depth < 0 || time < 0 || pv == 0 || *pv == '\0' ) {
         return;
@@ -382,33 +632,56 @@ VOID EngineOutputUpdate( int which, int depth, unsigned long nodes, int score, i
 
     VerifyDisplayMode();
 
+    ed.which = which;
+    ed.depth = depth;
+    ed.nodes = nodes;
+    ed.score = score;
+    ed.time = time;
+    ed.pv = pv;
+    ed.hint = hint;
+
     /* Get target control */
     if( which == 0 ) {
-        hLabel = GetDlgItem( engineOutputDialog, IDC_EngineLabel1 );
-        hLabelNPS = GetDlgItem( engineOutputDialog, IDC_Engine1_NPS );
-        hMemo  = GetDlgItem( engineOutputDialog, IDC_EngineMemo1 );
-        name = first.tidy;
+        ed.hColorIcon = GetDlgItem( engineOutputDialog, IDC_Color1 );
+        ed.hLabel = GetDlgItem( engineOutputDialog, IDC_EngineLabel1 );
+        ed.hStateIcon = GetDlgItem( engineOutputDialog, IDC_StateIcon1 );
+        ed.hStateData = GetDlgItem( engineOutputDialog, IDC_StateData1 );
+        ed.hLabelNPS = GetDlgItem( engineOutputDialog, IDC_Engine1_NPS );
+        ed.hMemo  = GetDlgItem( engineOutputDialog, IDC_EngineMemo1 );
+        ed.name = first.tidy;
     }
     else {
-        hLabel = GetDlgItem( engineOutputDialog, IDC_EngineLabel2 );
-        hLabelNPS = GetDlgItem( engineOutputDialog, IDC_Engine2_NPS );
-        hMemo  = GetDlgItem( engineOutputDialog, IDC_EngineMemo2 );
-        name = second.tidy;
+        ed.hColorIcon = GetDlgItem( engineOutputDialog, IDC_Color2 );
+        ed.hLabel = GetDlgItem( engineOutputDialog, IDC_EngineLabel2 );
+        ed.hStateIcon = GetDlgItem( engineOutputDialog, IDC_StateIcon2 );
+        ed.hStateData = GetDlgItem( engineOutputDialog, IDC_StateData2 );
+        ed.hLabelNPS = GetDlgItem( engineOutputDialog, IDC_Engine2_NPS );
+        ed.hMemo  = GetDlgItem( engineOutputDialog, IDC_EngineMemo2 );
+        ed.name = second.tidy;
     }
 
     /* Clear memo if needed */
     if( lastDepth[which] > depth || (lastDepth[which] == depth && depth <= 1) ) {
-        SendMessage( hMemo, WM_SETTEXT, 0, (LPARAM) "" );
+        clearMemo = TRUE;
+    }
+
+    if( lastForwardMostMove[which] != forwardMostMove ) {
+        clearMemo = TRUE;
+    }
+
+    if( clearMemo ) {
+        SendMessage( ed.hMemo, WM_SETTEXT, 0, (LPARAM) "" );
     }
 
     /* Update */
     lastDepth[which] = depth;
+    lastForwardMostMove[which] = forwardMostMove;
 
     if( pv[0] == ' ' ) {
         if( strncmp( pv, " no PV", 6 ) == 0 ) { /* Hack on hack! :-O */
-            pv = "";
+            ed.pv = "";
         }
     }
 
-    UpdateControls( hLabel, hLabelNPS, hMemo, name, depth, nodes, score, time, pv );
+    UpdateControls( &ed );
 }