4 * Author: Alessandro Scotti (Dec 2005)
6 * Copyright 2005 Alessandro Scotti
8 * Enhancements Copyright 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
10 * ------------------------------------------------------------------------
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.
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.
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/.
25 * ------------------------------------------------------------------------
26 ** See the file ChangeLog for a revision history. */
33 #include <sys/types.h>
38 #else /* not STDC_HEADERS */
39 extern char *getenv();
42 # else /* not HAVE_STRING_H */
44 # endif /* not HAVE_STRING_H */
45 #endif /* not STDC_HEADERS */
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>
75 #include "engineoutput.h"
79 # define _(s) gettext (s)
80 # define N_(s) gettext_noop (s)
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"
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
106 void EngineOutputPopDown();
107 void engineOutputPopUp();
108 int EngineOutputIsUp();
109 void SetEngineColorIcon( int which );
111 //extern WindowPlacement wpEngineOutput;
113 Position engineOutputX = -1, engineOutputY = -1;
114 Dimension engineOutputW, engineOutputH;
115 Widget engineOutputShell;
116 static int engineOutputDialogUp;
118 /* Module variables */
120 static int currentPV, highTextStart[2], highTextEnd[2];
135 //static void UpdateControls( EngineOutputData * ed );
138 ReadIcon (char *pixData[], int iconNr)
142 if ((r=XpmCreatePixmapFromData(xDisplay, XtWindow(outputField[0][nColorIcon]),
145 NULL, NULL /*&attr*/)) != 0) {
146 fprintf(stderr, _("Error %d loading icon image\n"), r);
152 InitializeEngineOutput ()
154 ReadIcon(WHITE_14, nColorWhite);
155 ReadIcon(BLACK_14, nColorBlack);
156 ReadIcon(UNKNOWN_14, nColorUnknown);
158 ReadIcon(CLEAR_14, nClear);
159 ReadIcon(PONDER_14, nPondering);
160 ReadIcon(THINK_14, nThinking);
161 ReadIcon(ANALYZE_14, nAnalyzing);
165 DoSetWindowText (int which, int field, char *s_label)
169 XtSetArg(arg, XtNlabel, (XtArgVal) s_label);
170 XtSetValues(outputField[which][field], &arg, 1);
174 SetEngineOutputTitle (char *title)
177 XtSetArg(arg, XtNtitle, (XtArgVal) title);
178 XtSetValues(engineOutputShell, &arg, 1);
182 InsertIntoMemo (int which, char * text, int where)
187 /* the backend adds \r\n, which is needed for winboard,
188 * for xboard we delete them again over here */
189 if(t.ptr = strchr(text, '\r')) *t.ptr = ' ';
191 t.ptr = text; t.firstPos = 0; t.length = strlen(text); t.format = XawFmt8Bit;
192 edit = XtNameToWidget(engineOutputShell, which ? "*form2.text" : "*form.text");
193 XawTextReplace(edit, where, where, &t);
194 if(where < highTextStart[which]) { // [HGM] multiPVdisplay: move highlighting
195 int len = strlen(text);
196 highTextStart[which] += len; highTextEnd[which] += len;
197 XawTextSetSelection( outputField[which][nMemo], highTextStart[which], highTextEnd[which] );
202 SetIcon (int which, int field, int nIcon)
207 XtSetArg(arg, XtNleftBitmap, (XtArgVal) icons[nIcon]);
208 XtSetValues(outputField[which][field], &arg, 1);
213 DoClearMemo (int which)
215 Widget edit = XtNameToWidget(engineOutputShell, which ? "*form2.text" : "*form.text");
217 // XtCallActionProc(edit, "select-all", NULL, NULL, 0);
218 // XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
219 XtSetArg(arg, XtNstring, ""); // clear without disturbing selection!
220 XtSetValues(edit, &arg, 1);
223 // cloned from CopyPositionProc. Abuse selected_fen_position to hold selection
225 Boolean SendPositionSelection(Widget w, Atom *selection, Atom *target,
226 Atom *type_return, XtPointer *value_return,
227 unsigned long *length_return, int *format_return); // from xboard.c
228 void SetFocus(Widget w, XtPointer data, XEvent *event, Boolean *b); // from xoptions.c
230 char memoTranslations[] =
231 ":Ctrl<Key>c: CopyMemoProc() \n \
232 <Btn3Motion>: HandlePV() \n \
233 Shift<Btn3Down>: select-start() SelectPV(1) \n \
234 Any<Btn3Down>: select-start() SelectPV(0) \n \
235 <Btn3Up>: extend-end() StopPV() \n";
238 SelectPV (Widget w, XEvent * event, String * params, Cardinal * nParams)
239 { // [HGM] pv: translate click to PV line, and load it for display
242 XawTextPosition index, dummy;
246 x = event->xmotion.x; y = event->xmotion.y;
247 currentPV = (w == outputField[1][nMemo]);
248 XawTextGetSelectionPos(w, &index, &dummy);
249 XtSetArg(arg, XtNstring, &val);
250 XtGetValues(w, &arg, 1);
251 shiftKey = strcmp(params[0], "0");
252 if(LoadMultiPV(x, y, val, index, &start, &end)) {
253 XawTextSetSelection( outputField[currentPV][nMemo], start, end );
254 highTextStart[currentPV] = start; highTextEnd[currentPV] = end;
259 StopPV (Widget w, XEvent * event, String * params, Cardinal * nParams)
260 { // [HGM] pv: on right-button release, stop displaying PV
261 XawTextUnsetSelection( w );
262 highTextStart[currentPV] = highTextEnd[currentPV] = 0;
267 MemoCB (Widget w, XtPointer client_data, Atom *selection,
268 Atom *type, XtPointer value, unsigned long *len, int *format)
270 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
271 selected_fen_position = value;
272 selected_fen_position[*len]='\0'; /* normally this string is terminated, but be safe */
273 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
275 SendPositionSelection,
276 NULL/* lose_ownership_proc */ ,
277 NULL/* transfer_done_proc */);
281 CopyMemoProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
283 if(appData.pasteSelection) return;
284 if (selected_fen_position) free(selected_fen_position);
285 XtGetSelectionValue(menuBarWidget,
286 XA_PRIMARY, XA_STRING,
287 /* (XtSelectionCallbackProc) */ MemoCB,
288 NULL, /* client_data passed to PastePositionCB */
290 /* better to use the time field from the event that triggered the
291 * call to this function, but that isn't trivial to get
297 // The following routines are mutated clones of the commentPopUp routines
300 PositionControlSet (int which, Widget shell, Widget form, Dimension bw_width)
303 Widget edit, NameWidget, ColorWidget, ModeWidget, MoveWidget, NodesWidget;
306 XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
307 XtSetArg(args[j], XtNlabel, (XtArgVal) ""); j++;
308 XtSetArg(args[j], XtNtop, XtChainTop); j++;
309 XtSetArg(args[j], XtNbottom, XtChainTop); j++;
310 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
311 XtSetArg(args[j], XtNright, XtChainLeft); j++;
312 XtSetArg(args[j], XtNheight, (XtArgVal) 16); j++;
313 XtSetArg(args[j], XtNwidth, (XtArgVal) 17); j++;
314 outputField[which][nColorIcon] = ColorWidget =
315 XtCreateManagedWidget("Color", labelWidgetClass,
319 XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
320 XtSetArg(args[j], XtNjustify, (XtArgVal) XtJustifyLeft); j++;
321 XtSetArg(args[j], XtNfromHoriz, (XtArgVal) ColorWidget); j++;
322 XtSetArg(args[j], XtNtop, XtChainTop); j++;
323 XtSetArg(args[j], XtNbottom, XtChainTop); j++;
324 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
325 XtSetArg(args[j], XtNheight, (XtArgVal) 16); j++;
326 XtSetArg(args[j], XtNwidth, (XtArgVal) bw_width/2 - 57); j++;
327 outputField[which][nLabel] = NameWidget =
328 XtCreateManagedWidget("Engine", labelWidgetClass,
332 XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
333 XtSetArg(args[j], XtNlabel, (XtArgVal) ""); j++;
334 XtSetArg(args[j], XtNfromHoriz, (XtArgVal) NameWidget); j++;
335 XtSetArg(args[j], XtNtop, XtChainTop); j++;
336 XtSetArg(args[j], XtNbottom, XtChainTop); j++;
337 XtSetArg(args[j], XtNheight, (XtArgVal) 16); j++;
338 XtSetArg(args[j], XtNwidth, (XtArgVal) 20); j++;
339 outputField[which][nStateIcon] = ModeWidget =
340 XtCreateManagedWidget("Mode", labelWidgetClass,
344 XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
345 XtSetArg(args[j], XtNjustify, (XtArgVal) XtJustifyLeft); j++;
346 XtSetArg(args[j], XtNlabel, (XtArgVal) ""); j++;
347 XtSetArg(args[j], XtNfromHoriz, (XtArgVal) ModeWidget); j++;
348 XtSetArg(args[j], XtNtop, XtChainTop); j++;
349 XtSetArg(args[j], XtNbottom, XtChainTop); j++;
350 XtSetArg(args[j], XtNright, XtChainRight); j++;
351 XtSetArg(args[j], XtNheight, (XtArgVal) 16); j++;
352 XtSetArg(args[j], XtNwidth, (XtArgVal) bw_width/2 - 102); j++;
353 outputField[which][nStateData] = MoveWidget =
354 XtCreateManagedWidget("Move", labelWidgetClass,
358 XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
359 XtSetArg(args[j], XtNjustify, (XtArgVal) XtJustifyRight); j++;
360 XtSetArg(args[j], XtNlabel, (XtArgVal) _("NPS")); j++;
361 XtSetArg(args[j], XtNfromHoriz, (XtArgVal) MoveWidget); j++;
362 XtSetArg(args[j], XtNtop, XtChainTop); j++;
363 XtSetArg(args[j], XtNbottom, XtChainTop); j++;
364 XtSetArg(args[j], XtNleft, XtChainRight); j++;
365 XtSetArg(args[j], XtNright, XtChainRight); j++;
366 XtSetArg(args[j], XtNheight, (XtArgVal) 16); j++;
367 XtSetArg(args[j], XtNwidth, (XtArgVal) 100); j++;
368 outputField[which][nLabelNPS] = NodesWidget =
369 XtCreateManagedWidget("Nodes", labelWidgetClass,
372 // create "text" within "form"
375 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
376 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
378 XtSetArg(args[j], XtNstring, ""); j++;
379 XtSetArg(args[j], XtNdisplayCaret, False); j++;
380 XtSetArg(args[j], XtNtop, XtChainTop); j++;
381 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
382 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
383 XtSetArg(args[j], XtNright, XtChainRight); j++;
384 XtSetArg(args[j], XtNresizable, True); j++;
385 XtSetArg(args[j], XtNwidth, bw_width); j++; /*force wider than buttons*/
386 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
387 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
388 XtSetArg(args[j], XtNscrollHorizontal, XawtextScrollWhenNeeded); j++;
389 // XtSetArg(args[j], XtNautoFill, True); j++;
390 // XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
391 outputField[which][nMemo] = edit =
392 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
394 XtOverrideTranslations(edit, XtParseTranslationTable(memoTranslations));
395 XtAddEventHandler(edit, ButtonPressMask, False, SetFocus, (XtPointer) shell);
398 XtSetArg(args[j], XtNfromVert, ColorWidget); j++;
399 // XtSetArg(args[j], XtNresizable, (XtArgVal) True); j++;
400 XtSetValues(edit, args, j);
404 EngineOutputCreate (char *name, char *text)
407 Widget shell, layout, form, form2;
408 Dimension bw_width, bw_height;
413 XtSetArg(args[j], XtNwidth, &bw_width); j++;
414 XtSetArg(args[j], XtNheight, &bw_height); j++;
415 XtGetValues(boardWidget, args, j);
417 // define form within layout within shell.
419 XtSetArg(args[j], XtNresizable, True); j++;
422 XtCreatePopupShell(name, topLevelShellWidgetClass,
424 XtCreatePopupShell(name, transientShellWidgetClass,
426 shellWidget, args, j);
428 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
429 layoutArgs, XtNumber(layoutArgs));
430 // divide window vertically into two equal parts, by creating two forms
432 XtCreateManagedWidget("form", formWidgetClass, layout,
433 formArgs, XtNumber(formArgs));
435 XtCreateManagedWidget("form2", formWidgetClass, layout,
436 formArgs, XtNumber(formArgs));
438 XtSetArg(args[j], XtNfromVert, (XtArgVal) form); j++;
439 XtSetValues(form2, args, j);
440 // make sure width is known in advance, for better placement of child widgets
442 XtSetArg(args[j], XtNwidth, (XtArgVal) bw_width-16); j++;
443 XtSetArg(args[j], XtNheight, (XtArgVal) bw_height/2); j++;
444 XtSetValues(shell, args, j);
446 // fill up both forms with control elements
447 PositionControlSet(0, shell, form, bw_width);
448 PositionControlSet(1, shell, form2, bw_width);
450 XtRealizeWidget(shell);
452 if(wpEngineOutput.width > 0) {
453 engineOutputW = wpEngineOutput.width;
454 engineOutputH = wpEngineOutput.height;
455 engineOutputX = wpEngineOutput.x;
456 engineOutputY = wpEngineOutput.y;
459 if (engineOutputX == -1) {
463 engineOutputH = bw_height/2;
464 engineOutputW = bw_width-16;
466 XSync(xDisplay, False);
468 /* This code seems to tickle an X bug if it is executed too soon
469 after xboard starts up. The coordinates get transformed as if
470 the main window was positioned at (0, 0).
472 XtTranslateCoords(shellWidget,
473 (bw_width - engineOutputW) / 2, 0 - engineOutputH / 2,
474 &engineOutputX, &engineOutputY);
476 XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
477 RootWindowOfScreen(XtScreen(shellWidget)),
478 (bw_width - engineOutputW) / 2, 0 - engineOutputH / 2,
483 if (engineOutputY < 0) engineOutputY = 0; /*avoid positioning top offscreen*/
486 XtSetArg(args[j], XtNheight, engineOutputH); j++;
487 XtSetArg(args[j], XtNwidth, engineOutputW); j++;
488 XtSetArg(args[j], XtNx, engineOutputX); j++;
489 XtSetArg(args[j], XtNy, engineOutputY); j++;
490 XtSetValues(shell, args, j);
496 ResizeWindowControls (int mode)
501 Dimension ew_height, tmp;
502 Widget shell = engineOutputShell;
504 form1 = XtNameToWidget(shell, "*form");
505 form2 = XtNameToWidget(shell, "*form2");
508 XtSetArg(args[j], XtNheight, (XtArgVal) &ew_height); j++;
509 XtGetValues(form1, args, j);
511 XtSetArg(args[j], XtNheight, (XtArgVal) &tmp); j++;
512 XtGetValues(form2, args, j);
513 ew_height += tmp; // total height
517 XtSetArg(args[j], XtNheight, (XtArgVal) 5); j++;
518 XtSetValues(form2, args, j);
520 XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height-5)); j++;
521 XtSetValues(form1, args, j);
524 XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height/2)); j++;
525 XtSetValues(form1, args, j);
527 XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height/2)); j++;
528 XtSetValues(form2, args, j);
538 static int needInit = TRUE;
539 static char *title = N_("Engine output"), *text = N_("This feature is experimental");
541 if (engineOutputShell == NULL) {
543 EngineOutputCreate(_(title), _(text));
544 XtRealizeWidget(engineOutputShell);
545 CatchDeleteWindow(engineOutputShell, "EngineOutputPopDown");
547 InitializeEngineOutput();
550 SetEngineColorIcon( 0 );
551 SetEngineColorIcon( 1 );
552 SetEngineState( 0, STATE_IDLE, "" );
553 SetEngineState( 1, STATE_IDLE, "" );
555 edit = XtNameToWidget(engineOutputShell, "*form.text");
557 XtSetArg(args[j], XtNstring, text); j++;
558 XtSetValues(edit, args, j);
560 XtSetArg(args[j], XtNiconName, (XtArgVal) _(title)); j++;
561 XtSetArg(args[j], XtNtitle, (XtArgVal) _(title)); j++;
562 XtSetValues(engineOutputShell, args, j);
565 XtPopup(engineOutputShell, XtGrabNone);
566 XSync(xDisplay, False);
569 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
570 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Engine Output"),
573 engineOutputDialogUp = True;
574 ShowThinkingEvent(); // [HGM] thinking: might need to prompt engine for thinking output
578 EngineOutputPopDown ()
583 if (!engineOutputDialogUp) return;
586 XtSetArg(args[j], XtNx, &engineOutputX); j++;
587 XtSetArg(args[j], XtNy, &engineOutputY); j++;
588 XtSetArg(args[j], XtNwidth, &engineOutputW); j++;
589 XtSetArg(args[j], XtNheight, &engineOutputH); j++;
590 XtGetValues(engineOutputShell, args, j);
591 wpEngineOutput.x = engineOutputX - 4;
592 wpEngineOutput.y = engineOutputY - 23;
593 wpEngineOutput.width = engineOutputW;
594 wpEngineOutput.height = engineOutputH;
595 XtPopdown(engineOutputShell);
596 XSync(xDisplay, False);
598 XtSetArg(args[j], XtNleftBitmap, None); j++;
599 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Engine Output"),
602 engineOutputDialogUp = False;
603 ShowThinkingEvent(); // [HGM] thinking: might need to shut off thinking output
609 return engineOutputDialogUp;
613 EngineOutputDialogExists ()
615 return engineOutputShell != NULL;
619 EngineOutputProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
621 if (engineOutputDialogUp) {
622 EngineOutputPopDown();