security fix: replaced strcpy with safeStrCpy from backend.c
[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 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 // imports from xboard.c
104 extern Widget formWidget, shellWidget, boardWidget, menuBarWidget;
105 extern Display *xDisplay;
106 extern Window xBoardWindow;
107 extern int squareSize;
108 extern Pixmap xMarkPixmap, wIconPixmap, bIconPixmap;
109 extern char *layoutName;
110
111 Pixmap icons[8]; // [HGM] this front-end array translates back-end icon indicator to handle
112 Widget outputField[2][7]; // [HGM] front-end array to translate output field to window handle
113
114 void EngineOutputPopDown();
115 void engineOutputPopUp();
116 int  EngineOutputIsUp();
117 void SetEngineColorIcon( int which );
118
119 /* Imports from backend.c */
120 extern int opponentKibitzes;
121
122 /* Imports from xboard.c */
123 extern Arg layoutArgs[2], formArgs[2], messageArgs[4];
124
125 //extern WindowPlacement wpEngineOutput;
126
127 Position engineOutputX = -1, engineOutputY = -1;
128 Dimension engineOutputW, engineOutputH;
129 Widget engineOutputShell;
130 static int engineOutputDialogUp;
131
132 /* Module variables */
133 int  windowMode = 1;
134 static int currentPV, highTextStart[2], highTextEnd[2];
135
136 typedef struct {
137     char * name;
138     int which;
139     int depth;
140     u64 nodes;
141     int score;
142     int time;
143     char * pv;
144     char * hint;
145     int an_move_index;
146     int an_move_count;
147 } EngineOutputData;
148
149 //static void UpdateControls( EngineOutputData * ed );
150
151 void ReadIcon(char *pixData[], int iconNr)
152 {
153     int r;
154
155         if ((r=XpmCreatePixmapFromData(xDisplay, XtWindow(outputField[0][nColorIcon]),
156                                        pixData,
157                                        &(icons[iconNr]),
158                                        NULL, NULL /*&attr*/)) != 0) {
159           fprintf(stderr, _("Error %d loading icon image\n"), r);
160           exit(1); 
161         }       
162 }
163
164 static void InitializeEngineOutput()
165 { int i;
166
167         ReadIcon(WHITE_14,   nColorWhite);
168         ReadIcon(BLACK_14,   nColorBlack);
169         ReadIcon(UNKNOWN_14, nColorUnknown);
170
171         ReadIcon(CLEAR_14,   nClear);
172         ReadIcon(PONDER_14,  nPondering);
173         ReadIcon(THINK_14,   nThinking);
174         ReadIcon(ANALYZE_14, nAnalyzing);
175 }
176
177 void DoSetWindowText(int which, int field, char *s_label)
178
179         Arg arg;
180
181         XtSetArg(arg, XtNlabel, (XtArgVal) s_label);
182         XtSetValues(outputField[which][field], &arg, 1);
183 }
184
185 void InsertIntoMemo( int which, char * text, int where )
186 {
187         Arg arg; XawTextBlock t; Widget edit;
188
189         /* the backend adds \r\n, which is needed for winboard, 
190          * for xboard we delete them again over here */
191         if(t.ptr = strchr(text, '\r')) *t.ptr = ' ';
192
193         t.ptr = text; t.firstPos = 0; t.length = strlen(text); t.format = XawFmt8Bit;
194         edit = XtNameToWidget(engineOutputShell, which ? "*form2.text" : "*form.text");
195         XawTextReplace(edit, where, where, &t);
196         if(where < highTextStart[which]) { // [HGM] multiPVdisplay: move highlighting
197             int len = strlen(text);
198             highTextStart[which] += len; highTextEnd[which] += len;
199             XawTextSetSelection( outputField[which][nMemo], highTextStart[which], highTextEnd[which] );
200         }
201 }
202
203 void SetIcon( int which, int field, int nIcon )
204 {
205     Arg arg;
206
207     if( nIcon != 0 ) {
208         XtSetArg(arg, XtNleftBitmap, (XtArgVal) icons[nIcon]);
209         XtSetValues(outputField[which][field], &arg, 1);
210     }
211 }
212
213 void DoClearMemo(int which)
214
215     Arg args[16];
216     int j;
217     Widget edit;
218
219         edit = XtNameToWidget(engineOutputShell, which ? "*form2.text" : "*form.text");
220         XtCallActionProc(edit, "select-all", NULL, NULL, 0);
221         XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
222 }
223
224 // cloned from CopyPositionProc. Abuse selected_fen_position to hold selection
225
226 extern char *selected_fen_position;
227
228 Boolean SendPositionSelection(Widget w, Atom *selection, Atom *target,
229                  Atom *type_return, XtPointer *value_return,
230                  unsigned long *length_return, int *format_return); // from xboard.c
231 void SetFocus(Widget w, XtPointer data, XEvent *event, Boolean *b); // from xoptions.c
232
233 char memoTranslations[] =
234 ":Ctrl<Key>c: CopyMemoProc() \n \
235 <Btn3Motion>: HandlePV() \n \
236 <Btn3Down>: select-start() SelectPV() \n \
237 <Btn3Up>: extend-end() StopPV() \n";
238
239 void
240 SelectPV (Widget w, XEvent * event, String * params, Cardinal * nParams)
241 {       // [HGM] pv: translate click to PV line, and load it for display
242         String val;
243         int start, end, memo, j;
244         XawTextPosition index, dummy;
245         int x, y;
246         Arg arg;
247
248         x = event->xmotion.x; y = event->xmotion.y;
249         currentPV = (w == outputField[1][nMemo]);
250         XawTextGetSelectionPos(w, &index, &dummy);
251         XtSetArg(arg, XtNstring, &val);
252         XtGetValues(w, &arg, 1);
253         if(LoadMultiPV(x, y, val, index, &start, &end)) {
254             XawTextSetSelection( outputField[currentPV][nMemo], start, end );
255             highTextStart[currentPV] = start; highTextEnd[currentPV] = end;
256         }
257 }
258
259 void
260 StopPV (Widget w, XEvent * event, String * params, Cardinal * nParams)
261 {       // [HGM] pv: on right-button release, stop displaying PV
262         XawTextUnsetSelection( w );
263         highTextStart[currentPV] = highTextEnd[currentPV] = 0;
264         UnLoadPV();
265 }
266
267 static void
268 MemoCB(Widget w, XtPointer client_data, Atom *selection,
269            Atom *type, XtPointer value, unsigned long *len, int *format)
270 {
271   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
272   selected_fen_position = value;
273   selected_fen_position[*len]='\0'; /* normally this string is terminated, but be safe */
274     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
275                    CurrentTime,
276                    SendPositionSelection,
277                    NULL/* lose_ownership_proc */ ,
278                    NULL/* transfer_done_proc */);
279 }
280
281 void CopyMemoProc(w, event, prms, nprms)
282   Widget w;
283   XEvent *event;
284   String *prms;
285   Cardinal *nprms;
286 {
287     if(appData.pasteSelection) return;
288     if (selected_fen_position) free(selected_fen_position);
289     XtGetSelectionValue(menuBarWidget, 
290       XA_PRIMARY, XA_STRING,
291       /* (XtSelectionCallbackProc) */ MemoCB,
292       NULL, /* client_data passed to PastePositionCB */
293
294       /* better to use the time field from the event that triggered the
295        * call to this function, but that isn't trivial to get
296        */
297       CurrentTime
298     );
299 }
300
301 // The following routines are mutated clones of the commentPopUp routines
302
303 void PositionControlSet(which, shell, form, bw_width)
304      int which;
305      Widget shell, form;
306      Dimension bw_width;
307 {
308     Arg args[16];
309     Widget edit, NameWidget, ColorWidget, ModeWidget, MoveWidget, NodesWidget;
310     int j, mutable=1;
311     j = 0;
312     XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
313     XtSetArg(args[j], XtNlabel,     (XtArgVal) ""); j++;
314     XtSetArg(args[j], XtNtop,       XtChainTop); j++;
315     XtSetArg(args[j], XtNbottom,    XtChainTop); j++;
316     XtSetArg(args[j], XtNleft,      XtChainLeft); j++;
317     XtSetArg(args[j], XtNright,     XtChainLeft); j++;
318     XtSetArg(args[j], XtNheight,    (XtArgVal) 16); j++;
319     XtSetArg(args[j], XtNwidth,     (XtArgVal) 17); j++;
320     outputField[which][nColorIcon] = ColorWidget =
321       XtCreateManagedWidget("Color", labelWidgetClass,
322                      form, args, j);
323
324     j = 0;
325     XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
326     XtSetArg(args[j], XtNjustify,   (XtArgVal) XtJustifyLeft); j++;
327     XtSetArg(args[j], XtNfromHoriz, (XtArgVal) ColorWidget); j++;
328     XtSetArg(args[j], XtNtop,       XtChainTop); j++;
329     XtSetArg(args[j], XtNbottom,    XtChainTop); j++;
330     XtSetArg(args[j], XtNleft,      XtChainLeft); j++;
331     XtSetArg(args[j], XtNheight,    (XtArgVal) 16); j++;
332     XtSetArg(args[j], XtNwidth,     (XtArgVal) bw_width/2 - 57); j++;
333     outputField[which][nLabel] = NameWidget =
334       XtCreateManagedWidget("Engine", labelWidgetClass,
335                      form, args, j);
336
337     j = 0;
338     XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
339     XtSetArg(args[j], XtNlabel,     (XtArgVal) ""); j++;
340     XtSetArg(args[j], XtNfromHoriz, (XtArgVal) NameWidget); j++;
341     XtSetArg(args[j], XtNtop,       XtChainTop); j++;
342     XtSetArg(args[j], XtNbottom,    XtChainTop); j++;
343     XtSetArg(args[j], XtNheight,    (XtArgVal) 16); j++;
344     XtSetArg(args[j], XtNwidth,     (XtArgVal) 20); j++;
345     outputField[which][nStateIcon] = ModeWidget =
346       XtCreateManagedWidget("Mode", labelWidgetClass,
347                      form, args, j);
348
349     j = 0;
350     XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
351     XtSetArg(args[j], XtNjustify,   (XtArgVal) XtJustifyLeft); j++;
352     XtSetArg(args[j], XtNlabel,     (XtArgVal) ""); j++;
353     XtSetArg(args[j], XtNfromHoriz, (XtArgVal) ModeWidget); j++;
354     XtSetArg(args[j], XtNtop,       XtChainTop); j++;
355     XtSetArg(args[j], XtNbottom,    XtChainTop); j++;
356     XtSetArg(args[j], XtNright,     XtChainRight); j++;
357     XtSetArg(args[j], XtNheight,    (XtArgVal) 16); j++;
358     XtSetArg(args[j], XtNwidth,     (XtArgVal) bw_width/2 - 102); j++;
359     outputField[which][nStateData] = MoveWidget =
360       XtCreateManagedWidget("Move", labelWidgetClass,
361                      form, args, j);
362
363     j = 0;
364     XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
365     XtSetArg(args[j], XtNjustify,   (XtArgVal) XtJustifyRight); j++;
366     XtSetArg(args[j], XtNlabel,     (XtArgVal) _("NPS")); j++;
367     XtSetArg(args[j], XtNfromHoriz, (XtArgVal) MoveWidget); j++;
368     XtSetArg(args[j], XtNtop,       XtChainTop); j++;
369     XtSetArg(args[j], XtNbottom,    XtChainTop); j++;
370     XtSetArg(args[j], XtNleft,      XtChainRight); j++;
371     XtSetArg(args[j], XtNright,     XtChainRight); j++;
372     XtSetArg(args[j], XtNheight,    (XtArgVal) 16); j++;
373     XtSetArg(args[j], XtNwidth,     (XtArgVal) 100); j++;
374     outputField[which][nLabelNPS] = NodesWidget =
375       XtCreateManagedWidget("Nodes", labelWidgetClass,
376                      form, args, j);
377
378     // create "text" within "form"
379     j = 0;
380     if (mutable) {
381         XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
382         XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
383     }
384     XtSetArg(args[j], XtNstring, "");  j++;
385     XtSetArg(args[j], XtNdisplayCaret, False);  j++;
386     XtSetArg(args[j], XtNtop, XtChainTop);  j++;
387     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;
388     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;
389     XtSetArg(args[j], XtNright, XtChainRight);  j++;
390     XtSetArg(args[j], XtNresizable, True);  j++;
391     XtSetArg(args[j], XtNwidth, bw_width);  j++; /*force wider than buttons*/
392     /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
393     XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++;
394     XtSetArg(args[j], XtNscrollHorizontal, XawtextScrollWhenNeeded);  j++;
395 //    XtSetArg(args[j], XtNautoFill, True);  j++;
396 //    XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
397     outputField[which][nMemo] = edit =
398       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
399
400     XtOverrideTranslations(edit, XtParseTranslationTable(memoTranslations));
401     XtAddEventHandler(edit, ButtonPressMask, False, SetFocus, (XtPointer) shell);
402
403     j = 0;
404     XtSetArg(args[j], XtNfromVert, ColorWidget); j++;
405 //    XtSetArg(args[j], XtNresizable, (XtArgVal) True); j++;
406     XtSetValues(edit, args, j);
407 }
408
409 Widget EngineOutputCreate(name, text)
410      char *name, *text;
411 {
412     Arg args[16];
413     Widget shell, layout, form, form2, edit;
414     Dimension bw_width, bw_height;
415     int j;
416
417     // get board width
418     j = 0;
419     XtSetArg(args[j], XtNwidth,  &bw_width);  j++;
420     XtSetArg(args[j], XtNheight, &bw_height);  j++;
421     XtGetValues(boardWidget, args, j);
422
423     // define form within layout within shell.
424     j = 0;
425     XtSetArg(args[j], XtNresizable, True);  j++;
426     shell =
427 #if TOPLEVEL 
428      XtCreatePopupShell(name, topLevelShellWidgetClass,
429 #else
430       XtCreatePopupShell(name, transientShellWidgetClass,
431 #endif
432                          shellWidget, args, j);
433     layout =
434       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
435                             layoutArgs, XtNumber(layoutArgs));
436     // divide window vertically into two equal parts, by creating two forms
437     form =
438       XtCreateManagedWidget("form", formWidgetClass, layout,
439                             formArgs, XtNumber(formArgs));
440     form2 =
441       XtCreateManagedWidget("form2", formWidgetClass, layout,
442                             formArgs, XtNumber(formArgs));
443     j = 0;
444     XtSetArg(args[j], XtNfromVert,  (XtArgVal) form); j++;
445     XtSetValues(form2, args, j);
446     // make sure width is known in advance, for better placement of child widgets
447     j = 0;
448     XtSetArg(args[j], XtNwidth,     (XtArgVal) bw_width-16); j++;
449     XtSetArg(args[j], XtNheight,    (XtArgVal) bw_height/2); j++;
450     XtSetValues(shell, args, j);
451
452     // fill up both forms with control elements
453     PositionControlSet(0, shell, form,  bw_width);
454     PositionControlSet(1, shell, form2, bw_width);
455
456     XtRealizeWidget(shell);
457
458     if(wpEngineOutput.width > 0) {
459       engineOutputW = wpEngineOutput.width;
460       engineOutputH = wpEngineOutput.height;
461       engineOutputX = wpEngineOutput.x;
462       engineOutputY = wpEngineOutput.y;
463     }
464
465     if (engineOutputX == -1) {
466         int xx, yy;
467         Window junk;
468         Dimension pw_height;
469         Dimension ew_height;
470         engineOutputH = bw_height/2;
471         engineOutputW = bw_width-16;
472
473         XSync(xDisplay, False);
474 #ifdef NOTDEF
475         /* This code seems to tickle an X bug if it is executed too soon
476            after xboard starts up.  The coordinates get transformed as if
477            the main window was positioned at (0, 0).
478            */
479         XtTranslateCoords(shellWidget,
480                           (bw_width - engineOutputW) / 2, 0 - engineOutputH / 2,
481                           &engineOutputX, &engineOutputY);
482 #else  /*!NOTDEF*/
483         XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
484                               RootWindowOfScreen(XtScreen(shellWidget)),
485                               (bw_width - engineOutputW) / 2, 0 - engineOutputH / 2,
486                               &xx, &yy, &junk);
487         engineOutputX = xx;
488         engineOutputY = yy;
489 #endif /*!NOTDEF*/
490         if (engineOutputY < 0) engineOutputY = 0; /*avoid positioning top offscreen*/
491     }
492     j = 0;
493     XtSetArg(args[j], XtNheight, engineOutputH);  j++;
494     XtSetArg(args[j], XtNwidth, engineOutputW);  j++;
495     XtSetArg(args[j], XtNx, engineOutputX);  j++;
496     XtSetArg(args[j], XtNy, engineOutputY);  j++;
497     XtSetValues(shell, args, j);
498 //    XtSetKeyboardFocus(shell, edit);
499
500     return shell;
501 }
502
503 void ResizeWindowControls(mode)
504         int mode;
505 {
506     Widget form1, form2;
507     Arg args[16];
508     int j;
509     Dimension ew_height, tmp;
510     Widget shell = engineOutputShell;
511
512     form1 = XtNameToWidget(shell, "*form");
513     form2 = XtNameToWidget(shell, "*form2");
514
515     j = 0;
516     XtSetArg(args[j], XtNheight, (XtArgVal) &ew_height); j++;
517     XtGetValues(form1, args, j);
518     j = 0;
519     XtSetArg(args[j], XtNheight, (XtArgVal) &tmp); j++;
520     XtGetValues(form2, args, j);
521     ew_height += tmp; // total height
522
523     if(mode==0) {
524         j = 0;
525         XtSetArg(args[j], XtNheight, (XtArgVal) 5); j++;
526         XtSetValues(form2, args, j);
527         j = 0;
528         XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height-5)); j++;
529         XtSetValues(form1, args, j);
530     } else {
531         j = 0;
532         XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height/2)); j++;
533         XtSetValues(form1, args, j);
534         j = 0;
535         XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height/2)); j++;
536         XtSetValues(form2, args, j);
537     }
538 }
539
540 void 
541 EngineOutputPopUp()
542 {
543     Arg args[16];
544     int j;
545     Widget edit;
546     static int  needInit = TRUE;
547     static char *title = _("Engine output"), *text = _("This feature is experimental");
548
549     if (engineOutputShell == NULL) {
550         engineOutputShell =
551           EngineOutputCreate(title, text);
552         XtRealizeWidget(engineOutputShell);
553         CatchDeleteWindow(engineOutputShell, "EngineOutputPopDown");
554         if( needInit ) {
555             InitializeEngineOutput();
556             needInit = FALSE;
557         }
558         SetEngineColorIcon( 0 );
559         SetEngineColorIcon( 1 );
560         SetEngineState( 0, STATE_IDLE, "" );
561         SetEngineState( 1, STATE_IDLE, "" );
562     } else {
563         edit = XtNameToWidget(engineOutputShell, "*form.text");
564         j = 0;
565         XtSetArg(args[j], XtNstring, text); j++;
566         XtSetValues(edit, args, j);
567         j = 0;
568         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
569         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
570         XtSetValues(engineOutputShell, args, j);
571     }
572
573     XtPopup(engineOutputShell, XtGrabNone);
574     XSync(xDisplay, False);
575
576     j=0;
577     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
578     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Show Engine Output"),
579                 args, j);
580
581     engineOutputDialogUp = True;
582     ShowThinkingEvent(); // [HGM] thinking: might need to prompt engine for thinking output
583 }
584
585 void EngineOutputPopDown()
586 {
587     Arg args[16];
588     int j;
589
590     if (!engineOutputDialogUp) return;
591     DoClearMemo(1);
592     j = 0;
593     XtSetArg(args[j], XtNx, &engineOutputX); j++;
594     XtSetArg(args[j], XtNy, &engineOutputY); j++;
595     XtSetArg(args[j], XtNwidth, &engineOutputW); j++;
596     XtSetArg(args[j], XtNheight, &engineOutputH); j++;
597     XtGetValues(engineOutputShell, args, j);
598     wpEngineOutput.x = engineOutputX - 4;
599     wpEngineOutput.y = engineOutputY - 23;
600     wpEngineOutput.width = engineOutputW;
601     wpEngineOutput.height = engineOutputH;
602     XtPopdown(engineOutputShell);
603     XSync(xDisplay, False);
604     j=0;
605     XtSetArg(args[j], XtNleftBitmap, None); j++;
606     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Show Engine Output"),
607                 args, j);
608
609     engineOutputDialogUp = False;
610     ShowThinkingEvent(); // [HGM] thinking: might need to shut off thinking output
611 }
612
613 int EngineOutputIsUp()
614 {
615     return engineOutputDialogUp;
616 }
617
618 int EngineOutputDialogExists()
619 {
620     return engineOutputShell != NULL;
621 }
622
623 void
624 EngineOutputProc(w, event, prms, nprms)
625      Widget w;
626      XEvent *event;
627      String *prms;
628      Cardinal *nprms;
629 {
630   if (engineOutputDialogUp) {
631     EngineOutputPopDown();
632   } else {
633     EngineOutputPopUp();
634   }
635 }