updated copyright to reflect A. Scotte as copyright holder
[xboard.git] / xengineoutput.c
1 /*\r
2  * Engine output (PV)\r
3  *\r
4  * Author: Alessandro Scotti (Dec 2005)\r
5  *\r
6  * Copyright 2005 Alessandro Scotti\r
7  *\r
8  * Enhancements Copyright 2009 Free Software Foundation, Inc.\r
9  *\r
10  * ------------------------------------------------------------------------\r
11  *\r
12  * GNU XBoard is free software: you can redistribute it and/or modify\r
13  * it under the terms of the GNU General Public License as published by\r
14  * the Free Software Foundation, either version 3 of the License, or (at\r
15  * your option) any later version.\r
16  *\r
17  * GNU XBoard is distributed in the hope that it will be useful, but\r
18  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
20  * General Public License for more details.\r
21  *\r
22  * You should have received a copy of the GNU General Public License\r
23  * along with this program. If not, see http://www.gnu.org/licenses/.\r
24  *\r
25  * ------------------------------------------------------------------------\r
26  ** See the file ChangeLog for a revision history.  */\r
27 \r
28 #include "config.h"\r
29 \r
30 #include <stdio.h>\r
31 #include <ctype.h>\r
32 #include <errno.h>\r
33 #include <sys/types.h>\r
34 \r
35 #if STDC_HEADERS\r
36 # include <stdlib.h>\r
37 # include <string.h>\r
38 #else /* not STDC_HEADERS */\r
39 extern char *getenv();\r
40 # if HAVE_STRING_H\r
41 #  include <string.h>\r
42 # else /* not HAVE_STRING_H */\r
43 #  include <strings.h>\r
44 # endif /* not HAVE_STRING_H */\r
45 #endif /* not STDC_HEADERS */\r
46 \r
47 #if HAVE_UNISTD_H\r
48 # include <unistd.h>\r
49 #endif\r
50 \r
51 #include <X11/Intrinsic.h>\r
52 #include <X11/StringDefs.h>\r
53 #include <X11/Shell.h>\r
54 #include <X11/Xaw/Dialog.h>\r
55 #include <X11/Xaw/Form.h>\r
56 #include <X11/Xaw/List.h>\r
57 #include <X11/Xaw/Label.h>\r
58 #include <X11/Xaw/SimpleMenu.h>\r
59 #include <X11/Xaw/SmeBSB.h>\r
60 #include <X11/Xaw/SmeLine.h>\r
61 #include <X11/Xaw/Box.h>\r
62 #include <X11/Xaw/Paned.h>\r
63 #include <X11/Xaw/MenuButton.h>\r
64 #include <X11/cursorfont.h>\r
65 #include <X11/Xaw/Text.h>\r
66 #include <X11/Xaw/AsciiText.h>\r
67 #include <X11/Xaw/Viewport.h>\r
68 \r
69 #include "common.h"\r
70 #include "frontend.h"\r
71 #include "backend.h"\r
72 #include "xboard.h"\r
73 // Add xengineo.h later\r
74 #include "gettext.h"\r
75 \r
76 #ifdef ENABLE_NLS\r
77 # define  _(s) gettext (s)\r
78 # define N_(s) gettext_noop (s)\r
79 #else\r
80 # define  _(s) (s)\r
81 # define N_(s)  s\r
82 #endif\r
83 \r
84 #include <X11/xpm.h>\r
85 \r
86 // [HGM] pixmaps of some ICONS used in the engine-outut window\r
87 #include "pixmaps/WHITE_14.xpm"\r
88 #include "pixmaps/BLACK_14.xpm"\r
89 #include "pixmaps/CLEAR_14.xpm"\r
90 #include "pixmaps/UNKNOWN_14.xpm"\r
91 #include "pixmaps/THINKING_14.xpm"\r
92 #include "pixmaps/PONDER_14.xpm"\r
93 #include "pixmaps/ANALYZING_14.xpm"\r
94 \r
95 #ifdef SNAP\r
96 #include "wsnap.h"\r
97 #endif\r
98 \r
99 #define _LL_ 100\r
100 \r
101 // imports from xboard.c\r
102 extern Widget formWidget, shellWidget, boardWidget, menuBarWidget;\r
103 extern Display *xDisplay;\r
104 extern Window xBoardWindow;\r
105 extern int squareSize;\r
106 extern Pixmap xMarkPixmap, wIconPixmap, bIconPixmap;\r
107 extern char *layoutName;\r
108 \r
109 // temporary kludge to avoid compile errors untill all Windows code has been replaced\r
110 #define HICON int *\r
111 #define HWND  int *\r
112 \r
113 // [HGM] define numbers to indicate icons, for referring to them in platform-independent way\r
114 #define nColorBlack   1\r
115 #define nColorWhite   2\r
116 #define nColorUnknown 3\r
117 #define nClear        4\r
118 #define nPondering    5\r
119 #define nThinking     6\r
120 #define nAnalyzing    7\r
121 \r
122 Pixmap icons[8]; // [HGM] this front-end array translates back-end icon indicator to handle\r
123 \r
124 // [HGM] same for output fields (note that there are two of each type, one per color)\r
125 #define nColorIcon 1\r
126 #define nStateIcon 2\r
127 #define nLabel     3\r
128 #define nStateData 4\r
129 #define nLabelNPS  5\r
130 #define nMemo      6\r
131 \r
132 Widget outputField[2][7]; // [HGM] front-end array to translate output field to window handle\r
133 \r
134 void EngineOutputPopDown();\r
135 void engineOutputPopUp(char *title, char *text);\r
136 int  EngineOutputIsUp();\r
137 static void SetEngineColorIcon( int which );\r
138 \r
139 #define SHOW_PONDERING\r
140 \r
141 /* Imports from backend.c */\r
142 char * SavePart(char *str);\r
143 extern int opponentKibitzes;\r
144 \r
145 /* Imports from winboard.c */\r
146 //extern HWND engineOutputDialog;\r
147 extern Arg layoutArgs[2], formArgs[2], messageArgs[4];\r
148 \r
149 //extern WindowPlacement wpEngineOutput;\r
150 \r
151 Position engineOutputX = -1, engineOutputY = -1;\r
152 Dimension engineOutputW, engineOutputH;\r
153 Widget engineOutputShell;\r
154 int engineOutputDialogUp;\r
155 \r
156 /* Module variables */\r
157 #define H_MARGIN            2\r
158 #define V_MARGIN            2\r
159 #define LABEL_V_DISTANCE    1   /* Distance between label and memo */\r
160 #define SPLITTER_SIZE       4   /* Distance between first memo and second label */\r
161 \r
162 #define ICON_SIZE           14\r
163 \r
164 #define STATE_UNKNOWN   -1\r
165 #define STATE_THINKING   0\r
166 #define STATE_IDLE       1\r
167 #define STATE_PONDERING  2\r
168 #define STATE_ANALYZING  3\r
169 \r
170 static int  windowMode = 1;\r
171 \r
172 static int  needInit = TRUE;\r
173 \r
174 static int  lastDepth[2] = { -1, -1 };\r
175 static int  lastForwardMostMove[2] = { -1, -1 };\r
176 static int  engineState[2] = { -1, -1 };\r
177 \r
178 typedef struct {\r
179     char * name;\r
180     int which;\r
181     int depth;\r
182     u64 nodes;\r
183     int score;\r
184     int time;\r
185     char * pv;\r
186     char * hint;\r
187     int an_move_index;\r
188     int an_move_count;\r
189 } EngineOutputData;\r
190 \r
191 static int VerifyDisplayMode();\r
192 static void UpdateControls( EngineOutputData * ed );\r
193 static SetEngineState( int which, int state, char * state_data );\r
194 \r
195 void ReadIcon(char *pixData[], int iconNr)\r
196 {\r
197     int r;\r
198 \r
199         if ((r=XpmCreatePixmapFromData(xDisplay, XtWindow(outputField[0][nColorIcon]),\r
200                                        pixData,\r
201                                        &(icons[iconNr]),\r
202                                        NULL, NULL /*&attr*/)) != 0) {\r
203           fprintf(stderr, _("Error %d loading icon image\n"), r);\r
204           exit(1); \r
205         }       \r
206 }\r
207 \r
208 static void InitializeEngineOutput()\r
209 { int i;\r
210 \r
211         ReadIcon(WHITE_14,   nColorWhite);\r
212         ReadIcon(BLACK_14,   nColorBlack);\r
213         ReadIcon(UNKNOWN_14, nColorUnknown);\r
214 \r
215         ReadIcon(CLEAR_14,   nClear);\r
216         ReadIcon(PONDER_14,  nPondering);\r
217         ReadIcon(THINK_14,   nThinking);\r
218         ReadIcon(ANALYZE_14, nAnalyzing);\r
219 }\r
220 \r
221 void DoSetWindowText(int which, int field, char *s_label)\r
222\r
223         Arg arg;\r
224 \r
225         XtSetArg(arg, XtNlabel, (XtArgVal) s_label);\r
226         XtSetValues(outputField[which][field], &arg, 1);\r
227 }\r
228 \r
229 static void InsertIntoMemo( int which, char * text )\r
230 {\r
231         Arg arg; XawTextBlock t; Widget edit;\r
232 \r
233         t.ptr = text; t.firstPos = 0; t.length = strlen(text); t.format = XawFmt8Bit;\r
234         edit = XtNameToWidget(engineOutputShell, which ? "*form2.text" : "*form.text");\r
235         XawTextReplace(edit, 0, 0, &t);\r
236 //      XtSetArg(arg, XtNstring, (XtArgVal) text);\r
237 //      XtSetValues(outputField[which][nMemo], &arg, 1);\r
238 }\r
239 \r
240 static void SetIcon( int which, int field, int nIcon )\r
241 {\r
242     Arg arg;\r
243 \r
244     if( nIcon != 0 ) {\r
245         XtSetArg(arg, XtNleftBitmap, (XtArgVal) icons[nIcon]);\r
246         XtSetValues(outputField[which][field], &arg, 1);\r
247     }\r
248 }\r
249 \r
250 void DoClearMemo(int which)\r
251\r
252     Arg args[16];\r
253     int j;\r
254     Widget edit;\r
255 \r
256         edit = XtNameToWidget(engineOutputShell, which ? "*form2.text" : "*form.text");\r
257         XtCallActionProc(edit, "select-all", NULL, NULL, 0);\r
258         XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);\r
259 }\r
260 \r
261 // The following routines are mutated clones of the commentPopUp routines\r
262 \r
263 void PositionControlSet(which, form, bw_width)\r
264      int which;\r
265      Widget form;\r
266      Dimension bw_width;\r
267 {\r
268     Arg args[16];\r
269     Widget edit, NameWidget, ColorWidget, ModeWidget, MoveWidget, NodesWidget;\r
270     int j, mutable=1;\r
271     j = 0;\r
272     XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;\r
273     XtSetArg(args[j], XtNlabel,     (XtArgVal) ""); j++;\r
274     XtSetArg(args[j], XtNtop,       XtChainTop); j++;\r
275     XtSetArg(args[j], XtNbottom,    XtChainTop); j++;\r
276     XtSetArg(args[j], XtNleft,      XtChainLeft); j++;\r
277     XtSetArg(args[j], XtNright,     XtChainLeft); j++;\r
278     XtSetArg(args[j], XtNheight,    (XtArgVal) 16); j++;\r
279     XtSetArg(args[j], XtNwidth,     (XtArgVal) 17); j++;\r
280     outputField[which][nColorIcon] = ColorWidget =\r
281       XtCreateManagedWidget("Color", labelWidgetClass,\r
282                      form, args, j);\r
283 \r
284     j = 0;\r
285     XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;\r
286     XtSetArg(args[j], XtNjustify,   (XtArgVal) XtJustifyLeft); j++;\r
287     XtSetArg(args[j], XtNfromHoriz, (XtArgVal) ColorWidget); j++;\r
288     XtSetArg(args[j], XtNtop,       XtChainTop); j++;\r
289     XtSetArg(args[j], XtNbottom,    XtChainTop); j++;\r
290     XtSetArg(args[j], XtNleft,      XtChainLeft); j++;\r
291     XtSetArg(args[j], XtNheight,    (XtArgVal) 16); j++;\r
292     XtSetArg(args[j], XtNwidth,     (XtArgVal) bw_width/2 - 57); j++;\r
293     outputField[which][nLabel] = NameWidget =\r
294       XtCreateManagedWidget("Engine", labelWidgetClass,\r
295                      form, args, j);\r
296 \r
297     j = 0;\r
298     XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;\r
299     XtSetArg(args[j], XtNlabel,     (XtArgVal) ""); j++;\r
300     XtSetArg(args[j], XtNfromHoriz, (XtArgVal) NameWidget); j++;\r
301     XtSetArg(args[j], XtNtop,       XtChainTop); j++;\r
302     XtSetArg(args[j], XtNbottom,    XtChainTop); j++;\r
303     XtSetArg(args[j], XtNheight,    (XtArgVal) 16); j++;\r
304     XtSetArg(args[j], XtNwidth,     (XtArgVal) 20); j++;\r
305     outputField[which][nStateIcon] = ModeWidget =\r
306       XtCreateManagedWidget("Mode", labelWidgetClass,\r
307                      form, args, j);\r
308 \r
309     j = 0;\r
310     XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;\r
311     XtSetArg(args[j], XtNjustify,   (XtArgVal) XtJustifyLeft); j++;\r
312     XtSetArg(args[j], XtNlabel,     (XtArgVal) ""); j++;\r
313     XtSetArg(args[j], XtNfromHoriz, (XtArgVal) ModeWidget); j++;\r
314     XtSetArg(args[j], XtNtop,       XtChainTop); j++;\r
315     XtSetArg(args[j], XtNbottom,    XtChainTop); j++;\r
316     XtSetArg(args[j], XtNright,     XtChainRight); j++;\r
317     XtSetArg(args[j], XtNheight,    (XtArgVal) 16); j++;\r
318     XtSetArg(args[j], XtNwidth,     (XtArgVal) bw_width/2 - 102); j++;\r
319     outputField[which][nStateData] = MoveWidget =\r
320       XtCreateManagedWidget("Move", labelWidgetClass,\r
321                      form, args, j);\r
322 \r
323     j = 0;\r
324     XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;\r
325     XtSetArg(args[j], XtNjustify,   (XtArgVal) XtJustifyRight); j++;\r
326     XtSetArg(args[j], XtNlabel,     (XtArgVal) _("NPS")); j++;\r
327     XtSetArg(args[j], XtNfromHoriz, (XtArgVal) MoveWidget); j++;\r
328     XtSetArg(args[j], XtNtop,       XtChainTop); j++;\r
329     XtSetArg(args[j], XtNbottom,    XtChainTop); j++;\r
330     XtSetArg(args[j], XtNleft,      XtChainRight); j++;\r
331     XtSetArg(args[j], XtNright,     XtChainRight); j++;\r
332     XtSetArg(args[j], XtNheight,    (XtArgVal) 16); j++;\r
333     XtSetArg(args[j], XtNwidth,     (XtArgVal) 100); j++;\r
334     outputField[which][nLabelNPS] = NodesWidget =\r
335       XtCreateManagedWidget("Nodes", labelWidgetClass,\r
336                      form, args, j);\r
337 \r
338     // create "text" within "form"\r
339     j = 0;\r
340     if (mutable) {\r
341         XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;\r
342         XtSetArg(args[j], XtNuseStringInPlace, False);  j++;\r
343     }\r
344     XtSetArg(args[j], XtNstring, "");  j++;\r
345     XtSetArg(args[j], XtNdisplayCaret, False);  j++;\r
346     XtSetArg(args[j], XtNtop, XtChainTop);  j++;\r
347     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;\r
348     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;\r
349     XtSetArg(args[j], XtNright, XtChainRight);  j++;\r
350     XtSetArg(args[j], XtNresizable, True);  j++;\r
351     XtSetArg(args[j], XtNwidth, bw_width);  j++; /*force wider than buttons*/\r
352 #if 0\r
353     XtSetArg(args[j], XtNscrollVertical, XawtextScrollWhenNeeded);  j++;\r
354 #else\r
355     /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */\r
356     XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++;\r
357     XtSetArg(args[j], XtNscrollHorizontal, XawtextScrollWhenNeeded);  j++;\r
358 #endif\r
359 //    XtSetArg(args[j], XtNautoFill, True);  j++;\r
360 //    XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;\r
361     outputField[which][nMemo] = edit =\r
362       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);\r
363 \r
364     j = 0;\r
365     XtSetArg(args[j], XtNfromVert, ColorWidget); j++;\r
366 //    XtSetArg(args[j], XtNresizable, (XtArgVal) True); j++;\r
367     XtSetValues(edit, args, j);\r
368 }\r
369 \r
370 Widget EngineOutputCreate(name, text)\r
371      char *name, *text;\r
372 {\r
373     Arg args[16];\r
374     Widget shell, layout, form, form2, edit;\r
375     Dimension bw_width, bw_height;\r
376     int j;\r
377 \r
378     // get board width\r
379     j = 0;\r
380     XtSetArg(args[j], XtNwidth,  &bw_width);  j++;\r
381     XtSetArg(args[j], XtNheight, &bw_height);  j++;\r
382     XtGetValues(boardWidget, args, j);\r
383 \r
384     // define form within layout within shell.\r
385     j = 0;\r
386     XtSetArg(args[j], XtNresizable, True);  j++;\r
387     shell =\r
388       XtCreatePopupShell(name, transientShellWidgetClass,\r
389                          shellWidget, args, j);\r
390     layout =\r
391       XtCreateManagedWidget(layoutName, formWidgetClass, shell,\r
392                             layoutArgs, XtNumber(layoutArgs));\r
393     // divide window vertically into two equal parts, by creating two forms\r
394     form =\r
395       XtCreateManagedWidget("form", formWidgetClass, layout,\r
396                             formArgs, XtNumber(formArgs));\r
397     form2 =\r
398       XtCreateManagedWidget("form2", formWidgetClass, layout,\r
399                             formArgs, XtNumber(formArgs));\r
400     j = 0;\r
401     XtSetArg(args[j], XtNfromVert,  (XtArgVal) form); j++;\r
402     XtSetValues(form2, args, j);\r
403     // make sure width is known in advance, for better placement of child widgets\r
404     j = 0;\r
405     XtSetArg(args[j], XtNwidth,     (XtArgVal) bw_width-16); j++;\r
406     XtSetArg(args[j], XtNheight,    (XtArgVal) bw_height/2); j++;\r
407     XtSetValues(shell, args, j);\r
408 \r
409     // fill up both forms with control elements\r
410     PositionControlSet(0, form,  bw_width);\r
411     PositionControlSet(1, form2, bw_width);\r
412 \r
413     XtRealizeWidget(shell);\r
414 \r
415     if (engineOutputX == -1) {\r
416         int xx, yy;\r
417         Window junk;\r
418         Dimension pw_height;\r
419         Dimension ew_height;\r
420 #if 0\r
421         j = 0;\r
422         XtSetArg(args[j], XtNheight, &ew_height);  j++;\r
423         XtGetValues(edit, args, j);\r
424 \r
425         j = 0;\r
426         XtSetArg(args[j], XtNheight, &pw_height);  j++;\r
427         XtGetValues(shell, args, j);\r
428         engineOutputH = pw_height + (lines - 1) * ew_height;\r
429         engineOutputW = bw_width - 16;\r
430 #else\r
431         engineOutputH = bw_height/2;\r
432         engineOutputW = bw_width-16;\r
433 #endif\r
434 \r
435         XSync(xDisplay, False);\r
436 #ifdef NOTDEF\r
437         /* This code seems to tickle an X bug if it is executed too soon\r
438            after xboard starts up.  The coordinates get transformed as if\r
439            the main window was positioned at (0, 0).\r
440            */\r
441         XtTranslateCoords(shellWidget,\r
442                           (bw_width - engineOutputW) / 2, 0 - engineOutputH / 2,\r
443                           &engineOutputX, &engineOutputY);\r
444 #else  /*!NOTDEF*/\r
445         XTranslateCoordinates(xDisplay, XtWindow(shellWidget),\r
446                               RootWindowOfScreen(XtScreen(shellWidget)),\r
447                               (bw_width - engineOutputW) / 2, 0 - engineOutputH / 2,\r
448                               &xx, &yy, &junk);\r
449         engineOutputX = xx;\r
450         engineOutputY = yy;\r
451 #endif /*!NOTDEF*/\r
452         if (engineOutputY < 0) engineOutputY = 0; /*avoid positioning top offscreen*/\r
453     }\r
454     j = 0;\r
455     XtSetArg(args[j], XtNheight, engineOutputH);  j++;\r
456     XtSetArg(args[j], XtNwidth, engineOutputW);  j++;\r
457     XtSetArg(args[j], XtNx, engineOutputX);  j++;\r
458     XtSetArg(args[j], XtNy, engineOutputY);  j++;\r
459     XtSetValues(shell, args, j);\r
460 //    XtSetKeyboardFocus(shell, edit);\r
461 \r
462     return shell;\r
463 }\r
464 \r
465 void ResizeWindowControls(shell, mode)\r
466         Widget shell;\r
467         int mode;\r
468 {\r
469     Widget form1, form2;\r
470     Arg args[16];\r
471     int j;\r
472     Dimension ew_height, tmp;\r
473 \r
474     form1 = XtNameToWidget(shell, "*form");\r
475     form2 = XtNameToWidget(shell, "*form2");\r
476 \r
477     j = 0;\r
478     XtSetArg(args[j], XtNheight, (XtArgVal) &ew_height); j++;\r
479     XtGetValues(form1, args, j);\r
480     j = 0;\r
481     XtSetArg(args[j], XtNheight, (XtArgVal) &tmp); j++;\r
482     XtGetValues(form2, args, j);\r
483     ew_height += tmp; // total height\r
484 \r
485     if(mode==0) {\r
486         j = 0;\r
487         XtSetArg(args[j], XtNheight, (XtArgVal) 5); j++;\r
488         XtSetValues(form2, args, j);\r
489         j = 0;\r
490         XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height-5)); j++;\r
491         XtSetValues(form1, args, j);\r
492     } else {\r
493         j = 0;\r
494         XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height/2)); j++;\r
495         XtSetValues(form1, args, j);\r
496         j = 0;\r
497         XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height/2)); j++;\r
498         XtSetValues(form2, args, j);\r
499     }\r
500 }\r
501 \r
502 void EngineOutputPopUp(title, text)\r
503      char *title, *text;\r
504 {\r
505     Arg args[16];\r
506     int j;\r
507     Widget edit;\r
508 \r
509     if (engineOutputShell == NULL) {\r
510         engineOutputShell =\r
511           EngineOutputCreate(title, text);\r
512         XtRealizeWidget(engineOutputShell);\r
513         CatchDeleteWindow(engineOutputShell, "EngineOutputPopDown");\r
514         if( needInit ) {\r
515             InitializeEngineOutput();\r
516             needInit = FALSE;\r
517         }\r
518         SetEngineColorIcon( 0 );\r
519         SetEngineColorIcon( 1 );\r
520         SetEngineState( 0, STATE_IDLE, "" );\r
521         SetEngineState( 1, STATE_IDLE, "" );\r
522     } else {\r
523         edit = XtNameToWidget(engineOutputShell, "*form.text");\r
524         j = 0;\r
525         XtSetArg(args[j], XtNstring, text); j++;\r
526         XtSetValues(edit, args, j);\r
527         j = 0;\r
528         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;\r
529         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;\r
530         XtSetValues(engineOutputShell, args, j);\r
531     }\r
532 \r
533     XtPopup(engineOutputShell, XtGrabNone);\r
534     XSync(xDisplay, False);\r
535 \r
536     j=0;\r
537     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;\r
538     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Show Engine Output"),\r
539                 args, j);\r
540 \r
541     engineOutputDialogUp = True;\r
542     ShowThinkingEvent(); // [HGM] thinking: might need to prompt engine for thinking output\r
543 }\r
544 \r
545 void EngineOutputPopDown()\r
546 {\r
547     Arg args[16];\r
548     int j;\r
549 \r
550     if (!engineOutputDialogUp) return;\r
551     DoClearMemo(1);\r
552     j = 0;\r
553     XtSetArg(args[j], XtNx, &engineOutputX); j++;\r
554     XtSetArg(args[j], XtNy, &engineOutputY); j++;\r
555     XtSetArg(args[j], XtNwidth, &engineOutputW); j++;\r
556     XtSetArg(args[j], XtNheight, &engineOutputH); j++;\r
557     XtGetValues(engineOutputShell, args, j);\r
558     XtPopdown(engineOutputShell);\r
559     XSync(xDisplay, False);\r
560     j=0;\r
561     XtSetArg(args[j], XtNleftBitmap, None); j++;\r
562     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Show Engine Output"),\r
563                 args, j);\r
564 \r
565     engineOutputDialogUp = False;\r
566     ShowThinkingEvent(); // [HGM] thinking: might need to shut off thinking output\r
567 }\r
568 \r
569 //------------------------ pure back-end routines -------------------------------\r
570 \r
571 \r
572 // back end, due to front-end wrapper for SetWindowText, and new SetIcon arguments\r
573 static SetEngineState( int which, int state, char * state_data )\r
574 {\r
575     int x_which = 1 - which;\r
576 \r
577     if( engineState[ which ] != state ) {\r
578         engineState[ which ] = state;\r
579 \r
580         switch( state ) {\r
581         case STATE_THINKING:\r
582             SetIcon( which, nStateIcon, nThinking );\r
583             if( engineState[ x_which ] == STATE_THINKING ) {\r
584                 SetEngineState( x_which, STATE_IDLE, "" );\r
585             }\r
586             break;\r
587         case STATE_PONDERING:\r
588             SetIcon( which, nStateIcon, nPondering );\r
589             break;\r
590         case STATE_ANALYZING:\r
591             SetIcon( which, nStateIcon, nAnalyzing );\r
592             break;\r
593         default:\r
594             SetIcon( which, nStateIcon, nClear );\r
595             break;\r
596         }\r
597     }\r
598 \r
599     if( state_data != 0 ) {\r
600         DoSetWindowText( which, nStateData, state_data );\r
601     }\r
602 }\r
603 \r
604 // back end, now the front-end wrapper ClearMemo is used, and ed no longer contains handles.\r
605 void EngineOutputUpdate( FrontEndProgramStats * stats )\r
606 {\r
607     EngineOutputData ed;\r
608     int clearMemo = FALSE;\r
609     int which;\r
610     int depth;\r
611 \r
612     if( stats == 0 ) {\r
613         SetEngineState( 0, STATE_IDLE, "" );\r
614         SetEngineState( 1, STATE_IDLE, "" );\r
615         return;\r
616     }\r
617 \r
618     if(gameMode == IcsObserving && !appData.icsEngineAnalyze)\r
619         return; // [HGM] kibitz: shut up engine if we are observing an ICS game\r
620 \r
621     which = stats->which;\r
622     depth = stats->depth;\r
623 \r
624     if( which < 0 || which > 1 || depth < 0 || stats->time < 0 || stats->pv == 0 ) {\r
625         return;\r
626     }\r
627 \r
628     if( engineOutputShell == NULL ) {\r
629         return;\r
630     }\r
631 \r
632     VerifyDisplayMode();\r
633 \r
634     ed.which = which;\r
635     ed.depth = depth;\r
636     ed.nodes = stats->nodes;\r
637     ed.score = stats->score;\r
638     ed.time = stats->time;\r
639     ed.pv = stats->pv;\r
640     ed.hint = stats->hint;\r
641     ed.an_move_index = stats->an_move_index;\r
642     ed.an_move_count = stats->an_move_count;\r
643 \r
644     /* Get target control. [HGM] this is moved to front end, which get them from a table */\r
645     if( which == 0 ) {\r
646         ed.name = first.tidy;\r
647     }\r
648     else {\r
649         ed.name = second.tidy;\r
650     }\r
651 \r
652     /* Clear memo if needed */\r
653     if( lastDepth[which] > depth || (lastDepth[which] == depth && depth <= 1) ) {\r
654         clearMemo = TRUE;\r
655     }\r
656 \r
657     if( lastForwardMostMove[which] != forwardMostMove ) {\r
658         clearMemo = TRUE;\r
659     }\r
660 \r
661     if( clearMemo ) DoClearMemo(which);\r
662 \r
663     /* Update */\r
664     lastDepth[which] = depth;\r
665     lastForwardMostMove[which] = forwardMostMove;\r
666 \r
667     if( ed.pv != 0 && ed.pv[0] == ' ' ) {\r
668         if( strncmp( ed.pv, " no PV", 6 ) == 0 ) { /* Hack on hack! :-O */\r
669             ed.pv = "";\r
670         }\r
671     }\r
672 \r
673     UpdateControls( &ed );\r
674 }\r
675 \r
676 #define ENGINE_COLOR_WHITE      'w'\r
677 #define ENGINE_COLOR_BLACK      'b'\r
678 #define ENGINE_COLOR_UNKNOWN    ' '\r
679 \r
680 // pure back end\r
681 char GetEngineColor( int which )\r
682 {\r
683     char result = ENGINE_COLOR_UNKNOWN;\r
684 \r
685     if( which == 0 || which == 1 ) {\r
686         ChessProgramState * cps;\r
687 \r
688         switch (gameMode) {\r
689         case MachinePlaysBlack:\r
690         case IcsPlayingBlack:\r
691             result = ENGINE_COLOR_BLACK;\r
692             break;\r
693         case MachinePlaysWhite:\r
694         case IcsPlayingWhite:\r
695             result = ENGINE_COLOR_WHITE;\r
696             break;\r
697         case AnalyzeMode:\r
698         case AnalyzeFile:\r
699             result = WhiteOnMove(forwardMostMove) ? ENGINE_COLOR_WHITE : ENGINE_COLOR_BLACK;\r
700             break;\r
701         case TwoMachinesPlay:\r
702             cps = (which == 0) ? &first : &second;\r
703             result = cps->twoMachinesColor[0];\r
704             result = result == 'w' ? ENGINE_COLOR_WHITE : ENGINE_COLOR_BLACK;\r
705             break;\r
706         }\r
707     }\r
708 \r
709     return result;\r
710 }\r
711 \r
712 // pure back end\r
713 char GetActiveEngineColor()\r
714 {\r
715     char result = ENGINE_COLOR_UNKNOWN;\r
716 \r
717     if( gameMode == TwoMachinesPlay ) {\r
718         result = WhiteOnMove(forwardMostMove) ? ENGINE_COLOR_WHITE : ENGINE_COLOR_BLACK;\r
719     }\r
720 \r
721     return result;\r
722 }\r
723 \r
724 // pure back end\r
725 static int IsEnginePondering( int which )\r
726 {\r
727     int result = FALSE;\r
728 \r
729     switch (gameMode) {\r
730     case MachinePlaysBlack:\r
731     case IcsPlayingBlack:\r
732         if( WhiteOnMove(forwardMostMove) ) result = TRUE;\r
733         break;\r
734     case MachinePlaysWhite:\r
735     case IcsPlayingWhite:\r
736         if( ! WhiteOnMove(forwardMostMove) ) result = TRUE;\r
737         break;\r
738     case TwoMachinesPlay:\r
739         if( GetActiveEngineColor() != ENGINE_COLOR_UNKNOWN ) {\r
740             if( GetEngineColor( which ) != GetActiveEngineColor() ) result = TRUE;\r
741         }\r
742         break;\r
743     }\r
744 \r
745     return result;\r
746 }\r
747 \r
748 // back end\r
749 static void SetDisplayMode( int mode )\r
750 {\r
751     if( windowMode != mode ) {\r
752         windowMode = mode;\r
753 \r
754         ResizeWindowControls( engineOutputShell, mode );\r
755     }\r
756 }\r
757 \r
758 // pure back end\r
759 int VerifyDisplayMode()\r
760 {\r
761     int mode;\r
762 \r
763     /* Get proper mode for current game */\r
764     switch( gameMode ) {\r
765     case IcsObserving:    // [HGM] ICS analyze\r
766         if(!appData.icsEngineAnalyze) return;\r
767     case AnalyzeMode:\r
768     case AnalyzeFile:\r
769     case MachinePlaysWhite:\r
770     case MachinePlaysBlack:\r
771         mode = 0;\r
772         break;\r
773     case IcsPlayingWhite:\r
774     case IcsPlayingBlack:\r
775         mode = appData.zippyPlay && opponentKibitzes; // [HGM] kibitz\r
776         break;\r
777     case TwoMachinesPlay:\r
778         mode = 1;\r
779         break;\r
780     default:\r
781         /* Do not change */\r
782         return;\r
783     }\r
784 \r
785     SetDisplayMode( mode );\r
786 }\r
787 \r
788 // back end. Determine what icon to se in the color-icon field, and print it\r
789 static void SetEngineColorIcon( int which )\r
790 {\r
791     char color = GetEngineColor(which);\r
792     int nicon = 0;\r
793 \r
794     if( color == ENGINE_COLOR_BLACK )\r
795         nicon = nColorBlack;\r
796     else if( color == ENGINE_COLOR_WHITE )\r
797         nicon = nColorWhite;\r
798     else\r
799         nicon = nColorUnknown;\r
800 \r
801     SetIcon( which, nColorIcon, nicon );\r
802 }\r
803 \r
804 #define MAX_NAME_LENGTH 32\r
805 \r
806 // pure back end, now SetWindowText is called via wrapper DoSetWindowText\r
807 static void UpdateControls( EngineOutputData * ed )\r
808 {\r
809     int isPondering = FALSE;\r
810 \r
811     char s_label[MAX_NAME_LENGTH + 32];\r
812     \r
813     char * name = ed->name;\r
814 \r
815     /* Label */\r
816     if( name == 0 || *name == '\0' ) {\r
817         name = "?";\r
818     }\r
819 \r
820     strncpy( s_label, name, MAX_NAME_LENGTH );\r
821     s_label[ MAX_NAME_LENGTH-1 ] = '\0';\r
822 \r
823 #ifdef SHOW_PONDERING\r
824     if( IsEnginePondering( ed->which ) ) {\r
825         char buf[8];\r
826 \r
827         buf[0] = '\0';\r
828 \r
829         if( ed->hint != 0 && *ed->hint != '\0' ) {\r
830             strncpy( buf, ed->hint, sizeof(buf) );\r
831             buf[sizeof(buf)-1] = '\0';\r
832         }\r
833         else if( ed->pv != 0 && *ed->pv != '\0' ) {\r
834             char * sep = strchr( ed->pv, ' ' );\r
835             int buflen = sizeof(buf);\r
836 \r
837             if( sep != NULL ) {\r
838                 buflen = sep - ed->pv + 1;\r
839                 if( buflen > sizeof(buf) ) buflen = sizeof(buf);\r
840             }\r
841 \r
842             strncpy( buf, ed->pv, buflen );\r
843             buf[ buflen-1 ] = '\0';\r
844         }\r
845 \r
846         SetEngineState( ed->which, STATE_PONDERING, buf );\r
847     }\r
848     else if( gameMode == TwoMachinesPlay ) {\r
849         SetEngineState( ed->which, STATE_THINKING, "" );\r
850     }\r
851     else if( gameMode == AnalyzeMode || gameMode == AnalyzeFile \r
852           || gameMode == IcsObserving && appData.icsEngineAnalyze) { // [HGM] ICS-analyze\r
853         char buf[64];\r
854         int time_secs = ed->time / 100;\r
855         int time_mins = time_secs / 60;\r
856 \r
857         buf[0] = '\0';\r
858 \r
859         if( ed->an_move_index != 0 && ed->an_move_count != 0 && *ed->hint != '\0' ) {\r
860             char mov[16];\r
861 \r
862             strncpy( mov, ed->hint, sizeof(mov) );\r
863             mov[ sizeof(mov)-1 ] = '\0';\r
864 \r
865             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
866         }\r
867 \r
868         SetEngineState( ed->which, STATE_ANALYZING, buf );\r
869     }\r
870     else {\r
871         SetEngineState( ed->which, STATE_IDLE, "" );\r
872     }\r
873 #endif\r
874 \r
875     DoSetWindowText( ed->which, nLabel, s_label );\r
876 \r
877     s_label[0] = '\0';\r
878 \r
879     if( ed->time > 0 && ed->nodes > 0 ) {\r
880         unsigned long nps_100 = ed->nodes / ed->time;\r
881 \r
882         if( nps_100 < 100000 ) {\r
883             sprintf( s_label, _("NPS: %lu"), nps_100 * 100 );\r
884         }\r
885         else {\r
886             sprintf( s_label, _("NPS: %.1fk"), nps_100 / 10.0 );\r
887         }\r
888     }\r
889 \r
890     DoSetWindowText( ed->which, nLabelNPS, s_label );\r
891 \r
892     /* Memo */\r
893     if( ed->pv != 0 && *ed->pv != '\0' ) {\r
894         char s_nodes[24];\r
895         char s_score[16];\r
896         char s_time[24];\r
897         char buf[256];\r
898         int buflen;\r
899         int time_secs = ed->time / 100;\r
900         int time_cent = ed->time % 100;\r
901 \r
902         /* Nodes */\r
903         if( ed->nodes < 1000000 ) {\r
904             sprintf( s_nodes, u64Display, ed->nodes );\r
905         }\r
906         else {\r
907             sprintf( s_nodes, "%.1fM", u64ToDouble(ed->nodes) / 1000000.0 );\r
908         }\r
909 \r
910         /* Score */\r
911         if( ed->score > 0 ) {\r
912             sprintf( s_score, "+%.2f", ed->score / 100.0 );\r
913         } else\r
914             sprintf( s_score, "%.2f", ed->score / 100.0 );\r
915 \r
916         /* Time */\r
917         sprintf( s_time, "%d:%02d.%02d", time_secs / 60, time_secs % 60, time_cent );\r
918 \r
919         /* Put all together... */\r
920         sprintf( buf, "%3d  %s  %s\t%s\t", ed->depth, s_score, s_nodes, s_time );\r
921 \r
922         /* Add PV */\r
923         buflen = strlen(buf);\r
924 \r
925         strncpy( buf + buflen, ed->pv, sizeof(buf) - buflen );\r
926 \r
927         buf[ sizeof(buf) - 3 ] = '\0';\r
928 \r
929         strcat( buf + buflen, "\n" );\r
930 \r
931         /* Update memo */\r
932         InsertIntoMemo( ed->which, buf );\r
933     }\r
934 \r
935     /* Colors */\r
936     SetEngineColorIcon( ed->which );\r
937 }\r
938 \r
939 // back end\r
940 int EngineOutputIsUp()\r
941 {\r
942     return engineOutputDialogUp;\r
943 }\r
944 \r
945 void\r
946 EngineOutputProc(w, event, prms, nprms)\r
947      Widget w;\r
948      XEvent *event;\r
949      String *prms;\r
950      Cardinal *nprms;\r
951 {\r
952   if (engineOutputDialogUp) {\r
953     EngineOutputPopDown();\r
954   } else {\r
955     EngineOutputPopUp(_("engine output"),_("This feature is experimental"));\r
956   }\r
957 //  ToNrEvent(currentMove);\r
958 }\r
959 \r
960 // [HGM] kibitz: write kibitz line; split window for it if necessary\r
961 void OutputKibitz(int window, char *text)\r
962 {\r
963         if(!EngineOutputIsUp()) return;\r
964         if(!opponentKibitzes) { // on first kibitz of game, clear memos\r
965             DoClearMemo(1);\r
966             if(gameMode == IcsObserving) DoClearMemo(0);\r
967         }\r
968         opponentKibitzes = TRUE; // this causes split window DisplayMode in ICS modes.\r
969         VerifyDisplayMode();\r
970         if(gameMode == IcsObserving) {\r
971             DoSetWindowText(0, nLabel, gameInfo.white);\r
972             SetIcon( 0, nColorIcon,  nColorWhite);\r
973             SetIcon( 0, nStateIcon,  nClear);\r
974         }\r
975         DoSetWindowText(1, nLabel, gameMode == IcsPlayingBlack ? gameInfo.white : gameInfo.black); // opponent name\r
976         SetIcon( 1, nColorIcon,  gameMode == IcsPlayingBlack ? nColorWhite : nColorBlack);\r
977         SetIcon( 1, nStateIcon,  nClear);\r
978         InsertIntoMemo(window-1, text);\r
979 }\r