2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
66 # if HAVE_SYS_SOCKET_H
67 # include <sys/socket.h>
68 # include <netinet/in.h>
70 # else /* not HAVE_SYS_SOCKET_H */
71 # if HAVE_LAN_SOCKET_H
72 # include <lan/socket.h>
74 # include <lan/netdb.h>
75 # else /* not HAVE_LAN_SOCKET_H */
76 # define OMIT_SOCKETS 1
77 # endif /* not HAVE_LAN_SOCKET_H */
78 # endif /* not HAVE_SYS_SOCKET_H */
79 #endif /* !OMIT_SOCKETS */
84 #else /* not STDC_HEADERS */
85 extern char *getenv();
88 # else /* not HAVE_STRING_H */
90 # endif /* not HAVE_STRING_H */
91 #endif /* not STDC_HEADERS */
94 # include <sys/fcntl.h>
95 #else /* not HAVE_SYS_FCNTL_H */
98 # endif /* HAVE_FCNTL_H */
99 #endif /* not HAVE_SYS_FCNTL_H */
101 #if HAVE_SYS_SYSTEMINFO_H
102 # include <sys/systeminfo.h>
103 #endif /* HAVE_SYS_SYSTEMINFO_H */
105 #if TIME_WITH_SYS_TIME
106 # include <sys/time.h>
110 # include <sys/time.h>
121 # include <sys/wait.h>
126 # define NAMLEN(dirent) strlen((dirent)->d_name)
127 # define HAVE_DIR_STRUCT
129 # define dirent direct
130 # define NAMLEN(dirent) (dirent)->d_namlen
132 # include <sys/ndir.h>
133 # define HAVE_DIR_STRUCT
136 # include <sys/dir.h>
137 # define HAVE_DIR_STRUCT
141 # define HAVE_DIR_STRUCT
149 #include <X11/Intrinsic.h>
150 #include <X11/StringDefs.h>
151 #include <X11/Shell.h>
152 #include <X11/cursorfont.h>
153 #include <X11/Xatom.h>
154 #include <X11/Xmu/Atoms.h>
156 #include <X11/Xaw3d/Dialog.h>
157 #include <X11/Xaw3d/Form.h>
158 #include <X11/Xaw3d/List.h>
159 #include <X11/Xaw3d/Label.h>
160 #include <X11/Xaw3d/SimpleMenu.h>
161 #include <X11/Xaw3d/SmeBSB.h>
162 #include <X11/Xaw3d/SmeLine.h>
163 #include <X11/Xaw3d/Box.h>
164 #include <X11/Xaw3d/MenuButton.h>
165 #include <X11/Xaw3d/Text.h>
166 #include <X11/Xaw3d/AsciiText.h>
168 #include <X11/Xaw/Dialog.h>
169 #include <X11/Xaw/Form.h>
170 #include <X11/Xaw/List.h>
171 #include <X11/Xaw/Label.h>
172 #include <X11/Xaw/SimpleMenu.h>
173 #include <X11/Xaw/SmeBSB.h>
174 #include <X11/Xaw/SmeLine.h>
175 #include <X11/Xaw/Box.h>
176 #include <X11/Xaw/MenuButton.h>
177 #include <X11/Xaw/Text.h>
178 #include <X11/Xaw/AsciiText.h>
181 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
186 #include "pixmaps/pixmaps.h"
187 #define IMAGE_EXT "xpm"
189 #define IMAGE_EXT "xim"
190 #include "bitmaps/bitmaps.h"
193 #include "bitmaps/icon_white.bm"
194 #include "bitmaps/icon_black.bm"
195 #include "bitmaps/checkmark.bm"
197 #include "frontend.h"
199 #include "backendz.h"
203 #include "xgamelist.h"
204 #include "xhistory.h"
205 #include "xedittags.h"
209 #include "engineoutput.h"
218 #define usleep(t) _sleep2(((t)+500)/1000)
222 # define _(s) gettext (s)
223 # define N_(s) gettext_noop (s)
229 int main P((int argc, char **argv));
230 RETSIGTYPE CmailSigHandler P((int sig));
231 RETSIGTYPE IntSigHandler P((int sig));
232 RETSIGTYPE TermSizeSigHandler P((int sig));
233 static void CreateGCs P((int redo));
234 static void CreateAnyPieces P((void));
235 void CreateXIMPieces P((void));
236 void CreateXPMPieces P((void));
237 void CreateXPMBoard P((char *s, int n));
238 void CreatePieces P((void));
239 Widget CreateMenuBar P((Menu *mb, int boardWidth));
241 char *InsertPxlSize P((char *pattern, int targetPxlSize));
242 XFontSet CreateFontSet P((char *base_fnt_lst));
244 char *FindFont P((char *pattern, int targetPxlSize));
246 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
247 u_int wreq, u_int hreq));
248 void CreateGrid P((void));
249 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
250 void DelayedDrag P((void));
251 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
252 void HandlePV P((Widget w, XEvent * event,
253 String * params, Cardinal * nParams));
254 void DrawPositionProc P((Widget w, XEvent *event,
255 String *prms, Cardinal *nprms));
256 void CommentClick P((Widget w, XEvent * event,
257 String * params, Cardinal * nParams));
258 void ICSInputBoxPopUp P((void));
259 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
260 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
261 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
262 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
263 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
264 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
265 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
266 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
267 Boolean TempBackwardActive = False;
268 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
269 void DisplayMove P((int moveNumber));
270 void ICSInitScript P((void));
271 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
272 void update_ics_width P(());
273 int CopyMemoProc P(());
276 * XBoard depends on Xt R4 or higher
278 int xtVersion = XtSpecificationRelease;
283 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
284 highlightSquareColor, premoveHighlightColor, dialogColor, buttonColor;
285 Pixel lowTimeWarningColor;
286 GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
287 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
289 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
290 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
291 Option *optList; // contains all widgets of main window
292 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
294 XFontSet fontSet, clockFontSet;
297 XFontStruct *clockFontStruct;
299 Font coordFontID, countFontID;
300 XFontStruct *coordFontStruct, *countFontStruct;
301 XtAppContext appContext;
304 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
306 Position commentX = -1, commentY = -1;
307 Dimension commentW, commentH;
308 typedef unsigned int BoardSize;
310 Boolean chessProgram;
312 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
313 int smallLayout = 0, tinyLayout = 0,
314 marginW, marginH, // [HGM] for run-time resizing
315 fromX = -1, fromY = -1, toX, toY, commentUp = False,
316 errorExitStatus = -1, defaultLineGap;
317 Dimension textHeight;
318 Pixel timerForegroundPixel, timerBackgroundPixel;
319 Pixel buttonForegroundPixel, buttonBackgroundPixel;
320 char *chessDir, *programName, *programVersion;
321 Boolean alwaysOnTop = False;
322 char *icsTextMenuString;
324 char *firstChessProgramNames;
325 char *secondChessProgramNames;
327 WindowPlacement wpMain;
328 WindowPlacement wpConsole;
329 WindowPlacement wpComment;
330 WindowPlacement wpMoveHistory;
331 WindowPlacement wpEvalGraph;
332 WindowPlacement wpEngineOutput;
333 WindowPlacement wpGameList;
334 WindowPlacement wpTags;
339 Pixmap pieceBitmap[2][(int)BlackPawn];
340 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
341 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
342 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
343 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
344 Pixmap xpmBoardBitmap[2];
345 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
346 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
347 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
348 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
349 XImage *ximLightSquare, *ximDarkSquare;
352 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
353 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
355 #define White(piece) ((int)(piece) < (int)BlackPawn)
357 /* Bitmaps for use as masks when drawing XPM pieces.
358 Need one for each black and white piece. */
359 static Pixmap xpmMask[BlackKing + 1];
361 /* This magic number is the number of intermediate frames used
362 in each half of the animation. For short moves it's reduced
363 by 1. The total number of frames will be factor * 2 + 1. */
366 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
373 DropMenuEnables dmEnables[] = {
390 XtResource clientResources[] = {
391 { "flashCount", "flashCount", XtRInt, sizeof(int),
392 XtOffset(AppDataPtr, flashCount), XtRImmediate,
393 (XtPointer) FLASH_COUNT },
396 XrmOptionDescRec shellOptions[] = {
397 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
398 { "-flash", "flashCount", XrmoptionNoArg, "3" },
399 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
402 XtActionsRec boardActions[] = {
403 { "DrawPosition", DrawPositionProc },
404 { "HandlePV", HandlePV },
405 { "SelectPV", SelectPV },
406 { "StopPV", StopPV },
407 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
408 { "QuitProc", QuitWrapper },
409 { "ManProc", ManInner },
410 { "TempBackwardProc", TempBackwardProc },
411 { "TempForwardProc", TempForwardProc },
412 { "CommentClick", (XtActionProc) CommentClick },
413 { "GenericPopDown", (XtActionProc) GenericPopDown },
414 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
415 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
416 { "SelectMove", (XtActionProc) SelectMove },
417 { "LoadSelectedProc", LoadSelectedProc },
418 { "SetFilterProc", SetFilterProc },
419 { "TypeInProc", TypeInProc },
420 { "EnterKeyProc", EnterKeyProc },
421 { "UpKeyProc", UpKeyProc },
422 { "DownKeyProc", DownKeyProc },
423 { "WheelProc", WheelProc },
424 { "TabProc", TabProc },
427 char globalTranslations[] =
428 ":<Key>F9: MenuItem(Actions.Resign) \n \
429 :Ctrl<Key>n: MenuItem(File.NewGame) \n \
430 :Meta<Key>V: MenuItem(File.NewVariant) \n \
431 :Ctrl<Key>o: MenuItem(File.LoadGame) \n \
432 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
433 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
434 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
435 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
436 :Ctrl<Key>s: MenuItem(File.SaveGame) \n \
437 :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
438 :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
439 :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
440 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
441 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
442 :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
443 :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
444 :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
445 :Ctrl<Key>q: MenuItem(File.Quit) \n \
446 :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
447 :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
448 :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
449 :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
450 :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
451 :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
452 :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
453 :Meta<Key>O: MenuItem(View.EngineOutput) \n \
454 :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
455 :Meta<Key>G: MenuItem(View.GameList) \n \
456 :Meta<Key>H: MenuItem(View.MoveHistory) \n \
457 :<Key>Pause: MenuItem(Mode.Pause) \n \
458 :<Key>F3: MenuItem(Action.Accept) \n \
459 :<Key>F4: MenuItem(Action.Decline) \n \
460 :<Key>F12: MenuItem(Action.Rematch) \n \
461 :<Key>F5: MenuItem(Action.CallFlag) \n \
462 :<Key>F6: MenuItem(Action.Draw) \n \
463 :<Key>F7: MenuItem(Action.Adjourn) \n \
464 :<Key>F8: MenuItem(Action.Abort) \n \
465 :<Key>F10: MenuItem(Action.StopObserving) \n \
466 :<Key>F11: MenuItem(Action.StopExamining) \n \
467 :Ctrl<Key>d: MenuItem(DebugProc) \n \
468 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
469 :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
470 :Meta<Key>Right: MenuItem(Edit.Forward) \n \
471 :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
472 :Meta<Key>Left: MenuItem(Edit.Backward) \n \
473 :<Key>Left: MenuItem(Edit.Backward) \n \
474 :<Key>Right: MenuItem(Edit.Forward) \n \
475 :<Key>Home: MenuItem(Edit.Revert) \n \
476 :<Key>End: MenuItem(Edit.TruncateGame) \n \
477 :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
478 :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
479 :Meta<Key>J: MenuItem(Options.Adjudications) \n \
480 :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
481 :Meta<Key>T: MenuItem(Options.TimeControl) \n \
482 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
483 #ifndef OPTIONSDIALOG
485 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
486 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
487 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
488 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
489 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
492 :<Key>F1: MenuItem(Help.ManXBoard) \n \
493 :<Key>F2: MenuItem(View.FlipView) \n \
494 :<KeyDown>Return: TempBackwardProc() \n \
495 :<KeyUp>Return: TempForwardProc() \n";
497 char ICSInputTranslations[] =
498 "<Key>Up: UpKeyProc() \n "
499 "<Key>Down: DownKeyProc() \n "
500 "<Key>Return: EnterKeyProc() \n";
502 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
503 // as the widget is destroyed before the up-click can call extend-end
504 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
506 String xboardResources[] = {
507 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
512 /* Max possible square size */
513 #define MAXSQSIZE 256
515 static int xpm_avail[MAXSQSIZE];
517 #ifdef HAVE_DIR_STRUCT
519 /* Extract piece size from filename */
521 xpm_getsize (char *name, int len, char *ext)
529 if ((p=strchr(name, '.')) == NULL ||
530 StrCaseCmp(p+1, ext) != 0)
536 while (*p && isdigit(*p))
543 /* Setup xpm_avail */
545 xpm_getavail (char *dirname, char *ext)
551 for (i=0; i<MAXSQSIZE; ++i)
554 if (appData.debugMode)
555 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
557 dir = opendir(dirname);
560 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
561 programName, dirname);
565 while ((ent=readdir(dir)) != NULL) {
566 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
567 if (i > 0 && i < MAXSQSIZE)
577 xpm_print_avail (FILE *fp, char *ext)
581 fprintf(fp, _("Available `%s' sizes:\n"), ext);
582 for (i=1; i<MAXSQSIZE; ++i) {
588 /* Return XPM piecesize closest to size */
590 xpm_closest_to (char *dirname, int size, char *ext)
593 int sm_diff = MAXSQSIZE;
597 xpm_getavail(dirname, ext);
599 if (appData.debugMode)
600 xpm_print_avail(stderr, ext);
602 for (i=1; i<MAXSQSIZE; ++i) {
605 diff = (diff<0) ? -diff : diff;
606 if (diff < sm_diff) {
614 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
620 #else /* !HAVE_DIR_STRUCT */
621 /* If we are on a system without a DIR struct, we can't
622 read the directory, so we can't collect a list of
623 filenames, etc., so we can't do any size-fitting. */
625 xpm_closest_to (char *dirname, int size, char *ext)
628 Warning: No DIR structure found on this system --\n\
629 Unable to autosize for XPM/XIM pieces.\n\
630 Please report this error to %s.\n\
631 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
634 #endif /* HAVE_DIR_STRUCT */
637 /* Arrange to catch delete-window events */
638 Atom wm_delete_window;
640 CatchDeleteWindow (Widget w, String procname)
643 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
644 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
645 XtAugmentTranslations(w, XtParseTranslationTable(buf));
652 XtSetArg(args[0], XtNiconic, False);
653 XtSetValues(shellWidget, args, 1);
655 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
658 //---------------------------------------------------------------------------------------------------------
659 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
662 #define CW_USEDEFAULT (1<<31)
663 #define ICS_TEXT_MENU_SIZE 90
664 #define DEBUG_FILE "xboard.debug"
665 #define SetCurrentDirectory chdir
666 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
670 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
673 // front-end part of option handling
675 // [HGM] This platform-dependent table provides the location for storing the color info
676 extern char *crWhite, * crBlack;
680 &appData.whitePieceColor,
681 &appData.blackPieceColor,
682 &appData.lightSquareColor,
683 &appData.darkSquareColor,
684 &appData.highlightSquareColor,
685 &appData.premoveHighlightColor,
686 &appData.lowTimeWarningColor,
697 // [HGM] font: keep a font for each square size, even non-stndard ones
700 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
701 char *fontTable[NUM_FONTS][MAX_SIZE];
704 ParseFont (char *name, int number)
705 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
707 if(sscanf(name, "size%d:", &size)) {
708 // [HGM] font: font is meant for specific boardSize (likely from settings file);
709 // defer processing it until we know if it matches our board size
710 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
711 fontTable[number][size] = strdup(strchr(name, ':')+1);
712 fontValid[number][size] = True;
717 case 0: // CLOCK_FONT
718 appData.clockFont = strdup(name);
720 case 1: // MESSAGE_FONT
721 appData.font = strdup(name);
723 case 2: // COORD_FONT
724 appData.coordFont = strdup(name);
729 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
734 { // only 2 fonts currently
735 appData.clockFont = CLOCK_FONT_NAME;
736 appData.coordFont = COORD_FONT_NAME;
737 appData.font = DEFAULT_FONT_NAME;
742 { // no-op, until we identify the code for this already in XBoard and move it here
746 ParseColor (int n, char *name)
747 { // in XBoard, just copy the color-name string
748 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
752 ParseTextAttribs (ColorClass cc, char *s)
754 (&appData.colorShout)[cc] = strdup(s);
758 ParseBoardSize (void *addr, char *name)
760 appData.boardSize = strdup(name);
765 { // In XBoard the sound-playing program takes care of obtaining the actual sound
769 SetCommPortDefaults ()
770 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
773 // [HGM] args: these three cases taken out to stay in front-end
775 SaveFontArg (FILE *f, ArgDescriptor *ad)
778 int i, n = (int)(intptr_t)ad->argLoc;
780 case 0: // CLOCK_FONT
781 name = appData.clockFont;
783 case 1: // MESSAGE_FONT
786 case 2: // COORD_FONT
787 name = appData.coordFont;
792 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
793 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
794 fontTable[n][squareSize] = strdup(name);
795 fontValid[n][squareSize] = True;
798 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
799 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
804 { // nothing to do, as the sounds are at all times represented by their text-string names already
808 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
809 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
810 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
814 SaveColor (FILE *f, ArgDescriptor *ad)
815 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
816 if(colorVariable[(int)(intptr_t)ad->argLoc])
817 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
821 SaveBoardSize (FILE *f, char *name, void *addr)
822 { // wrapper to shield back-end from BoardSize & sizeInfo
823 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
827 ParseCommPortSettings (char *s)
828 { // no such option in XBoard (yet)
834 GetActualPlacement (Widget wg, WindowPlacement *wp)
836 XWindowAttributes winAt;
843 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
844 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
845 wp->x = rx - winAt.x;
846 wp->y = ry - winAt.y;
847 wp->height = winAt.height;
848 wp->width = winAt.width;
849 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
854 { // wrapper to shield use of window handles from back-end (make addressible by number?)
855 // In XBoard this will have to wait until awareness of window parameters is implemented
856 GetActualPlacement(shellWidget, &wpMain);
857 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
858 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
859 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
860 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
861 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
862 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
866 PrintCommPortSettings (FILE *f, char *name)
867 { // This option does not exist in XBoard
871 EnsureOnScreen (int *x, int *y, int minX, int minY)
878 { // [HGM] args: allows testing if main window is realized from back-end
879 return xBoardWindow != 0;
885 extern Option dualOptions[];
887 Window tmp = xBoardWindow;
888 if(!dual) dual = XtWindow(dualOptions[3].handle); // must be first call
889 xBoardWindow = dual; // swap them
894 PopUpStartupDialog ()
895 { // start menu not implemented in XBoard
899 ConvertToLine (int argc, char **argv)
901 static char line[128*1024], buf[1024];
905 for(i=1; i<argc; i++)
907 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
908 && argv[i][0] != '{' )
909 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
911 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
912 strncat(line, buf, 128*1024 - strlen(line) - 1 );
915 line[strlen(line)-1] = NULLCHAR;
919 //--------------------------------------------------------------------------------------------
921 #define BoardSize int
923 InitDrawingSizes (BoardSize boardSize, int flags)
924 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
925 Dimension boardWidth, boardHeight, w, h;
927 static Dimension oldWidth, oldHeight;
928 static VariantClass oldVariant;
929 static int oldMono = -1, oldTwoBoards = 0;
931 if(!formWidget) return;
933 if(oldTwoBoards && !twoBoards) PopDown(DummyDlg);
934 oldTwoBoards = twoBoards;
936 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
937 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
938 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
940 if(boardWidth != oldWidth || boardHeight != oldHeight) { // do resizing stuff only if size actually changed
942 oldWidth = boardWidth; oldHeight = boardHeight;
946 * Inhibit shell resizing.
948 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW ;
949 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
950 shellArgs[4].value = shellArgs[2].value = w;
951 shellArgs[5].value = shellArgs[3].value = h;
952 XtSetValues(shellWidget, &shellArgs[0], 6);
954 XSync(xDisplay, False);
958 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
961 if(gameInfo.variant != oldVariant) { // and only if variant changed
966 for(p=0; p<=(int)WhiteKing; p++)
967 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
968 if(gameInfo.variant == VariantShogi) {
969 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
970 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
971 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
972 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
973 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
976 if(gameInfo.variant == VariantGothic) {
977 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
980 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
981 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
982 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
985 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
986 for(p=0; p<=(int)WhiteKing; p++)
987 ximMaskPm[p] = ximMaskPm2[p]; // defaults
988 if(gameInfo.variant == VariantShogi) {
989 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
990 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
991 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
992 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
993 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
996 if(gameInfo.variant == VariantGothic) {
997 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1000 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1001 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1002 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1007 for(i=0; i<2; i++) {
1009 for(p=0; p<=(int)WhiteKing; p++)
1010 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1011 if(gameInfo.variant == VariantShogi) {
1012 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1013 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1014 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1015 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1016 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1019 if(gameInfo.variant == VariantGothic) {
1020 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1023 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1024 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1025 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1029 oldMono = -10; // kludge to force recreation of animation masks
1030 oldVariant = gameInfo.variant;
1033 if(appData.monoMode != oldMono)
1036 oldMono = appData.monoMode;
1040 MakeOneColor (char *name, Pixel *color)
1042 XrmValue vFrom, vTo;
1043 if (!appData.monoMode) {
1044 vFrom.addr = (caddr_t) name;
1045 vFrom.size = strlen(name);
1046 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1047 if (vTo.addr == NULL) {
1048 appData.monoMode = True;
1051 *color = *(Pixel *) vTo.addr;
1059 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1060 int forceMono = False;
1062 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1063 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1064 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1065 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1066 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1067 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1068 if (appData.lowTimeWarning)
1069 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
1070 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1071 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1078 { // [HGM] taken out of main
1080 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1081 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1082 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1084 if (appData.bitmapDirectory[0] != NULLCHAR) {
1088 CreateXPMBoard(appData.liteBackTextureFile, 1);
1089 CreateXPMBoard(appData.darkBackTextureFile, 0);
1093 /* Create regular pieces */
1094 if (!useImages) CreatePieces();
1099 InitDrawingParams ()
1101 MakeColors(); CreateGCs(True);
1106 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
1107 { // detervtomine what fonts to use, and create them
1111 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1112 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1113 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1114 appData.font = fontTable[MESSAGE_FONT][squareSize];
1115 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1116 appData.coordFont = fontTable[COORD_FONT][squareSize];
1119 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1120 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1121 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1122 fontSet = CreateFontSet(appData.font);
1123 clockFontSet = CreateFontSet(appData.clockFont);
1125 /* For the coordFont, use the 0th font of the fontset. */
1126 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1127 XFontStruct **font_struct_list;
1128 XFontSetExtents *fontSize;
1129 char **font_name_list;
1130 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1131 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1132 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1133 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1134 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1137 appData.font = FindFont(appData.font, fontPxlSize);
1138 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1139 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1140 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1141 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1142 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1143 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1144 // textHeight in !NLS mode!
1146 countFontID = coordFontID; // [HGM] holdings
1147 countFontStruct = coordFontStruct;
1149 xdb = XtDatabase(xDisplay);
1151 XrmPutLineResource(&xdb, "*international: True");
1152 vTo.size = sizeof(XFontSet);
1153 vTo.addr = (XtPointer) &fontSet;
1154 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1156 XrmPutStringResource(&xdb, "*font", appData.font);
1161 main (int argc, char **argv)
1163 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1164 XSetWindowAttributes window_attributes;
1166 Dimension boardWidth, boardHeight, w, h;
1168 int forceMono = False;
1170 srandom(time(0)); // [HGM] book: make random truly random
1172 setbuf(stdout, NULL);
1173 setbuf(stderr, NULL);
1176 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1177 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1181 programName = strrchr(argv[0], '/');
1182 if (programName == NULL)
1183 programName = argv[0];
1188 XtSetLanguageProc(NULL, NULL, NULL);
1189 if (appData.debugMode) {
1190 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1193 bindtextdomain(PACKAGE, LOCALEDIR);
1194 textdomain(PACKAGE);
1197 appData.boardSize = "";
1198 InitAppData(ConvertToLine(argc, argv));
1200 if (p == NULL) p = "/tmp";
1201 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1202 gameCopyFilename = (char*) malloc(i);
1203 gamePasteFilename = (char*) malloc(i);
1204 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1205 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1207 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1208 static char buf[MSG_SIZ];
1209 EscapeExpand(buf, appData.firstInitString);
1210 appData.firstInitString = strdup(buf);
1211 EscapeExpand(buf, appData.secondInitString);
1212 appData.secondInitString = strdup(buf);
1213 EscapeExpand(buf, appData.firstComputerString);
1214 appData.firstComputerString = strdup(buf);
1215 EscapeExpand(buf, appData.secondComputerString);
1216 appData.secondComputerString = strdup(buf);
1219 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1222 if (chdir(chessDir) != 0) {
1223 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1229 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1230 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1231 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1232 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1235 setbuf(debugFP, NULL);
1238 /* [HGM,HR] make sure board size is acceptable */
1239 if(appData.NrFiles > BOARD_FILES ||
1240 appData.NrRanks > BOARD_RANKS )
1241 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1244 /* This feature does not work; animation needs a rewrite */
1245 appData.highlightDragging = FALSE;
1249 gameInfo.variant = StringToVariant(appData.variant);
1250 InitPosition(FALSE);
1253 XtAppInitialize(&appContext, "XBoard", shellOptions,
1254 XtNumber(shellOptions),
1255 &argc, argv, xboardResources, NULL, 0);
1257 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1258 clientResources, XtNumber(clientResources),
1261 xDisplay = XtDisplay(shellWidget);
1262 xScreen = DefaultScreen(xDisplay);
1263 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1266 * determine size, based on supplied or remembered -size, or screen size
1268 if (isdigit(appData.boardSize[0])) {
1269 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1270 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1271 &fontPxlSize, &smallLayout, &tinyLayout);
1273 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1274 programName, appData.boardSize);
1278 /* Find some defaults; use the nearest known size */
1279 SizeDefaults *szd, *nearest;
1280 int distance = 99999;
1281 nearest = szd = sizeDefaults;
1282 while (szd->name != NULL) {
1283 if (abs(szd->squareSize - squareSize) < distance) {
1285 distance = abs(szd->squareSize - squareSize);
1286 if (distance == 0) break;
1290 if (i < 2) lineGap = nearest->lineGap;
1291 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1292 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1293 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1294 if (i < 6) smallLayout = nearest->smallLayout;
1295 if (i < 7) tinyLayout = nearest->tinyLayout;
1298 SizeDefaults *szd = sizeDefaults;
1299 if (*appData.boardSize == NULLCHAR) {
1300 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1301 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1304 if (szd->name == NULL) szd--;
1305 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1307 while (szd->name != NULL &&
1308 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1309 if (szd->name == NULL) {
1310 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1311 programName, appData.boardSize);
1315 squareSize = szd->squareSize;
1316 lineGap = szd->lineGap;
1317 clockFontPxlSize = szd->clockFontPxlSize;
1318 coordFontPxlSize = szd->coordFontPxlSize;
1319 fontPxlSize = szd->fontPxlSize;
1320 smallLayout = szd->smallLayout;
1321 tinyLayout = szd->tinyLayout;
1322 // [HGM] font: use defaults from settings file if available and not overruled
1325 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1326 if (strlen(appData.pixmapDirectory) > 0) {
1327 p = ExpandPathName(appData.pixmapDirectory);
1329 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1330 appData.pixmapDirectory);
1333 if (appData.debugMode) {
1334 fprintf(stderr, _("\
1335 XBoard square size (hint): %d\n\
1336 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1338 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1339 if (appData.debugMode) {
1340 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1343 defaultLineGap = lineGap;
1344 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1346 /* [HR] height treated separately (hacked) */
1347 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1348 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1351 * Determine what fonts to use.
1353 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1356 * Detect if there are not enough colors available and adapt.
1358 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1359 appData.monoMode = True;
1362 forceMono = MakeColors();
1365 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1367 appData.monoMode = True;
1370 if (appData.monoMode && appData.debugMode) {
1371 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1372 (unsigned long) XWhitePixel(xDisplay, xScreen),
1373 (unsigned long) XBlackPixel(xDisplay, xScreen));
1376 ParseIcsTextColors();
1378 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1384 layoutName = "tinyLayout";
1385 } else if (smallLayout) {
1386 layoutName = "smallLayout";
1388 layoutName = "normalLayout";
1391 optList = BoardPopUp(squareSize, lineGap, (void*)
1397 boardWidget = optList[W_BOARD].handle;
1398 menuBarWidget = optList[W_MENU].handle;
1399 dropMenu = optList[W_DROP].handle;
1400 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1401 formWidget = XtParent(boardWidget);
1402 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1403 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1404 XtGetValues(optList[W_WHITE].handle, args, 2);
1405 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1406 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1407 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1408 XtGetValues(optList[W_PAUSE].handle, args, 2);
1410 AppendEnginesToMenu(appData.recentEngineList);
1412 xBoardWindow = XtWindow(boardWidget);
1414 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1415 // not need to go into InitDrawingSizes().
1418 * Create X checkmark bitmap and initialize option menu checks.
1420 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1421 checkmark_bits, checkmark_width, checkmark_height);
1427 ReadBitmap(&wIconPixmap, "icon_white.bm",
1428 icon_white_bits, icon_white_width, icon_white_height);
1429 ReadBitmap(&bIconPixmap, "icon_black.bm",
1430 icon_black_bits, icon_black_width, icon_black_height);
1431 iconPixmap = wIconPixmap;
1433 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1434 XtSetValues(shellWidget, args, i);
1437 * Create a cursor for the board widget.
1439 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1440 XChangeWindowAttributes(xDisplay, xBoardWindow,
1441 CWCursor, &window_attributes);
1444 * Inhibit shell resizing.
1446 shellArgs[0].value = (XtArgVal) &w;
1447 shellArgs[1].value = (XtArgVal) &h;
1448 XtGetValues(shellWidget, shellArgs, 2);
1449 shellArgs[4].value = shellArgs[2].value = w;
1450 shellArgs[5].value = shellArgs[3].value = h;
1451 XtSetValues(shellWidget, &shellArgs[2], 4);
1452 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1453 marginH = h - boardHeight;
1455 CatchDeleteWindow(shellWidget, "QuitProc");
1461 if(appData.logoSize)
1462 { // locate and read user logo
1464 snprintf(buf, MSG_SIZ, "%s/%s.xpm", appData.logoDir, UserName());
1465 XpmReadFileToPixmap(xDisplay, xBoardWindow, buf, (Pixmap *) &userLogo, NULL, NULL);
1468 if (appData.animate || appData.animateDragging)
1471 XtAugmentTranslations(formWidget,
1472 XtParseTranslationTable(globalTranslations));
1474 XtAddEventHandler(formWidget, KeyPressMask, False,
1475 (XtEventHandler) MoveTypeInProc, NULL);
1476 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1477 (XtEventHandler) EventProc, NULL);
1479 /* [AS] Restore layout */
1480 if( wpMoveHistory.visible ) {
1484 if( wpEvalGraph.visible )
1489 if( wpEngineOutput.visible ) {
1490 EngineOutputPopUp();
1495 if (errorExitStatus == -1) {
1496 if (appData.icsActive) {
1497 /* We now wait until we see "login:" from the ICS before
1498 sending the logon script (problems with timestamp otherwise) */
1499 /*ICSInitScript();*/
1500 if (appData.icsInputBox) ICSInputBoxPopUp();
1504 signal(SIGWINCH, TermSizeSigHandler);
1506 signal(SIGINT, IntSigHandler);
1507 signal(SIGTERM, IntSigHandler);
1508 if (*appData.cmailGameName != NULLCHAR) {
1509 signal(SIGUSR1, CmailSigHandler);
1513 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1516 // XtSetKeyboardFocus(shellWidget, formWidget);
1517 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1519 XtAppMainLoop(appContext);
1520 if (appData.debugMode) fclose(debugFP); // [DM] debug
1525 TermSizeSigHandler (int sig)
1531 IntSigHandler (int sig)
1537 CmailSigHandler (int sig)
1542 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1544 /* Activate call-back function CmailSigHandlerCallBack() */
1545 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1547 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1551 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1554 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1556 /**** end signal code ****/
1559 #define Abs(n) ((n)<0 ? -(n) : (n))
1563 InsertPxlSize (char *pattern, int targetPxlSize)
1565 char *base_fnt_lst, strInt[12], *p, *q;
1566 int alternatives, i, len, strIntLen;
1569 * Replace the "*" (if present) in the pixel-size slot of each
1570 * alternative with the targetPxlSize.
1574 while ((p = strchr(p, ',')) != NULL) {
1578 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1579 strIntLen = strlen(strInt);
1580 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1584 while (alternatives--) {
1585 char *comma = strchr(p, ',');
1586 for (i=0; i<14; i++) {
1587 char *hyphen = strchr(p, '-');
1589 if (comma && hyphen > comma) break;
1590 len = hyphen + 1 - p;
1591 if (i == 7 && *p == '*' && len == 2) {
1593 memcpy(q, strInt, strIntLen);
1603 len = comma + 1 - p;
1610 return base_fnt_lst;
1614 CreateFontSet (char *base_fnt_lst)
1617 char **missing_list;
1621 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1622 &missing_list, &missing_count, &def_string);
1623 if (appData.debugMode) {
1625 XFontStruct **font_struct_list;
1626 char **font_name_list;
1627 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1629 fprintf(debugFP, " got list %s, locale %s\n",
1630 XBaseFontNameListOfFontSet(fntSet),
1631 XLocaleOfFontSet(fntSet));
1632 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1633 for (i = 0; i < count; i++) {
1634 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1637 for (i = 0; i < missing_count; i++) {
1638 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1641 if (fntSet == NULL) {
1642 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1647 #else // not ENABLE_NLS
1649 * Find a font that matches "pattern" that is as close as
1650 * possible to the targetPxlSize. Prefer fonts that are k
1651 * pixels smaller to fonts that are k pixels larger. The
1652 * pattern must be in the X Consortium standard format,
1653 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1654 * The return value should be freed with XtFree when no
1658 FindFont (char *pattern, int targetPxlSize)
1660 char **fonts, *p, *best, *scalable, *scalableTail;
1661 int i, j, nfonts, minerr, err, pxlSize;
1663 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1665 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1666 programName, pattern);
1673 for (i=0; i<nfonts; i++) {
1676 if (*p != '-') continue;
1678 if (*p == NULLCHAR) break;
1679 if (*p++ == '-') j++;
1681 if (j < 7) continue;
1684 scalable = fonts[i];
1687 err = pxlSize - targetPxlSize;
1688 if (Abs(err) < Abs(minerr) ||
1689 (minerr > 0 && err < 0 && -err == minerr)) {
1695 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1696 /* If the error is too big and there is a scalable font,
1697 use the scalable font. */
1698 int headlen = scalableTail - scalable;
1699 p = (char *) XtMalloc(strlen(scalable) + 10);
1700 while (isdigit(*scalableTail)) scalableTail++;
1701 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1703 p = (char *) XtMalloc(strlen(best) + 2);
1704 safeStrCpy(p, best, strlen(best)+1 );
1706 if (appData.debugMode) {
1707 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1708 pattern, targetPxlSize, p);
1710 XFreeFontNames(fonts);
1717 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
1718 // must be called before all non-first callse to CreateGCs()
1719 XtReleaseGC(shellWidget, highlineGC);
1720 XtReleaseGC(shellWidget, lightSquareGC);
1721 XtReleaseGC(shellWidget, darkSquareGC);
1722 XtReleaseGC(shellWidget, lineGC);
1723 if (appData.monoMode) {
1724 if (DefaultDepth(xDisplay, xScreen) == 1) {
1725 XtReleaseGC(shellWidget, wbPieceGC);
1727 XtReleaseGC(shellWidget, bwPieceGC);
1730 XtReleaseGC(shellWidget, prelineGC);
1731 XtReleaseGC(shellWidget, wdPieceGC);
1732 XtReleaseGC(shellWidget, wlPieceGC);
1733 XtReleaseGC(shellWidget, bdPieceGC);
1734 XtReleaseGC(shellWidget, blPieceGC);
1739 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
1741 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1742 | GCBackground | GCFunction | GCPlaneMask;
1743 gc_values->foreground = foreground;
1744 gc_values->background = background;
1745 return XtGetGC(shellWidget, value_mask, gc_values);
1749 CreateGCs (int redo)
1751 XGCValues gc_values;
1753 Pixel white = XWhitePixel(xDisplay, xScreen);
1754 Pixel black = XBlackPixel(xDisplay, xScreen);
1756 gc_values.plane_mask = AllPlanes;
1757 gc_values.line_width = lineGap;
1758 gc_values.line_style = LineSolid;
1759 gc_values.function = GXcopy;
1762 DeleteGCs(); // called a second time; clean up old GCs first
1763 } else { // [HGM] grid and font GCs created on first call only
1764 coordGC = CreateOneGC(&gc_values, black, white);
1765 XSetFont(xDisplay, coordGC, coordFontID);
1767 // [HGM] make font for holdings counts (white on black)
1768 countGC = CreateOneGC(&gc_values, white, black);
1769 XSetFont(xDisplay, countGC, countFontID);
1771 lineGC = CreateOneGC(&gc_values, black, black);
1773 if (appData.monoMode) {
1775 highlineGC = CreateOneGC(&gc_values, white, white);
1776 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
1777 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
1779 if (DefaultDepth(xDisplay, xScreen) == 1) {
1780 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
1781 gc_values.function = GXcopyInverted;
1782 copyInvertedGC = CreateOneGC(&gc_values, black, white);
1783 gc_values.function = GXcopy;
1784 if (XBlackPixel(xDisplay, xScreen) == 1) {
1785 bwPieceGC = darkSquareGC;
1786 wbPieceGC = copyInvertedGC;
1788 bwPieceGC = copyInvertedGC;
1789 wbPieceGC = lightSquareGC;
1794 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
1795 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
1796 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
1797 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
1798 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
1799 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
1800 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
1801 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
1806 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
1814 fp = fopen(filename, "rb");
1816 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
1823 for (y=0; y<h; ++y) {
1824 for (x=0; x<h; ++x) {
1829 XPutPixel(xim, x, y, blackPieceColor);
1831 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1834 XPutPixel(xim, x, y, darkSquareColor);
1836 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1839 XPutPixel(xim, x, y, whitePieceColor);
1841 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1844 XPutPixel(xim, x, y, lightSquareColor);
1846 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1854 /* create Pixmap of piece */
1855 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1857 XPutImage(xDisplay, *dest, lightSquareGC, xim,
1860 /* create Pixmap of clipmask
1861 Note: We assume the white/black pieces have the same
1862 outline, so we make only 6 masks. This is okay
1863 since the XPM clipmask routines do the same. */
1865 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1867 XPutImage(xDisplay, temp, lightSquareGC, xmask,
1870 /* now create the 1-bit version */
1871 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1874 values.foreground = 1;
1875 values.background = 0;
1877 /* Don't use XtGetGC, not read only */
1878 maskGC = XCreateGC(xDisplay, *mask,
1879 GCForeground | GCBackground, &values);
1880 XCopyPlane(xDisplay, temp, *mask, maskGC,
1881 0, 0, squareSize, squareSize, 0, 0, 1);
1882 XFreePixmap(xDisplay, temp);
1887 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
1895 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
1900 /* The XSynchronize calls were copied from CreatePieces.
1901 Not sure if needed, but can't hurt */
1902 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
1905 /* temp needed by loadXIM() */
1906 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1907 0, 0, ss, ss, AllPlanes, XYPixmap);
1909 if (strlen(appData.pixmapDirectory) == 0) {
1913 if (appData.monoMode) {
1914 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
1918 fprintf(stderr, _("\nLoading XIMs...\n"));
1920 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
1921 fprintf(stderr, "%d", piece+1);
1922 for (kind=0; kind<4; kind++) {
1923 fprintf(stderr, ".");
1924 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
1925 ExpandPathName(appData.pixmapDirectory),
1926 piece <= (int) WhiteKing ? "" : "w",
1927 pieceBitmapNames[piece],
1929 ximPieceBitmap[kind][piece] =
1930 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1931 0, 0, ss, ss, AllPlanes, XYPixmap);
1932 if (appData.debugMode)
1933 fprintf(stderr, _("(File:%s:) "), buf);
1934 loadXIM(ximPieceBitmap[kind][piece],
1936 &(xpmPieceBitmap2[kind][piece]),
1937 &(ximMaskPm2[piece]));
1938 if(piece <= (int)WhiteKing)
1939 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
1941 fprintf(stderr," ");
1943 /* Load light and dark squares */
1944 /* If the LSQ and DSQ pieces don't exist, we will
1945 draw them with solid squares. */
1946 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
1947 if (access(buf, 0) != 0) {
1951 fprintf(stderr, _("light square "));
1953 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1954 0, 0, ss, ss, AllPlanes, XYPixmap);
1955 if (appData.debugMode)
1956 fprintf(stderr, _("(File:%s:) "), buf);
1958 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
1959 fprintf(stderr, _("dark square "));
1960 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
1961 ExpandPathName(appData.pixmapDirectory), ss);
1962 if (appData.debugMode)
1963 fprintf(stderr, _("(File:%s:) "), buf);
1965 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1966 0, 0, ss, ss, AllPlanes, XYPixmap);
1967 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
1968 xpmJailSquare = xpmLightSquare;
1970 fprintf(stderr, _("Done.\n"));
1972 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
1975 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
1979 CreateXPMBoard (char *s, int kind)
1983 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
1984 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
1985 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
1991 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
1992 // thisroutine has to be called t free the old piece pixmaps
1994 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
1995 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
1997 XFreePixmap(xDisplay, xpmLightSquare);
1998 XFreePixmap(xDisplay, xpmDarkSquare);
2007 u_int ss = squareSize;
2009 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2010 XpmColorSymbol symbols[4];
2011 static int redo = False;
2013 if(redo) FreeXPMPieces(); else redo = 1;
2015 /* The XSynchronize calls were copied from CreatePieces.
2016 Not sure if needed, but can't hurt */
2017 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2019 /* Setup translations so piece colors match square colors */
2020 symbols[0].name = "light_piece";
2021 symbols[0].value = appData.whitePieceColor;
2022 symbols[1].name = "dark_piece";
2023 symbols[1].value = appData.blackPieceColor;
2024 symbols[2].name = "light_square";
2025 symbols[2].value = appData.lightSquareColor;
2026 symbols[3].name = "dark_square";
2027 symbols[3].value = appData.darkSquareColor;
2029 attr.valuemask = XpmColorSymbols;
2030 attr.colorsymbols = symbols;
2031 attr.numsymbols = 4;
2033 if (appData.monoMode) {
2034 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2038 if (strlen(appData.pixmapDirectory) == 0) {
2039 XpmPieces* pieces = builtInXpms;
2042 while (pieces->size != squareSize && pieces->size) pieces++;
2043 if (!pieces->size) {
2044 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2047 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2048 for (kind=0; kind<4; kind++) {
2050 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2051 pieces->xpm[piece][kind],
2052 &(xpmPieceBitmap2[kind][piece]),
2053 NULL, &attr)) != 0) {
2054 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2058 if(piece <= (int) WhiteKing)
2059 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2063 xpmJailSquare = xpmLightSquare;
2067 fprintf(stderr, _("\nLoading XPMs...\n"));
2070 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2071 fprintf(stderr, "%d ", piece+1);
2072 for (kind=0; kind<4; kind++) {
2073 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2074 ExpandPathName(appData.pixmapDirectory),
2075 piece > (int) WhiteKing ? "w" : "",
2076 pieceBitmapNames[piece],
2078 if (appData.debugMode) {
2079 fprintf(stderr, _("(File:%s:) "), buf);
2081 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2082 &(xpmPieceBitmap2[kind][piece]),
2083 NULL, &attr)) != 0) {
2084 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2085 // [HGM] missing: read of unorthodox piece failed; substitute King.
2086 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2087 ExpandPathName(appData.pixmapDirectory),
2089 if (appData.debugMode) {
2090 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2092 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2093 &(xpmPieceBitmap2[kind][piece]),
2097 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2102 if(piece <= (int) WhiteKing)
2103 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2106 /* Load light and dark squares */
2107 /* If the LSQ and DSQ pieces don't exist, we will
2108 draw them with solid squares. */
2109 fprintf(stderr, _("light square "));
2110 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2111 if (access(buf, 0) != 0) {
2115 if (appData.debugMode)
2116 fprintf(stderr, _("(File:%s:) "), buf);
2118 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2119 &xpmLightSquare, NULL, &attr)) != 0) {
2120 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2123 fprintf(stderr, _("dark square "));
2124 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2125 ExpandPathName(appData.pixmapDirectory), ss);
2126 if (appData.debugMode) {
2127 fprintf(stderr, _("(File:%s:) "), buf);
2129 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2130 &xpmDarkSquare, NULL, &attr)) != 0) {
2131 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2135 xpmJailSquare = xpmLightSquare;
2136 fprintf(stderr, _("Done.\n"));
2138 oldVariant = -1; // kludge to force re-makig of animation masks
2139 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2142 #endif /* HAVE_LIBXPM */
2145 /* No built-in bitmaps */
2150 u_int ss = squareSize;
2152 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2155 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2156 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2157 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2158 pieceBitmapNames[piece],
2159 ss, kind == SOLID ? 's' : 'o');
2160 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2161 if(piece <= (int)WhiteKing)
2162 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2166 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2170 /* With built-in bitmaps */
2174 BuiltInBits* bib = builtInBits;
2177 u_int ss = squareSize;
2179 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2182 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2184 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2185 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2186 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2187 pieceBitmapNames[piece],
2188 ss, kind == SOLID ? 's' : 'o');
2189 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2190 bib->bits[kind][piece], ss, ss);
2191 if(piece <= (int)WhiteKing)
2192 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2196 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2202 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2207 char msg[MSG_SIZ], fullname[MSG_SIZ];
2209 if (*appData.bitmapDirectory != NULLCHAR) {
2210 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2211 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2212 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2213 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2214 &w, &h, pm, &x_hot, &y_hot);
2215 fprintf(stderr, "load %s\n", name);
2216 if (errcode != BitmapSuccess) {
2218 case BitmapOpenFailed:
2219 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2221 case BitmapFileInvalid:
2222 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2224 case BitmapNoMemory:
2225 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2229 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2233 fprintf(stderr, _("%s: %s...using built-in\n"),
2235 } else if (w != wreq || h != hreq) {
2237 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2238 programName, fullname, w, h, wreq, hreq);
2244 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2254 if (lineGap == 0) return;
2256 /* [HR] Split this into 2 loops for non-square boards. */
2258 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2259 gridSegments[i].x1 = 0;
2260 gridSegments[i].x2 =
2261 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2262 gridSegments[i].y1 = gridSegments[i].y2
2263 = lineGap / 2 + (i * (squareSize + lineGap));
2266 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2267 gridSegments[j + i].y1 = 0;
2268 gridSegments[j + i].y2 =
2269 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2270 gridSegments[j + i].x1 = gridSegments[j + i].x2
2271 = lineGap / 2 + (j * (squareSize + lineGap));
2276 MarkMenuItem (char *menuRef, int state)
2278 MenuItem *item = MenuNameToItem(menuRef);
2282 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2283 XtSetValues(item->handle, args, 1);
2288 EnableNamedMenuItem (char *menuRef, int state)
2290 MenuItem *item = MenuNameToItem(menuRef);
2292 if(item) XtSetSensitive(item->handle, state);
2296 EnableButtonBar (int state)
2298 XtSetSensitive(optList[W_BUTTON].handle, state);
2303 SetMenuEnables (Enables *enab)
2305 while (enab->name != NULL) {
2306 EnableNamedMenuItem(enab->name, enab->value);
2312 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2313 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2315 if(*nprms == 0) return;
2316 item = MenuNameToItem(prms[0]);
2317 if(item) ((MenuProc *) item->proc) ();
2321 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2323 RecentEngineEvent((int) (intptr_t) addr);
2327 AppendMenuItem (char *msg, int n)
2329 CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
2341 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2342 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2343 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2344 dmEnables[i].piece);
2345 XtSetSensitive(entry, p != NULL || !appData.testLegality
2346 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2347 && !appData.icsActive));
2349 while (p && *p++ == dmEnables[i].piece) count++;
2350 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2352 XtSetArg(args[j], XtNlabel, label); j++;
2353 XtSetValues(entry, args, j);
2359 do_flash_delay (unsigned long msec)
2365 DrawBorder (int x, int y, int type)
2369 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
2371 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
2372 squareSize+lineGap, squareSize+lineGap);
2376 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
2378 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2379 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2381 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2382 if(textureW[kind] < W*squareSize)
2383 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2385 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2386 if(textureH[kind] < H*squareSize)
2387 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2389 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2394 DrawLogo (void *handle, void *logo)
2396 if(!logo || !handle) return;
2397 XCopyArea(xDisplay, (Pixmap) logo, XtWindow((Widget) handle), wlPieceGC,
2398 0, 0, appData.logoSize, appData.logoSize/2, 0, 0);
2402 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2403 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2405 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2406 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2407 squareSize, squareSize, x*fac, y*fac);
2409 if (useImages && useImageSqs) {
2413 pm = xpmLightSquare;
2418 case 2: /* neutral */
2420 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2423 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2424 squareSize, squareSize, x*fac, y*fac);
2434 case 2: /* neutral */
2439 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2444 I split out the routines to draw a piece so that I could
2445 make a generic flash routine.
2448 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2450 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2451 switch (square_color) {
2453 case 2: /* neutral */
2455 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2456 ? *pieceToOutline(piece)
2457 : *pieceToSolid(piece),
2458 dest, bwPieceGC, 0, 0,
2459 squareSize, squareSize, x, y);
2462 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2463 ? *pieceToSolid(piece)
2464 : *pieceToOutline(piece),
2465 dest, wbPieceGC, 0, 0,
2466 squareSize, squareSize, x, y);
2472 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2474 switch (square_color) {
2476 case 2: /* neutral */
2478 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2479 ? *pieceToOutline(piece)
2480 : *pieceToSolid(piece),
2481 dest, bwPieceGC, 0, 0,
2482 squareSize, squareSize, x, y, 1);
2485 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2486 ? *pieceToSolid(piece)
2487 : *pieceToOutline(piece),
2488 dest, wbPieceGC, 0, 0,
2489 squareSize, squareSize, x, y, 1);
2495 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2497 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2498 switch (square_color) {
2500 XCopyPlane(xDisplay, *pieceToSolid(piece),
2501 dest, (int) piece < (int) BlackPawn
2502 ? wlPieceGC : blPieceGC, 0, 0,
2503 squareSize, squareSize, x, y, 1);
2506 XCopyPlane(xDisplay, *pieceToSolid(piece),
2507 dest, (int) piece < (int) BlackPawn
2508 ? wdPieceGC : bdPieceGC, 0, 0,
2509 squareSize, squareSize, x, y, 1);
2511 case 2: /* neutral */
2513 break; // should never contain pieces
2518 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2520 int kind, p = piece;
2522 switch (square_color) {
2524 case 2: /* neutral */
2526 if ((int)piece < (int) BlackPawn) {
2534 if ((int)piece < (int) BlackPawn) {
2542 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2543 if(useTexture & square_color+1) {
2544 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2545 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2546 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2547 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2548 XSetClipMask(xDisplay, wlPieceGC, None);
2549 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2551 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2552 dest, wlPieceGC, 0, 0,
2553 squareSize, squareSize, x, y);
2556 typedef void (*DrawFunc)();
2561 if (appData.monoMode) {
2562 if (DefaultDepth(xDisplay, xScreen) == 1) {
2563 return monoDrawPiece_1bit;
2565 return monoDrawPiece;
2569 return colorDrawPieceImage;
2571 return colorDrawPiece;
2576 DrawDot (int marker, int x, int y, int r)
2578 if(appData.monoMode) {
2579 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
2580 x, y, r, r, 0, 64*360);
2581 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
2582 x, y, r, r, 0, 64*360);
2584 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
2585 x, y, r, r, 0, 64*360);
2589 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2590 { // basic front-end board-draw function: takes care of everything that can be in square:
2591 // piece, background, coordinate/count, marker dot
2592 int direction, font_ascent, font_descent;
2593 XCharStruct overall;
2596 if (piece == EmptySquare) {
2597 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2599 drawfunc = ChooseDrawFunc();
2600 drawfunc(piece, square_color, x, y, xBoardWindow);
2603 if(align) { // square carries inscription (coord or piece count)
2605 GC hGC = align < 3 ? coordGC : countGC;
2606 // first calculate where it goes
2607 XTextExtents(countFontStruct, string, 1, &direction,
2608 &font_ascent, &font_descent, &overall);
2610 xx += squareSize - overall.width - 2;
2611 yy += squareSize - font_descent - 1;
2612 } else if (align == 2) {
2613 xx += 2, yy += font_ascent + 1;
2614 } else if (align == 3) {
2615 xx += squareSize - overall.width - 2;
2616 yy += font_ascent + 1;
2617 } else if (align == 4) {
2618 xx += 2, yy += font_ascent + 1;
2621 if (appData.monoMode) {
2622 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2624 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2628 if(marker) { // print fat marker dot, if requested
2629 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2634 FlashDelay (int flash_delay)
2636 XSync(xDisplay, False);
2637 if(flash_delay) do_flash_delay(flash_delay);
2641 Fraction (int x, int start, int stop)
2643 double f = ((double) x - start)/(stop - start);
2644 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2648 static WindowPlacement wpNew;
2651 CoDrag (Widget sh, WindowPlacement *wp)
2654 int j=0, touch=0, fudge = 2;
2655 GetActualPlacement(sh, wp);
2656 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
2657 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
2658 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2659 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
2660 if(!touch ) return; // only windows that touch co-move
2661 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2662 int heightInc = wpNew.height - wpMain.height;
2663 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2664 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2665 wp->y += fracTop * heightInc;
2666 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2667 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2668 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2669 int widthInc = wpNew.width - wpMain.width;
2670 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2671 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2672 wp->y += fracLeft * widthInc;
2673 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2674 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2676 wp->x += wpNew.x - wpMain.x;
2677 wp->y += wpNew.y - wpMain.y;
2678 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2679 if(touch == 3) wp->y += wpNew.height - wpMain.height;
2680 XtSetArg(args[j], XtNx, wp->x); j++;
2681 XtSetArg(args[j], XtNy, wp->y); j++;
2682 XtSetValues(sh, args, j);
2685 static XtIntervalId delayedDragID = 0;
2690 GetActualPlacement(shellWidget, &wpNew);
2691 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
2692 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
2693 return; // false alarm
2694 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
2695 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
2696 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
2697 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
2699 DrawPosition(True, NULL);
2700 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
2707 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
2709 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
2713 EventProc (Widget widget, caddr_t unused, XEvent *event)
2715 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
2716 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
2719 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
2721 DrawSeekAxis (int x, int y, int xTo, int yTo)
2723 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
2727 DrawSeekBackground (int left, int top, int right, int bottom)
2729 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
2733 DrawSeekText (char *buf, int x, int y)
2735 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
2739 DrawSeekDot (int x, int y, int colorNr)
2741 int square = colorNr & 0x80;
2744 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
2746 XFillRectangle(xDisplay, xBoardWindow, color,
2747 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
2749 XFillArc(xDisplay, xBoardWindow, color,
2750 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
2756 XDrawSegments(xDisplay, xBoardWindow, lineGC,
2757 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
2762 * event handler for redrawing the board
2765 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2767 DrawPosition(True, NULL);
2772 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
2773 { // [HGM] pv: walk PV
2774 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
2777 static int savedIndex; /* gross that this is global */
2780 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
2783 XawTextPosition index, dummy;
2786 XawTextGetSelectionPos(w, &index, &dummy);
2787 XtSetArg(arg, XtNstring, &val);
2788 XtGetValues(w, &arg, 1);
2789 ReplaceComment(savedIndex, val);
2790 if(savedIndex != currentMove) ToNrEvent(savedIndex);
2791 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
2795 EditCommentPopUp (int index, char *title, char *text)
2798 if (text == NULL) text = "";
2799 NewCommentPopup(title, text, index);
2803 CommentPopUp (char *title, char *text)
2805 savedIndex = currentMove; // [HGM] vari
2806 NewCommentPopup(title, text, currentMove);
2812 PopDown(CommentDlg);
2816 /* Disable all user input other than deleting the window */
2817 static int frozen = 0;
2823 /* Grab by a widget that doesn't accept input */
2824 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
2828 /* Undo a FreezeUI */
2832 if (!frozen) return;
2833 XtRemoveGrab(optList[W_MESSG].handle);
2841 static int oldPausing = FALSE;
2842 static GameMode oldmode = (GameMode) -1;
2845 if (!boardWidget || !XtIsRealized(boardWidget)) return;
2847 if (pausing != oldPausing) {
2848 oldPausing = pausing;
2849 MarkMenuItem("Mode.Pause", pausing);
2851 if (appData.showButtonBar) {
2852 /* Always toggle, don't set. Previous code messes up when
2853 invoked while the button is pressed, as releasing it
2854 toggles the state again. */
2857 XtSetArg(args[0], XtNbackground, &oldbg);
2858 XtSetArg(args[1], XtNforeground, &oldfg);
2859 XtGetValues(optList[W_PAUSE].handle,
2861 XtSetArg(args[0], XtNbackground, oldfg);
2862 XtSetArg(args[1], XtNforeground, oldbg);
2864 XtSetValues(optList[W_PAUSE].handle, args, 2);
2868 wname = ModeToWidgetName(oldmode);
2869 if (wname != NULL) {
2870 MarkMenuItem(wname, False);
2872 wname = ModeToWidgetName(gameMode);
2873 if (wname != NULL) {
2874 MarkMenuItem(wname, True);
2877 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
2879 /* Maybe all the enables should be handled here, not just this one */
2880 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
2882 DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);
2887 * Button/menu procedures
2890 /* this variable is shared between CopyPositionProc and SendPositionSelection */
2891 char *selected_fen_position=NULL;
2894 SendPositionSelection (Widget w, Atom *selection, Atom *target,
2895 Atom *type_return, XtPointer *value_return,
2896 unsigned long *length_return, int *format_return)
2898 char *selection_tmp;
2900 // if (!selected_fen_position) return False; /* should never happen */
2901 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
2902 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
2903 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
2906 if (f == NULL) return False;
2910 selection_tmp = XtMalloc(len + 1);
2911 count = fread(selection_tmp, 1, len, f);
2914 XtFree(selection_tmp);
2917 selection_tmp[len] = NULLCHAR;
2919 /* note: since no XtSelectionDoneProc was registered, Xt will
2920 * automatically call XtFree on the value returned. So have to
2921 * make a copy of it allocated with XtMalloc */
2922 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
2923 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
2926 *value_return=selection_tmp;
2927 *length_return=strlen(selection_tmp);
2928 *type_return=*target;
2929 *format_return = 8; /* bits per byte */
2931 } else if (*target == XA_TARGETS(xDisplay)) {
2932 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
2933 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
2934 targets_tmp[1] = XA_STRING;
2935 *value_return = targets_tmp;
2936 *type_return = XA_ATOM;
2939 // This code leads to a read of value_return out of bounds on 64-bit systems.
2940 // Other code which I have seen always sets *format_return to 32 independent of
2941 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
2942 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
2943 *format_return = 8 * sizeof(Atom);
2944 if (*format_return > 32) {
2945 *length_return *= *format_return / 32;
2946 *format_return = 32;
2949 *format_return = 32;
2957 /* note: when called from menu all parameters are NULL, so no clue what the
2958 * Widget which was clicked on was, or what the click event was
2961 CopySomething (char *src)
2963 selected_fen_position = src;
2965 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
2966 * have a notion of a position that is selected but not copied.
2967 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
2969 XtOwnSelection(menuBarWidget, XA_PRIMARY,
2971 SendPositionSelection,
2972 NULL/* lose_ownership_proc */ ,
2973 NULL/* transfer_done_proc */);
2974 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
2976 SendPositionSelection,
2977 NULL/* lose_ownership_proc */ ,
2978 NULL/* transfer_done_proc */);
2981 /* function called when the data to Paste is ready */
2983 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
2984 Atom *type, XtPointer value, unsigned long *len, int *format)
2987 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
2988 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
2989 EditPositionPasteFEN(fenstr);
2993 /* called when Paste Position button is pressed,
2994 * all parameters will be NULL */
2996 PastePositionProc ()
2998 XtGetSelectionValue(menuBarWidget,
2999 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3000 /* (XtSelectionCallbackProc) */ PastePositionCB,
3001 NULL, /* client_data passed to PastePositionCB */
3003 /* better to use the time field from the event that triggered the
3004 * call to this function, but that isn't trivial to get
3011 /* note: when called from menu all parameters are NULL, so no clue what the
3012 * Widget which was clicked on was, or what the click event was
3014 /* function called when the data to Paste is ready */
3016 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3017 Atom *type, XtPointer value, unsigned long *len, int *format)
3020 if (value == NULL || *len == 0) {
3021 return; /* nothing had been selected to copy */
3023 f = fopen(gamePasteFilename, "w");
3025 DisplayError(_("Can't open temp file"), errno);
3028 fwrite(value, 1, *len, f);
3031 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3034 /* called when Paste Game button is pressed,
3035 * all parameters will be NULL */
3039 XtGetSelectionValue(menuBarWidget,
3040 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3041 /* (XtSelectionCallbackProc) */ PasteGameCB,
3042 NULL, /* client_data passed to PasteGameCB */
3044 /* better to use the time field from the event that triggered the
3045 * call to this function, but that isn't trivial to get
3054 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3061 { // bassic primitive for determining if modifier keys are pressed
3062 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3065 XQueryKeymap(xDisplay,keys);
3066 for(i=0; i<6; i++) {
3068 j = XKeysymToKeycode(xDisplay, codes[i]);
3069 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3075 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3079 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3080 if ( n == 1 && *buf >= 32 // printable
3081 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3082 ) BoxAutoPopUp (buf);
3086 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3087 { // [HGM] input: let up-arrow recall previous line from history
3092 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3093 { // [HGM] input: let down-arrow recall next line from history
3098 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3104 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3106 if (!TempBackwardActive) {
3107 TempBackwardActive = True;
3113 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3115 /* Check to see if triggered by a key release event for a repeating key.
3116 * If so the next queued event will be a key press of the same key at the same time */
3117 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3119 XPeekEvent(xDisplay, &next);
3120 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3121 next.xkey.keycode == event->xkey.keycode)
3125 TempBackwardActive = False;
3129 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3130 { // called as key binding
3133 if (nprms && *nprms > 0)
3137 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3143 { // called from menu
3144 ManInner(NULL, NULL, NULL, NULL);
3148 SetWindowTitle (char *text, char *title, char *icon)
3152 if (appData.titleInWindow) {
3154 XtSetArg(args[i], XtNlabel, text); i++;
3155 XtSetValues(titleWidget, args, i);
3158 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3159 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3160 XtSetValues(shellWidget, args, i);
3161 XSync(xDisplay, False);
3166 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3172 DisplayIcsInteractionTitle (String message)
3174 if (oldICSInteractionTitle == NULL) {
3175 /* Magic to find the old window title, adapted from vim */
3176 char *wina = getenv("WINDOWID");
3178 Window win = (Window) atoi(wina);
3179 Window root, parent, *children;
3180 unsigned int nchildren;
3181 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3183 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3184 if (!XQueryTree(xDisplay, win, &root, &parent,
3185 &children, &nchildren)) break;
3186 if (children) XFree((void *)children);
3187 if (parent == root || parent == 0) break;
3190 XSetErrorHandler(oldHandler);
3192 if (oldICSInteractionTitle == NULL) {
3193 oldICSInteractionTitle = "xterm";
3196 printf("\033]0;%s\007", message);
3201 XtIntervalId delayedEventTimerXID = 0;
3202 DelayedEventCallback delayedEventCallback = 0;
3207 delayedEventTimerXID = 0;
3208 delayedEventCallback();
3212 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3214 if(delayedEventTimerXID && delayedEventCallback == cb)
3215 // [HGM] alive: replace, rather than add or flush identical event
3216 XtRemoveTimeOut(delayedEventTimerXID);
3217 delayedEventCallback = cb;
3218 delayedEventTimerXID =
3219 XtAppAddTimeOut(appContext, millisec,
3220 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3223 DelayedEventCallback
3226 if (delayedEventTimerXID) {
3227 return delayedEventCallback;
3234 CancelDelayedEvent ()
3236 if (delayedEventTimerXID) {
3237 XtRemoveTimeOut(delayedEventTimerXID);
3238 delayedEventTimerXID = 0;
3242 XtIntervalId loadGameTimerXID = 0;
3245 LoadGameTimerRunning ()
3247 return loadGameTimerXID != 0;
3251 StopLoadGameTimer ()
3253 if (loadGameTimerXID != 0) {
3254 XtRemoveTimeOut(loadGameTimerXID);
3255 loadGameTimerXID = 0;
3263 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3265 loadGameTimerXID = 0;
3270 StartLoadGameTimer (long millisec)
3273 XtAppAddTimeOut(appContext, millisec,
3274 (XtTimerCallbackProc) LoadGameTimerCallback,
3278 XtIntervalId analysisClockXID = 0;
3281 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3283 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3284 || appData.icsEngineAnalyze) { // [DM]
3285 AnalysisPeriodicEvent(0);
3286 StartAnalysisClock();
3291 StartAnalysisClock ()
3294 XtAppAddTimeOut(appContext, 2000,
3295 (XtTimerCallbackProc) AnalysisClockCallback,
3299 XtIntervalId clockTimerXID = 0;
3302 ClockTimerRunning ()
3304 return clockTimerXID != 0;
3310 if (clockTimerXID != 0) {
3311 XtRemoveTimeOut(clockTimerXID);
3320 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3327 StartClockTimer (long millisec)
3330 XtAppAddTimeOut(appContext, millisec,
3331 (XtTimerCallbackProc) ClockTimerCallback,
3336 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
3340 Widget w = (Widget) opt->handle;
3342 /* check for low time warning */
3343 Pixel foregroundOrWarningColor = timerForegroundPixel;
3346 appData.lowTimeWarning &&
3347 (timer / 1000) < appData.icsAlarmTime)
3348 foregroundOrWarningColor = lowTimeWarningColor;
3350 if (appData.clockMode) {
3351 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
3352 XtSetArg(args[0], XtNlabel, buf);
3354 snprintf(buf, MSG_SIZ, "%s ", color);
3355 XtSetArg(args[0], XtNlabel, buf);
3360 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3361 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3363 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3364 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3367 XtSetValues(w, args, 3);
3370 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3373 SetClockIcon (int color)
3376 Pixmap pm = *clockIcons[color];
3377 if (iconPixmap != pm) {
3379 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3380 XtSetValues(shellWidget, args, 1);
3385 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3387 InputSource *is = (InputSource *) closure;
3392 if (is->lineByLine) {
3393 count = read(is->fd, is->unused,
3394 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3396 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3399 is->unused += count;
3401 while (p < is->unused) {
3402 q = memchr(p, '\n', is->unused - p);
3403 if (q == NULL) break;
3405 (is->func)(is, is->closure, p, q - p, 0);
3409 while (p < is->unused) {
3414 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3419 (is->func)(is, is->closure, is->buf, count, error);
3424 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3427 ChildProc *cp = (ChildProc *) pr;
3429 is = (InputSource *) calloc(1, sizeof(InputSource));
3430 is->lineByLine = lineByLine;
3434 is->fd = fileno(stdin);
3436 is->kind = cp->kind;
3437 is->fd = cp->fdFrom;
3440 is->unused = is->buf;
3443 is->xid = XtAppAddInput(appContext, is->fd,
3444 (XtPointer) (XtInputReadMask),
3445 (XtInputCallbackProc) DoInputCallback,
3447 is->closure = closure;
3448 return (InputSourceRef) is;
3452 RemoveInputSource (InputSourceRef isr)
3454 InputSource *is = (InputSource *) isr;
3456 if (is->xid == 0) return;
3457 XtRemoveInput(is->xid);
3461 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
3463 /* Masks for XPM pieces. Black and white pieces can have
3464 different shapes, but in the interest of retaining my
3465 sanity pieces must have the same outline on both light
3466 and dark squares, and all pieces must use the same
3467 background square colors/images. */
3469 static int xpmDone = 0;
3470 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
3471 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
3474 CreateAnimMasks (int pieceDepth)
3480 unsigned long plane;
3483 /* Need a bitmap just to get a GC with right depth */
3484 buf = XCreatePixmap(xDisplay, xBoardWindow,
3486 values.foreground = 1;
3487 values.background = 0;
3488 /* Don't use XtGetGC, not read only */
3489 maskGC = XCreateGC(xDisplay, buf,
3490 GCForeground | GCBackground, &values);
3491 XFreePixmap(xDisplay, buf);
3493 buf = XCreatePixmap(xDisplay, xBoardWindow,
3494 squareSize, squareSize, pieceDepth);
3495 values.foreground = XBlackPixel(xDisplay, xScreen);
3496 values.background = XWhitePixel(xDisplay, xScreen);
3497 bufGC = XCreateGC(xDisplay, buf,
3498 GCForeground | GCBackground, &values);
3500 for (piece = WhitePawn; piece <= BlackKing; piece++) {
3501 /* Begin with empty mask */
3502 if(!xpmDone) // [HGM] pieces: keep using existing
3503 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
3504 squareSize, squareSize, 1);
3505 XSetFunction(xDisplay, maskGC, GXclear);
3506 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
3507 0, 0, squareSize, squareSize);
3509 /* Take a copy of the piece */
3514 XSetFunction(xDisplay, bufGC, GXcopy);
3515 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
3517 0, 0, squareSize, squareSize, 0, 0);
3519 /* XOR the background (light) over the piece */
3520 XSetFunction(xDisplay, bufGC, GXxor);
3522 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
3523 0, 0, squareSize, squareSize, 0, 0);
3525 XSetForeground(xDisplay, bufGC, lightSquareColor);
3526 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
3529 /* We now have an inverted piece image with the background
3530 erased. Construct mask by just selecting all the non-zero
3531 pixels - no need to reconstruct the original image. */
3532 XSetFunction(xDisplay, maskGC, GXor);
3534 /* Might be quicker to download an XImage and create bitmap
3535 data from it rather than this N copies per piece, but it
3536 only takes a fraction of a second and there is a much
3537 longer delay for loading the pieces. */
3538 for (n = 0; n < pieceDepth; n ++) {
3539 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
3540 0, 0, squareSize, squareSize,
3546 XFreePixmap(xDisplay, buf);
3547 XFreeGC(xDisplay, bufGC);
3548 XFreeGC(xDisplay, maskGC);
3552 InitAnimState (AnimNr anr, XWindowAttributes *info)
3557 /* Each buffer is square size, same depth as window */
3558 animBufs[anr+4] = xBoardWindow;
3559 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
3560 squareSize, squareSize, info->depth);
3561 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
3562 squareSize, squareSize, info->depth);
3564 /* Create a plain GC for blitting */
3565 mask = GCForeground | GCBackground | GCFunction |
3566 GCPlaneMask | GCGraphicsExposures;
3567 values.foreground = XBlackPixel(xDisplay, xScreen);
3568 values.background = XWhitePixel(xDisplay, xScreen);
3569 values.function = GXcopy;
3570 values.plane_mask = AllPlanes;
3571 values.graphics_exposures = False;
3572 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
3574 /* Piece will be copied from an existing context at
3575 the start of each new animation/drag. */
3576 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
3578 /* Outline will be a read-only copy of an existing */
3579 animGCs[anr+4] = None;
3585 XWindowAttributes info;
3587 if (xpmDone && gameInfo.variant == oldVariant) return;
3588 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
3589 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
3591 InitAnimState(Game, &info);
3592 InitAnimState(Player, &info);
3594 /* For XPM pieces, we need bitmaps to use as masks. */
3596 CreateAnimMasks(info.depth), xpmDone = 1;
3601 static Boolean frameWaiting;
3604 FrameAlarm (int sig)
3606 frameWaiting = False;
3607 /* In case System-V style signals. Needed?? */
3608 signal(SIGALRM, FrameAlarm);
3612 FrameDelay (int time)
3614 struct itimerval delay;
3616 XSync(xDisplay, False);
3619 frameWaiting = True;
3620 signal(SIGALRM, FrameAlarm);
3621 delay.it_interval.tv_sec =
3622 delay.it_value.tv_sec = time / 1000;
3623 delay.it_interval.tv_usec =
3624 delay.it_value.tv_usec = (time % 1000) * 1000;
3625 setitimer(ITIMER_REAL, &delay, NULL);
3626 while (frameWaiting) pause();
3627 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
3628 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
3629 setitimer(ITIMER_REAL, &delay, NULL);
3636 FrameDelay (int time)
3638 XSync(xDisplay, False);
3640 usleep(time * 1000);
3646 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
3650 /* Bitmap for piece being moved. */
3651 if (appData.monoMode) {
3652 *mask = *pieceToSolid(piece);
3653 } else if (useImages) {
3655 *mask = xpmMask[piece];
3657 *mask = ximMaskPm[piece];
3660 *mask = *pieceToSolid(piece);
3663 /* GC for piece being moved. Square color doesn't matter, but
3664 since it gets modified we make a copy of the original. */
3666 if (appData.monoMode)
3671 if (appData.monoMode)
3676 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
3678 /* Outline only used in mono mode and is not modified */
3680 *outline = bwPieceGC;
3682 *outline = wbPieceGC;
3686 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
3691 /* Draw solid rectangle which will be clipped to shape of piece */
3692 XFillRectangle(xDisplay, dest, clip,
3693 0, 0, squareSize, squareSize);
3694 if (appData.monoMode)
3695 /* Also draw outline in contrasting color for black
3696 on black / white on white cases */
3697 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
3698 0, 0, squareSize, squareSize, 0, 0, 1);
3700 /* Copy the piece */
3705 if(appData.upsideDown && flipView) kind ^= 2;
3706 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3708 0, 0, squareSize, squareSize,
3714 InsertPiece (AnimNr anr, ChessSquare piece)
3716 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
3720 DrawBlank (AnimNr anr, int x, int y, int startColor)
3722 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
3725 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
3726 int srcX, int srcY, int width, int height, int destX, int destY)
3728 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
3729 srcX, srcY, width, height, destX, destY);
3733 SetDragPiece (AnimNr anr, ChessSquare piece)
3736 /* The piece will be drawn using its own bitmap as a matte */
3737 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
3738 XSetClipMask(xDisplay, animGCs[anr+2], mask);
3741 /* [AS] Arrow highlighting support */
3744 DrawPolygon (Pnt arrow[], int nr)
3748 for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
3749 XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
3750 if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
3754 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
3756 char buf[MSG_SIZ], *logoName = buf;
3757 if(appData.logo[n][0]) {
3758 logoName = appData.logo[n];
3759 } else if(appData.autoLogo) {
3760 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
3761 sprintf(buf, "%s/%s.xpm", appData.logoDir, appData.icsHost);
3762 } else if(appData.directory[n] && appData.directory[n][0]) {
3763 sprintf(buf, "%s/%s.xpm", appData.logoDir, cps->tidy);
3767 XpmReadFileToPixmap(xDisplay, xBoardWindow, logoName, (Pixmap *) &(cps->programLogo), NULL, NULL);
3771 UpdateLogos (int displ)
3773 if(optList[W_WHITE-1].handle == NULL) return;
3774 LoadLogo(&first, 0, 0);
3775 LoadLogo(&second, 1, appData.icsActive);
3776 if(displ) DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);