3c0921e133d40284c109f2bf27423090debd96c1
[xboard.git] / xengineoutput.c
1 /*
2  * Engine output (PV)
3  *
4  * Author: Alessandro Scotti (Dec 2005)
5  *
6  * Copyright 2005 Alessandro Scotti
7  *
8  * Enhancements Copyright 2009, 2010, 2011 Free Software Foundation, Inc.
9  *
10  * ------------------------------------------------------------------------
11  *
12  * GNU XBoard is free software: you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation, either version 3 of the License, or (at
15  * your option) any later version.
16  *
17  * GNU XBoard is distributed in the hope that it will be useful, but
18  * WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20  * General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program. If not, see http://www.gnu.org/licenses/.
24  *
25  * ------------------------------------------------------------------------
26  ** See the file ChangeLog for a revision history.  */
27
28 #include "config.h"
29
30 #include <stdio.h>
31 #include <ctype.h>
32 #include <errno.h>
33 #include <sys/types.h>
34
35 #if STDC_HEADERS
36 # include <stdlib.h>
37 # include <string.h>
38 #else /* not STDC_HEADERS */
39 extern char *getenv();
40 # if HAVE_STRING_H
41 #  include <string.h>
42 # else /* not HAVE_STRING_H */
43 #  include <strings.h>
44 # endif /* not HAVE_STRING_H */
45 #endif /* not STDC_HEADERS */
46
47 #if HAVE_UNISTD_H
48 # include <unistd.h>
49 #endif
50
51 #include <X11/Intrinsic.h>
52 #include <X11/StringDefs.h>
53 #include <X11/Shell.h>
54 #include <X11/Xaw/Dialog.h>
55 #include <X11/Xaw/Form.h>
56 #include <X11/Xaw/List.h>
57 #include <X11/Xaw/Label.h>
58 #include <X11/Xaw/SimpleMenu.h>
59 #include <X11/Xaw/SmeBSB.h>
60 #include <X11/Xaw/SmeLine.h>
61 #include <X11/Xaw/Box.h>
62 #include <X11/Xaw/Paned.h>
63 #include <X11/Xaw/MenuButton.h>
64 #include <X11/cursorfont.h>
65 #include <X11/Xaw/Text.h>
66 #include <X11/Xaw/AsciiText.h>
67 #include <X11/Xaw/Viewport.h>
68 #include <X11/Xatom.h>
69 #include <X11/Xmu/Atoms.h>
70
71 #include "common.h"
72 #include "frontend.h"
73 #include "backend.h"
74 #include "xboard.h"
75 #include "engineoutput.h"
76 #include "gettext.h"
77
78 #ifdef ENABLE_NLS
79 # define  _(s) gettext (s)
80 # define N_(s) gettext_noop (s)
81 #else
82 # define  _(s) (s)
83 # define N_(s)  s
84 #endif
85
86 #include <X11/xpm.h>
87
88 // [HGM] pixmaps of some ICONS used in the engine-outut window
89 #include "pixmaps/WHITE_14.xpm"
90 #include "pixmaps/BLACK_14.xpm"
91 #include "pixmaps/CLEAR_14.xpm"
92 #include "pixmaps/UNKNOWN_14.xpm"
93 #include "pixmaps/THINKING_14.xpm"
94 #include "pixmaps/PONDER_14.xpm"
95 #include "pixmaps/ANALYZING_14.xpm"
96
97 #ifdef SNAP
98 #include "wsnap.h"
99 #endif
100
101 #define _LL_ 100
102
103 Pixmap icons[8]; // [HGM] this front-end array translates back-end icon indicator to handle
104 Widget outputField[2][7]; // [HGM] front-end array to translate output field to window handle
105
106 void EngineOutputPopDown();
107 void engineOutputPopUp();
108 int  EngineOutputIsUp();
109 void SetEngineColorIcon( int which );
110
111 //extern WindowPlacement wpEngineOutput;
112
113 Position engineOutputX = -1, engineOutputY = -1;
114 Dimension engineOutputW, engineOutputH;
115 Widget engineOutputShell = NULL;
116 static int engineOutputDialogUp;
117
118 /* Module variables */
119 int  windowMode = 1;
120 static int currentPV, highTextStart[2], highTextEnd[2];
121
122 typedef struct {
123     char * name;
124     int which;
125     int depth;
126     u64 nodes;
127     int score;
128     int time;
129     char * pv;
130     char * hint;
131     int an_move_index;
132     int an_move_count;
133 } EngineOutputData;
134
135 //static void UpdateControls( EngineOutputData * ed );
136
137 void ReadIcon(char *pixData[], int iconNr)
138 {
139     int r;
140
141         if ((r=XpmCreatePixmapFromData(xDisplay, XtWindow(outputField[0][nColorIcon]),
142                                        pixData,
143                                        &(icons[iconNr]),
144                                        NULL, NULL /*&attr*/)) != 0) {
145           fprintf(stderr, _("Error %d loading icon image\n"), r);
146           exit(1);
147         }
148 }
149
150 static void InitializeEngineOutput()
151 {
152         ReadIcon(WHITE_14,   nColorWhite);
153         ReadIcon(BLACK_14,   nColorBlack);
154         ReadIcon(UNKNOWN_14, nColorUnknown);
155
156         ReadIcon(CLEAR_14,   nClear);
157         ReadIcon(PONDER_14,  nPondering);
158         ReadIcon(THINK_14,   nThinking);
159         ReadIcon(ANALYZE_14, nAnalyzing);
160 }
161
162 void DoSetWindowText(int which, int field, char *s_label)
163 {
164         Arg args[16];
165
166         XtSetArg(args[0], XtNlabel, (XtArgVal) s_label);
167         XtSetValues(outputField[which][field], args, 1);
168 }
169
170 void SetEngineOutputTitle(char *title)
171 {
172         Arg args[16];
173         if(engineOutputShell==NULL) return;
174         XtSetArg(args[0], XtNtitle, (XtArgVal) title);
175         XtSetValues(engineOutputShell, args, 1);
176 }
177
178 void InsertIntoMemo( int which, char * text, int where )
179 {
180         XawTextBlock t;
181         Widget edit;
182
183         /* the backend adds \r\n, which is needed for winboard,
184          * for xboard we delete them again over here */
185         if(t.ptr = strchr(text, '\r')) *t.ptr = ' ';
186
187         t.ptr = text; t.firstPos = 0; t.length = strlen(text); t.format = XawFmt8Bit;
188         edit = XtNameToWidget(engineOutputShell, which ? "*form2.text" : "*form.text");
189         XawTextReplace(edit, where, where, &t);
190         if(where < highTextStart[which]) { // [HGM] multiPVdisplay: move highlighting
191             int len = strlen(text);
192             highTextStart[which] += len; highTextEnd[which] += len;
193             XawTextSetSelection( outputField[which][nMemo], highTextStart[which], highTextEnd[which] );
194         }
195 }
196
197 void SetIcon( int which, int field, int nIcon )
198 {
199     Arg arg;
200
201     if( nIcon != 0 ) {
202         XtSetArg(arg, XtNleftBitmap, (XtArgVal) icons[nIcon]);
203         XtSetValues(outputField[which][field], &arg, 1);
204     }
205 }
206
207 void DoClearMemo(int which)
208 {
209     Widget edit = XtNameToWidget(engineOutputShell, which ? "*form2.text" : "*form.text");
210     Arg arg;
211 //    XtCallActionProc(edit, "select-all", NULL, NULL, 0);
212 //    XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
213     XtSetArg(arg, XtNstring, ""); // clear without disturbing selection!
214     XtSetValues(edit, &arg, 1);
215 }
216
217 // cloned from CopyPositionProc. Abuse selected_fen_position to hold selection
218
219 Boolean SendPositionSelection(Widget w, Atom *selection, Atom *target,
220                  Atom *type_return, XtPointer *value_return,
221                  unsigned long *length_return, int *format_return); // from xboard.c
222 void SetFocus(Widget w, XtPointer data, XEvent *event, Boolean *b); // from xoptions.c
223
224 char memoTranslations[] =
225 ":Ctrl<Key>c: CopyMemoProc() \n \
226 <Btn3Motion>: HandlePV() \n \
227 Shift<Btn3Down>: select-start() SelectPV(1) \n \
228 Any<Btn3Down>: select-start() SelectPV(0) \n \
229 <Btn3Up>: extend-end() StopPV() \n";
230
231 void
232 SelectPV (Widget w, XEvent * event, String * params, Cardinal * nParams)
233 {       // [HGM] pv: translate click to PV line, and load it for display
234         String val;
235         int start, end;
236         XawTextPosition index, dummy;
237         int x, y;
238         Arg arg;
239
240         x = event->xmotion.x; y = event->xmotion.y;
241         currentPV = (w == outputField[1][nMemo]);
242         XawTextGetSelectionPos(w, &index, &dummy);
243         XtSetArg(arg, XtNstring, &val);
244         XtGetValues(w, &arg, 1);
245         shiftKey = strcmp(params[0], "0");
246         if(LoadMultiPV(x, y, val, index, &start, &end)) {
247             XawTextSetSelection( outputField[currentPV][nMemo], start, end );
248             highTextStart[currentPV] = start; highTextEnd[currentPV] = end;
249         }
250 }
251
252 void
253 StopPV (Widget w, XEvent * event, String * params, Cardinal * nParams)
254 {       // [HGM] pv: on right-button release, stop displaying PV
255         XawTextUnsetSelection( w );
256         highTextStart[currentPV] = highTextEnd[currentPV] = 0;
257         UnLoadPV();
258 }
259
260 static void
261 MemoCB(Widget w, XtPointer client_data, Atom *selection,
262            Atom *type, XtPointer value, unsigned long *len, int *format)
263 {
264   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
265   selected_fen_position = value;
266   selected_fen_position[*len]='\0'; /* normally this string is terminated, but be safe */
267     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
268                    CurrentTime,
269                    SendPositionSelection,
270                    NULL/* lose_ownership_proc */ ,
271                    NULL/* transfer_done_proc */);
272 }
273
274 void CopyMemoProc(w, event, prms, nprms)
275   Widget w;
276   XEvent *event;
277   String *prms;
278   Cardinal *nprms;
279 {
280     if(appData.pasteSelection) return;
281     if (selected_fen_position) free(selected_fen_position);
282     XtGetSelectionValue(menuBarWidget,
283       XA_PRIMARY, XA_STRING,
284       /* (XtSelectionCallbackProc) */ MemoCB,
285       NULL, /* client_data passed to PastePositionCB */
286
287       /* better to use the time field from the event that triggered the
288        * call to this function, but that isn't trivial to get
289        */
290       CurrentTime
291     );
292 }
293
294 // The following routines are mutated clones of the commentPopUp routines
295
296 void PositionControlSet(which, shell, form, bw_width)
297      int which;
298      Widget shell, form;
299      Dimension bw_width;
300 {
301     Arg args[16];
302     Widget edit, NameWidget, ColorWidget, ModeWidget, MoveWidget, NodesWidget;
303     int j, mutable=1;
304     j = 0;
305     XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
306     XtSetArg(args[j], XtNlabel,     (XtArgVal) ""); j++;
307     XtSetArg(args[j], XtNtop,       XtChainTop); j++;
308     XtSetArg(args[j], XtNbottom,    XtChainTop); j++;
309     XtSetArg(args[j], XtNleft,      XtChainLeft); j++;
310     XtSetArg(args[j], XtNright,     XtChainLeft); j++;
311     XtSetArg(args[j], XtNheight,    (XtArgVal) 16); j++;
312     XtSetArg(args[j], XtNwidth,     (XtArgVal) 17); j++;
313     outputField[which][nColorIcon] = ColorWidget =
314       XtCreateManagedWidget("Color", labelWidgetClass,
315                      form, args, j);
316
317     j = 0;
318     XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
319     XtSetArg(args[j], XtNjustify,   (XtArgVal) XtJustifyLeft); j++;
320     XtSetArg(args[j], XtNfromHoriz, (XtArgVal) ColorWidget); j++;
321     XtSetArg(args[j], XtNtop,       XtChainTop); j++;
322     XtSetArg(args[j], XtNbottom,    XtChainTop); j++;
323     XtSetArg(args[j], XtNleft,      XtChainLeft); j++;
324     XtSetArg(args[j], XtNheight,    (XtArgVal) 16); j++;
325     XtSetArg(args[j], XtNwidth,     (XtArgVal) bw_width/2 - 57); j++;
326     outputField[which][nLabel] = NameWidget =
327       XtCreateManagedWidget("Engine", labelWidgetClass,
328                      form, args, j);
329
330     j = 0;
331     XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
332     XtSetArg(args[j], XtNlabel,     (XtArgVal) ""); j++;
333     XtSetArg(args[j], XtNfromHoriz, (XtArgVal) NameWidget); j++;
334     XtSetArg(args[j], XtNtop,       XtChainTop); j++;
335     XtSetArg(args[j], XtNbottom,    XtChainTop); j++;
336     XtSetArg(args[j], XtNheight,    (XtArgVal) 16); j++;
337     XtSetArg(args[j], XtNwidth,     (XtArgVal) 20); j++;
338     outputField[which][nStateIcon] = ModeWidget =
339       XtCreateManagedWidget("Mode", labelWidgetClass,
340                      form, args, j);
341
342     j = 0;
343     XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
344     XtSetArg(args[j], XtNjustify,   (XtArgVal) XtJustifyLeft); j++;
345     XtSetArg(args[j], XtNlabel,     (XtArgVal) ""); j++;
346     XtSetArg(args[j], XtNfromHoriz, (XtArgVal) ModeWidget); j++;
347     XtSetArg(args[j], XtNtop,       XtChainTop); j++;
348     XtSetArg(args[j], XtNbottom,    XtChainTop); j++;
349     XtSetArg(args[j], XtNright,     XtChainRight); j++;
350     XtSetArg(args[j], XtNheight,    (XtArgVal) 16); j++;
351     XtSetArg(args[j], XtNwidth,     (XtArgVal) bw_width/2 - 102); j++;
352     outputField[which][nStateData] = MoveWidget =
353       XtCreateManagedWidget("Move", labelWidgetClass,
354                      form, args, j);
355
356     j = 0;
357     XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
358     XtSetArg(args[j], XtNjustify,   (XtArgVal) XtJustifyRight); j++;
359     XtSetArg(args[j], XtNlabel,     (XtArgVal) _("NPS")); j++;
360     XtSetArg(args[j], XtNfromHoriz, (XtArgVal) MoveWidget); j++;
361     XtSetArg(args[j], XtNtop,       XtChainTop); j++;
362     XtSetArg(args[j], XtNbottom,    XtChainTop); j++;
363     XtSetArg(args[j], XtNleft,      XtChainRight); j++;
364     XtSetArg(args[j], XtNright,     XtChainRight); j++;
365     XtSetArg(args[j], XtNheight,    (XtArgVal) 16); j++;
366     XtSetArg(args[j], XtNwidth,     (XtArgVal) 100); j++;
367     outputField[which][nLabelNPS] = NodesWidget =
368       XtCreateManagedWidget("Nodes", labelWidgetClass,
369                      form, args, j);
370
371     // create "text" within "form"
372     j = 0;
373     if (mutable) {
374         XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
375         XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
376     }
377     XtSetArg(args[j], XtNstring, "");  j++;
378     XtSetArg(args[j], XtNdisplayCaret, False);  j++;
379     XtSetArg(args[j], XtNtop, XtChainTop);  j++;
380     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;
381     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;
382     XtSetArg(args[j], XtNright, XtChainRight);  j++;
383     XtSetArg(args[j], XtNresizable, True);  j++;
384     XtSetArg(args[j], XtNwidth, bw_width);  j++; /*force wider than buttons*/
385     /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
386     XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++;
387     XtSetArg(args[j], XtNscrollHorizontal, XawtextScrollWhenNeeded);  j++;
388 //    XtSetArg(args[j], XtNautoFill, True);  j++;
389 //    XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
390     outputField[which][nMemo] = edit =
391       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
392
393     XtOverrideTranslations(edit, XtParseTranslationTable(memoTranslations));
394     XtAddEventHandler(edit, ButtonPressMask, False, SetFocus, (XtPointer) shell);
395
396     j = 0;
397     XtSetArg(args[j], XtNfromVert, ColorWidget); j++;
398 //    XtSetArg(args[j], XtNresizable, (XtArgVal) True); j++;
399     XtSetValues(edit, args, j);
400 }
401
402 Widget EngineOutputCreate(name, text)
403      char *name, *text;
404 {
405     Arg args[16];
406     Widget shell, layout, form, form2;
407     Dimension bw_width, bw_height;
408     int j;
409
410     // get board width
411     j = 0;
412     XtSetArg(args[j], XtNwidth,  &bw_width);  j++;
413     XtSetArg(args[j], XtNheight, &bw_height);  j++;
414     XtGetValues(boardWidget, args, j);
415
416     // define form within layout within shell.
417     j = 0;
418     XtSetArg(args[j], XtNresizable, True);  j++;
419     shell =
420 #if TOPLEVEL
421      XtCreatePopupShell(name, topLevelShellWidgetClass,
422 #else
423       XtCreatePopupShell(name, transientShellWidgetClass,
424 #endif
425                          shellWidget, args, j);
426     layout =
427       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
428                             layoutArgs, XtNumber(layoutArgs));
429     // divide window vertically into two equal parts, by creating two forms
430     form =
431       XtCreateManagedWidget("form", formWidgetClass, layout,
432                             formArgs, XtNumber(formArgs));
433     form2 =
434       XtCreateManagedWidget("form2", formWidgetClass, layout,
435                             formArgs, XtNumber(formArgs));
436     j = 0;
437     XtSetArg(args[j], XtNfromVert,  (XtArgVal) form); j++;
438     XtSetValues(form2, args, j);
439     // make sure width is known in advance, for better placement of child widgets
440     j = 0;
441     XtSetArg(args[j], XtNwidth,     (XtArgVal) bw_width-16); j++;
442     XtSetArg(args[j], XtNheight,    (XtArgVal) bw_height/2); j++;
443     XtSetValues(shell, args, j);
444
445     // fill up both forms with control elements
446     PositionControlSet(0, shell, form,  bw_width);
447     PositionControlSet(1, shell, form2, bw_width);
448
449     XtRealizeWidget(shell);
450
451     if(wpEngineOutput.width > 0) {
452       engineOutputW = wpEngineOutput.width;
453       engineOutputH = wpEngineOutput.height;
454       engineOutputX = wpEngineOutput.x;
455       engineOutputY = wpEngineOutput.y;
456     }
457
458     if (engineOutputX == -1) {
459         int xx, yy;
460         Window junk;
461
462         engineOutputH = bw_height/2;
463         engineOutputW = bw_width-16;
464
465         XSync(xDisplay, False);
466 #ifdef NOTDEF
467         /* This code seems to tickle an X bug if it is executed too soon
468            after xboard starts up.  The coordinates get transformed as if
469            the main window was positioned at (0, 0).
470            */
471         XtTranslateCoords(shellWidget,
472                           (bw_width - engineOutputW) / 2, 0 - engineOutputH / 2,
473                           &engineOutputX, &engineOutputY);
474 #else  /*!NOTDEF*/
475         XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
476                               RootWindowOfScreen(XtScreen(shellWidget)),
477                               (bw_width - engineOutputW) / 2, 0 - engineOutputH / 2,
478                               &xx, &yy, &junk);
479         engineOutputX = xx;
480         engineOutputY = yy;
481 #endif /*!NOTDEF*/
482         if (engineOutputY < 0) engineOutputY = 0; /*avoid positioning top offscreen*/
483     }
484     j = 0;
485     XtSetArg(args[j], XtNheight, engineOutputH);  j++;
486     XtSetArg(args[j], XtNwidth, engineOutputW);  j++;
487     XtSetArg(args[j], XtNx, engineOutputX);  j++;
488     XtSetArg(args[j], XtNy, engineOutputY);  j++;
489     XtSetValues(shell, args, j);
490
491     return shell;
492 }
493
494 void ResizeWindowControls(mode)
495         int mode;
496 {
497     Widget form1, form2;
498     Arg args[16];
499     int j;
500     Dimension ew_height, tmp;
501     Widget shell = engineOutputShell;
502
503     form1 = XtNameToWidget(shell, "*form");
504     form2 = XtNameToWidget(shell, "*form2");
505
506     j = 0;
507     XtSetArg(args[j], XtNheight, (XtArgVal) &ew_height); j++;
508     XtGetValues(form1, args, j);
509     j = 0;
510     XtSetArg(args[j], XtNheight, (XtArgVal) &tmp); j++;
511     XtGetValues(form2, args, j);
512     ew_height += tmp; // total height
513
514     if(mode==0) {
515         j = 0;
516         XtSetArg(args[j], XtNheight, (XtArgVal) 5); j++;
517         XtSetValues(form2, args, j);
518         j = 0;
519         XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height-5)); j++;
520         XtSetValues(form1, args, j);
521     } else {
522         j = 0;
523         XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height/2)); j++;
524         XtSetValues(form1, args, j);
525         j = 0;
526         XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height/2)); j++;
527         XtSetValues(form2, args, j);
528     }
529 }
530
531 void
532 EngineOutputPopUp()
533 {
534     Arg args[16];
535     int j;
536     Widget edit;
537     static int  needInit = TRUE;
538     static char *title = N_("Engine output"), *text = N_("This feature is experimental");
539
540     if (engineOutputShell == NULL) {
541         engineOutputShell =
542           EngineOutputCreate(_(title), _(text));
543         XtRealizeWidget(engineOutputShell);
544         CatchDeleteWindow(engineOutputShell, "EngineOutputPopDown");
545         if( needInit ) {
546             InitializeEngineOutput();
547             needInit = FALSE;
548         }
549         SetEngineColorIcon( 0 );
550         SetEngineColorIcon( 1 );
551         SetEngineState( 0, STATE_IDLE, "" );
552         SetEngineState( 1, STATE_IDLE, "" );
553     } else {
554         edit = XtNameToWidget(engineOutputShell, "*form.text");
555         j = 0;
556         XtSetArg(args[j], XtNstring, text); j++;
557         XtSetValues(edit, args, j);
558         j = 0;
559         XtSetArg(args[j], XtNiconName, (XtArgVal) _(title));   j++;
560         XtSetArg(args[j], XtNtitle, (XtArgVal) _(title));      j++;
561         XtSetValues(engineOutputShell, args, j);
562     }
563
564     XtPopup(engineOutputShell, XtGrabNone);
565     XSync(xDisplay, False);
566
567     j=0;
568     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
569     XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Engine Output"),
570                 args, j);
571
572     engineOutputDialogUp = True;
573     ShowThinkingEvent(); // [HGM] thinking: might need to prompt engine for thinking output
574 }
575
576 void EngineOutputPopDown()
577 {
578     Arg args[16];
579     int j;
580
581     if (!engineOutputDialogUp) return;
582     DoClearMemo(1);
583     j = 0;
584     XtSetArg(args[j], XtNx, &engineOutputX); j++;
585     XtSetArg(args[j], XtNy, &engineOutputY); j++;
586     XtSetArg(args[j], XtNwidth, &engineOutputW); j++;
587     XtSetArg(args[j], XtNheight, &engineOutputH); j++;
588     XtGetValues(engineOutputShell, args, j);
589     wpEngineOutput.x = engineOutputX - 4;
590     wpEngineOutput.y = engineOutputY - 23;
591     wpEngineOutput.width = engineOutputW;
592     wpEngineOutput.height = engineOutputH;
593     XtPopdown(engineOutputShell);
594     XSync(xDisplay, False);
595     j=0;
596     XtSetArg(args[j], XtNleftBitmap, None); j++;
597     XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Engine Output"),
598                 args, j);
599
600     engineOutputDialogUp = False;
601     ShowThinkingEvent(); // [HGM] thinking: might need to shut off thinking output
602 }
603
604 int EngineOutputIsUp()
605 {
606     return engineOutputDialogUp;
607 }
608
609 int EngineOutputDialogExists()
610 {
611     return engineOutputShell != NULL;
612 }
613
614 void
615 EngineOutputProc(w, event, prms, nprms)
616      Widget w;
617      XEvent *event;
618      String *prms;
619      Cardinal *nprms;
620 {
621   if (engineOutputDialogUp) {
622     EngineOutputPopDown();
623   } else {
624     EngineOutputPopUp();
625   }
626 }