From 5353e7326ef32081eea7669e885ae3f906ca677d Mon Sep 17 00:00:00 2001 From: H.G. Muller Date: Sun, 6 Dec 2009 12:11:46 -0800 Subject: [PATCH] implements the eval-graph window for XBoard --- Makefile.am | 1 + frontend.h | 4 + xboard.c | 37 ++--- xevalgraph.c | 472 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ xhistory.c | 1 + 5 files changed, 495 insertions(+), 20 deletions(-) create mode 100644 xevalgraph.c diff --git a/Makefile.am b/Makefile.am index 49e7e15..7345205 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,6 +22,7 @@ xboard_SOURCES = backend.c backend.h backendz.h \ xedittags.c xedittags.h \ engineoutput.c engineoutput.h \ xengineoutput.c \ + evalgraph.c evalgraph.h xevalgraph.c \ xgamelist.c xgamelist.h\ xhistory.c xhistory.h \ xoptions.c \ diff --git a/frontend.h b/frontend.h index f4734aa..ea2f842 100644 --- a/frontend.h +++ b/frontend.h @@ -205,5 +205,9 @@ void EngineOutputPopUp P((void)); void EngineOutputPopDown P((void)); int EngineOutputIsUp P((void)); int EngineOutputDialogExists P((void)); +void EvalGraphPopUp P((void)); +void EvalGraphPopDown P((void)); +Boolean EvalGraphIsUp P((void)); +int EvalGraphDialogExists P((void)); #endif diff --git a/xboard.c b/xboard.c index c9546e2..bd46436 100644 --- a/xboard.c +++ b/xboard.c @@ -200,7 +200,9 @@ extern char *getenv(); // must be moved to xengineoutput.h void EngineOutputProc P((Widget w, XEvent *event, - String *prms, Cardinal *nprms)); + String *prms, Cardinal *nprms)); +void EvalGraphProc P((Widget w, XEvent *event, + String *prms, Cardinal *nprms)); #ifdef __EMX__ @@ -605,9 +607,9 @@ MenuItem modeMenu[] = { {N_("Training"), TrainingProc}, {"----", NothingProc}, {N_("Show Engine Output"), EngineOutputProc}, - {N_("Show Evaluation Graph"), NothingProc}, // [HGM] evalgr: not functional yet + {N_("Show Evaluation Graph"), EvalGraphProc}, {N_("Show Game List"), ShowGameListProc}, - {"Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code + {N_("Show Move History"), HistoryShowProc}, // [HGM] hist: activate 4.2.7 code {"----", NothingProc}, {N_("Edit Tags"), EditTagsProc}, {N_("Edit Comment"), EditCommentProc}, @@ -865,6 +867,7 @@ XtActionsRec boardActions[] = { { "EditPositionProc", EditPositionProc }, { "TrainingProc", EditPositionProc }, { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window + { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window { "ShowGameListProc", ShowGameListProc }, { "ShowMoveListProc", HistoryShowProc}, { "EditTagsProc", EditCommentProc }, @@ -944,6 +947,7 @@ XtActionsRec boardActions[] = { { "PromotionPopDown", (XtActionProc) PromotionPopDown }, { "HistoryPopDown", (XtActionProc) HistoryPopDown }, { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown }, + { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown }, { "ShufflePopDown", (XtActionProc) ShufflePopDown }, { "EnginePopDown", (XtActionProc) EnginePopDown }, { "UciPopDown", (XtActionProc) UciPopDown }, @@ -1251,7 +1255,6 @@ BoardToTop() #define SEPCHAR " " // these two must some day move to frontend.h, when they are implemented -Boolean EvalGraphIsUp(); Boolean MoveHistoryIsUp(); Boolean GameListIsUp(); @@ -1261,6 +1264,8 @@ Boolean GameListIsUp(); // front-end part of option handling // [HGM] This platform-dependent table provides the location for storing the color info +extern char *crWhite, * crBlack; + void * colorVariable[] = { &appData.whitePieceColor, @@ -1275,8 +1280,8 @@ colorVariable[] = { NULL, NULL, NULL, - NULL, - NULL, + &crWhite, + &crBlack, NULL }; @@ -1420,15 +1425,12 @@ GetWindowCoords() GetActualPlacement(shellWidget, &wpMain); if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory); + if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph); if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList); if(commentShell) GetActualPlacement(commentShell, &wpComment); else GetActualPlacement(editShell, &wpComment); if(tagsShell) GetActualPlacement(tagsShell, &wpTags); else GetActualPlacement(editTagsShell, &wpTags); -#if 0 - GetActualPlacement(hwndConsole, &wpConsole); - GetActualPlacement(evalGraphDialog, &wpEvalGraph); -#endif } void @@ -1460,12 +1462,6 @@ EnsureOnScreen(int *x, int *y, int minX, int minY) return; } -Boolean -EvalGraphIsUp() -{ - return False; -} - int MainWindowUp() { // [HGM] args: allows testing if main window is realized from back-end @@ -2466,10 +2462,11 @@ XBoard square size (hint): %d\n\ HistoryPopUp(); } -// if( wpEvalGraph.visible ) { -// EvalGraphPopUp(); -// } - + if( wpEvalGraph.visible ) + { + EvalGraphPopUp(); + }; + if( wpEngineOutput.visible ) { EngineOutputPopUp(); } diff --git a/xevalgraph.c b/xevalgraph.c new file mode 100644 index 0000000..3556473 --- /dev/null +++ b/xevalgraph.c @@ -0,0 +1,472 @@ +/* + * Evaluation graph + * + * Author: Alessandro Scotti (Dec 2005) + * Translated to X by H.G.Muller (Nov 2009) + * + * Copyright 2005 Alessandro Scotti + * + * Enhancements Copyright 2009 Free Software Foundation, Inc. + * + * ------------------------------------------------------------------------ + * + * GNU XBoard is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * GNU XBoard is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + * ------------------------------------------------------------------------ + ** See the file ChangeLog for a revision history. */ + +#include "config.h" + +#include +#include +#include +#include + +#if STDC_HEADERS +# include +# include +#else /* not STDC_HEADERS */ +extern char *getenv(); +# if HAVE_STRING_H +# include +# else /* not HAVE_STRING_H */ +# include +# endif /* not HAVE_STRING_H */ +#endif /* not STDC_HEADERS */ + +#if HAVE_UNISTD_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "frontend.h" +#include "backend.h" +#include "xboard.h" +#include "evalgraph.h" +#include "gettext.h" + +#ifdef ENABLE_NLS +# define _(s) gettext (s) +# define N_(s) gettext_noop (s) +#else +# define _(s) (s) +# define N_(s) s +#endif + +#include + +// [HGM] pixmaps of some ICONS used in the engine-outut window +#include "pixmaps/WHITE_14.xpm" +#include "pixmaps/BLACK_14.xpm" +#include "pixmaps/CLEAR_14.xpm" +#include "pixmaps/UNKNOWN_14.xpm" +#include "pixmaps/THINKING_14.xpm" +#include "pixmaps/PONDER_14.xpm" +#include "pixmaps/ANALYZING_14.xpm" + +#ifdef SNAP +#include "wsnap.h" +#endif + +#define _LL_ 100 + +// imports from xboard.c +extern Widget formWidget, shellWidget, boardWidget, menuBarWidget; +extern Display *xDisplay; +extern Window xBoardWindow; +extern int squareSize; +extern Pixmap xMarkPixmap, wIconPixmap, bIconPixmap; +extern char *layoutName; + +Pixmap icons[8]; // [HGM] this front-end array translates back-end icon indicator to handle +Widget outputField[2][7]; // [HGM] front-end array to translate output field to window handle + +/* Imports from backend.c */ + +/* Imports from xboard.c */ +extern Arg layoutArgs[2], formArgs[2], messageArgs[4]; +extern GC coordGC; + +//extern WindowPlacement wpEvalGraph; + +Position evalGraphX = -1, evalGraphY = -1; +Dimension evalGraphW, evalGraphH; +Widget evalGraphShell; +static int evalGraphDialogUp; + +/* Module variables */ + +char *crWhite = "#FFFFB0"; +char *crBlack = "#AD5D3D"; +static Display *yDisplay; +static Window eGraphWindow; + +static GC pens[6]; // [HGM] put all pens in one array +static GC hbrHist[3]; + +#if 0 +static HDC hdcPB = NULL; +static HBITMAP hbmPB = NULL; +#endif + +// [HGM] front-end, added as wrapper to avoid use of LineTo and MoveToEx in other routines (so they can be back-end) +void DrawSegment( int x, int y, int *lastX, int *lastY, int penType ) +{ +static curX, curY; + if(penType != PEN_NONE) + XDrawLine(yDisplay, eGraphWindow, pens[penType], curX, curY, x, y); + if(lastX != NULL) { *lastX = curX; *lastY = curY; } + curX = x; curY = y; +} + +// front-end wrapper for drawing functions to do rectangles +void DrawRectangle( int left, int top, int right, int bottom, int side, int style ) +{ + XFillRectangle(yDisplay, eGraphWindow, hbrHist[side], left, top, right-left, bottom-top); + if(style != FILLED) + XDrawRectangle(yDisplay, eGraphWindow, pens[PEN_BLACK], left, top, right-left-1, bottom-top-1); +} + +// front-end wrapper for putting text in graph +void DrawEvalText(char *buf, int cbBuf, int y) +{ + // the magic constants 7 and 5 should really be derived from the font size somehow + XDrawString(yDisplay, eGraphWindow, coordGC, MarginX - 2 - 7*cbBuf, y+5, buf, cbBuf); +} + +// front-end +static Pixel MakeColor(char *color ) +{ + XrmValue vFrom, vTo; + + vFrom.addr = (caddr_t) color; + vFrom.size = strlen(color); + XtConvert(evalGraphShell, XtRString, &vFrom, XtRPixel, &vTo); + // test for NULL? + + return *(Pixel *) vTo.addr; +} + +static GC CreateGC(int width, char *fg, char *bg, int style) +{ + XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground + | GCBackground | GCFunction | GCPlaneMask; + XGCValues gc_values; + + gc_values.plane_mask = AllPlanes; + gc_values.line_width = width; + gc_values.line_style = style; + gc_values.function = GXcopy; + + gc_values.foreground = MakeColor(fg); + gc_values.background = MakeColor(bg); + + return XtGetGC(evalGraphShell, value_mask, &gc_values); +} + +// front-end. Create pens, device context and buffer bitmap for global use, copy result to display +// The back-end part n the middle has been taken out and moed to PainEvalGraph() +static void DisplayEvalGraph() +{ + int j; + int width; + int height; + Dimension w, h; + Arg args[6]; + + /* Get client area */ + j = 0; + XtSetArg(args[j], XtNwidth, &w); j++; + XtSetArg(args[j], XtNheight, &h); j++; + XtGetValues(evalGraphShell, args, j); + width = w; + height = h; + + /* Create or recreate paint box if needed */ + if( width != nWidthPB || height != nHeightPB ) { + + nWidthPB = width; + nHeightPB = height; + } + + // back-end painting; calls back front-end primitives for lines, rectangles and text + PaintEvalGraph(); + + XSync(yDisplay, False); +} + +static void InitializeEvalGraph() +{ int i; XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground + | GCBackground | GCFunction | GCPlaneMask; + XGCValues gc_values; +// GC copyInvertedGC; + + pens[PEN_BLACK] = CreateGC(1, "black", "black", LineSolid); + pens[PEN_DOTTED] = CreateGC(1, "#A0A0A0", "#A0A0A0", LineOnOffDash); + pens[PEN_BLUEDOTTED] = CreateGC(1, "#0000FF", "#0000FF", LineOnOffDash); + pens[PEN_BOLD] = CreateGC(3, crWhite, crWhite, LineSolid); + pens[PEN_BOLD+1] = CreateGC(3, crBlack, crBlack, LineSolid); + hbrHist[0] = CreateGC(3, crWhite, crWhite, LineSolid); + hbrHist[1] = CreateGC(3, crBlack, crBlack, LineSolid); + hbrHist[2] = CreateGC(3, "#E0E0F0", "#E0E0F0", LineSolid);; // background (a bit blueish, for contrst with yellow curve) +} + +void EvalClick(widget, unused, event) + Widget widget; + caddr_t unused; + XEvent *event; +{ + if( widget && event->type == ButtonPress ) { + int index = GetMoveIndexFromPoint( event->xbutton.x, event->xbutton.y ); + + if( index >= 0 && index < currLast ) { + ToNrEvent( index + 1 ); + } + } +} + +// This (cloned from EventProc in xboard.c) is needed as event handler, to prevent +// the graph being wiped out after covering / uncovering by other windows. +void EvalEventProc(widget, unused, event) + Widget widget; + caddr_t unused; + XEvent *event; +{ + if (!XtIsRealized(widget)) + return; + + switch (event->type) { + case Expose: + if (event->xexpose.count > 0) return; /* no clipping is done */ + DisplayEvalGraph(); + break; + default: + return; + } +} +// The following routines are mutated clones of the commentPopUp routines + +Widget EvalGraphCreate(name) + char *name; +{ + Arg args[16]; + Widget shell, layout, form, form2, edit; + Dimension bw_width, bw_height; + int j; + + // get board width + j = 0; + XtSetArg(args[j], XtNwidth, &bw_width); j++; + XtSetArg(args[j], XtNheight, &bw_height); j++; + XtGetValues(boardWidget, args, j); + + // define form within layout within shell. + j = 0; + XtSetArg(args[j], XtNresizable, True); j++; + shell = +#if TOPLEVEL + XtCreatePopupShell(name, topLevelShellWidgetClass, +#else + XtCreatePopupShell(name, transientShellWidgetClass, +#endif + shellWidget, args, j); + layout = + XtCreateManagedWidget(layoutName, formWidgetClass, shell, + layoutArgs, XtNumber(layoutArgs)); + // divide window vertically into two equal parts, by creating two forms + form = + XtCreateManagedWidget("form", formWidgetClass, layout, + formArgs, XtNumber(formArgs)); + // make sure width is known in advance, for better placement of child widgets + j = 0; + XtSetArg(args[j], XtNwidth, (XtArgVal) bw_width-16); j++; + XtSetArg(args[j], XtNheight, (XtArgVal) bw_height/4); j++; + XtSetValues(shell, args, j); + + XtRealizeWidget(shell); + + if(wpEvalGraph.width > 0) { + evalGraphW = wpEvalGraph.width; + evalGraphH = wpEvalGraph.height; + evalGraphX = wpEvalGraph.x; + evalGraphY = wpEvalGraph.y; + } + + if (evalGraphX == -1) { + int xx, yy; + Window junk; + Dimension pw_height; + Dimension ew_height; + evalGraphH = bw_height/4; + evalGraphW = bw_width-16; + + XSync(xDisplay, False); +#ifdef NOTDEF + /* This code seems to tickle an X bug if it is executed too soon + after xboard starts up. The coordinates get transformed as if + the main window was positioned at (0, 0). + */ + XtTranslateCoords(shellWidget, + (bw_width - evalGraphW) / 2, 0 - evalGraphH / 2, + &evalGraphX, &evalGraphY); +#else /*!NOTDEF*/ + XTranslateCoordinates(xDisplay, XtWindow(shellWidget), + RootWindowOfScreen(XtScreen(shellWidget)), + (bw_width - evalGraphW) / 2, 0 - evalGraphH / 2, + &xx, &yy, &junk); + evalGraphX = xx; + evalGraphY = yy; +#endif /*!NOTDEF*/ + if (evalGraphY < 0) evalGraphY = 0; /*avoid positioning top offscreen*/ + } + j = 0; + XtSetArg(args[j], XtNheight, evalGraphH); j++; + XtSetArg(args[j], XtNwidth, evalGraphW); j++; + XtSetArg(args[j], XtNx, evalGraphX); j++; + XtSetArg(args[j], XtNy, evalGraphY); j++; + XtSetValues(shell, args, j); +// XtSetKeyboardFocus(shell, edit); + + yDisplay = XtDisplay(shell); + eGraphWindow = XtWindow(form); + XtAddEventHandler(form, ExposureMask, False, + (XtEventHandler) EvalEventProc, NULL); + XtAddEventHandler(form, ButtonPressMask, False, + (XtEventHandler) EvalClick, NULL); + + return shell; +} + +void +EvalGraphPopUp() +{ + Arg args[16]; + int j; + Widget edit; + static int needInit = TRUE; + static char *title = _("Evaluation graph"); + + if (evalGraphShell == NULL) { + + evalGraphShell = + EvalGraphCreate(title); + XtRealizeWidget(evalGraphShell); + CatchDeleteWindow(evalGraphShell, "EvalGraphPopDown"); + if( needInit ) { + InitializeEvalGraph(); + needInit = FALSE; + } + } else { + j = 0; + XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++; + XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++; + XtSetValues(evalGraphShell, args, j); + } + + XtPopup(evalGraphShell, XtGrabNone); + XSync(yDisplay, False); + + j=0; + XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++; + XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Show Evaluation Graph"), + args, j); + + evalGraphDialogUp = True; +// ShowThinkingEvent(); // [HGM] thinking: might need to prompt engine for thinking output +} + +void EvalGraphPopDown() +{ + Arg args[16]; + int j; + + if (!evalGraphDialogUp) return; + j = 0; + XtSetArg(args[j], XtNx, &evalGraphX); j++; + XtSetArg(args[j], XtNy, &evalGraphY); j++; + XtSetArg(args[j], XtNwidth, &evalGraphW); j++; + XtSetArg(args[j], XtNheight, &evalGraphH); j++; + XtGetValues(evalGraphShell, args, j); + wpEvalGraph.x = evalGraphX - 4; + wpEvalGraph.y = evalGraphY - 23; + wpEvalGraph.width = evalGraphW; + wpEvalGraph.height = evalGraphH; + XtPopdown(evalGraphShell); + XSync(xDisplay, False); + j=0; + XtSetArg(args[j], XtNleftBitmap, None); j++; + XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Show Evaluation Graph"), + args, j); + + evalGraphDialogUp = False; +// ShowThinkingEvent(); // [HGM] thinking: might need to shut off thinking output +} + +Boolean EvalGraphIsUp() +{ + return evalGraphDialogUp; +} + +int EvalGraphDialogExists() +{ + return evalGraphShell != NULL; +} + +void +EvalGraphProc(w, event, prms, nprms) + Widget w; + XEvent *event; + String *prms; + Cardinal *nprms; +{ + if (evalGraphDialogUp) { + EvalGraphPopDown(); + } else { + EvalGraphPopUp(); + } +} +// This function is the interface to the back-end. It is currently called through the front-end, +// though, where it shares the HistorySet() wrapper with MoveHistorySet(). Once all front-ends +// support the eval graph, it would be more logical to call it directly from the back-end. +void EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo ) +{ + /* [AS] Danger! For now we rely on the pvInfo parameter being a static variable! */ + + currFirst = first; + currLast = last; + currCurrent = current; + currPvInfo = pvInfo; + + if( evalGraphShell ) { + DisplayEvalGraph(); + } +} diff --git a/xhistory.c b/xhistory.c index c48ee32..dca9319 100644 --- a/xhistory.c +++ b/xhistory.c @@ -275,6 +275,7 @@ void HistorySet(char movelist[][2*MOVE_LEN],int first,int last,int current){ else XawListUnhighlight(hist->mvb); } } + EvalGraphSet( first, last, current, pvInfoList ); // piggy-backed } Widget HistoryCreate() -- 1.7.0.4