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