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