/*****************************************************************************/ /* */ /* */ /* X patience version 2 -- module Xaw-main.c */ /* */ /* main function for the Athena Widget interface */ /* written by Michael Bischoff (mbi@mo.math.nat.tu-bs.de) */ /* March-1994 */ /* see COPYRIGHT.xpat2 for Copyright details */ /* */ /* */ /*****************************************************************************/ #ifdef useXaw #include "X-pat.h" #include "Tableau.h" #include "version.h" Window table, mainwindow; Widget toplevel; static void perform_command(Widget widget, XtPointer client_data, XtPointer call_data) { (*(void (*)(void))client_data)(); /* any questions? */ } void create_help(void); void popup_help(void); void popdown_help(Widget, XtPointer, XtPointer); static Widget messagebox, container, desktop; void show_message(const char *str, ...) { static char last_message[256]; Arg Args; va_list args; va_start(args, str); if (!str) { memset(last_message, ' ', sizeof(last_message)-1); last_message[sizeof(last_message)-1] = '\0'; } else vsprintf(last_message, str, args); XtSetArg(Args, XtNlabel, last_message); XtSetValues(messagebox, &Args, 1); } static XtAppContext app_con; static void (*execfunc)(void) = NULL; static Widget dialog, popup; void cmd_LeavePat(void) { play_sound("goodbye"); XtDestroyApplicationContext(app_con); exit(0); } static void popup_confirm(const char *prompt) { Arg args[2]; Position x, y; Dimension xx, yy; XtSetArg(args[0], XtNlabel, prompt); XtSetValues(dialog, args, 1); XtVaGetValues(dialog, XtNwidth, &xx, XtNheight, &yy, NULL); x = (geo.w_width/2) - xx/2; y = (geo.w_height/3) - yy/2; XtTranslateCoords(toplevel, x, y, &x, &y); XtSetArg(args[0], XtNx, x); XtSetArg(args[1], XtNy, y); XtSetValues(popup, args, 2); XtPopup(popup, XtGrabNone); } void cmd_Confirm(void) { if (execfunc) { void (*execfunc2)(void) = execfunc; /* erase it first! */ execfunc = NULL; XtPopdown(popup); (*execfunc2)(); /* finally execute the desired function */ } } void cmd_Cancel(void) { if (execfunc) { execfunc = NULL; XtPopdown(popup); } } /* type converter functions: */ static void mXtAP_Cancel (Widget w, XEvent *xev, String *params, Cardinal *num) { cmd_Cancel(); } static void mXtAP_Confirm(Widget w, XEvent *xev, String *params, Cardinal *num) { cmd_Confirm(); } static void Cancel(Widget widget, XtPointer client_data, XtPointer call_data) { cmd_Cancel(); } static void Ok(Widget widget, XtPointer client_data, XtPointer call_data) { cmd_Confirm(); } void request_confirm(void (*dofunc)(void), const char *prompt) { if (execfunc) return; /* request pending => deny another one */ execfunc = dofunc; popup_confirm(prompt); } static String fallback_resources[] = { "*beNiceToColormap: false", "*shapeStyle: Rectangle", "*topShadowContrast: 20", "*bottomShadowContrast: 40", "*Scrollbar*background: Grey70", "*Background: grey85", "*Foreground: black", "*resizeToPreferred: True", "*input: True", "*showGrip: off", "*shadowWidth: 2", "*messages.justify: Left", "*upperbox.orientation: XtorientHorizontal", "*lowerbox.orientation: XtorientHorizontal", "*Tableau.backingStore: WhenMapped", "*Tableau.keyboardFile: keys", "*Tableau.messageFile: messages", "*Tableau.background: LightSlateGray", "*Tableau.arrowColor: Gold", "*Tableau.markColor: Black", "*Tableau.rules: Gypsy", "*Tableau.rng: 1", "*Tableau.xpmcls: " XDEFAULT_CLOSENESS, "*Viewport.allowHoriz: True", "*Viewport.allowVert: True", "*Viewport.useBottom: True", "*Viewport.useRight: True", "*Label.shadowWidth: 0", "*Label.BorderWidth: 2", "*Dialog*Translations: #override \ny: Ok()\nn: Cancel()\n", "XPat*title: XPat", "XPat.prompt.allowShellResize: True", "XPat.prompt.saveUnder: True", "*Dialog*resizable: True", "*Hint.Translations: #override\n:set()\n:HintNotify()unset()\n", "*Sound.state: True", "XPat.help.width: 403", "XPat.help.height: 200", "XPat.help.title: XPat Help Window", "XPat.help.saveUnder: True", "XPat*Close Help.fromHoriz: Topic", "*helptext*string: Please choose a topic.", "*helptext*displayCaret: False", "*helptext*scrollHorizontal: whenNeeded", "*helptext*scrollVertical: whenNeeded", "*helptext*editType: read", NULL, }; static XrmOptionDescRec options[] = { /* tableau resources */ { "-xpmdir", "*Tableau.xpmdir", XrmoptionSepArg, NULL }, { "-xpmcls", "*Tableau.xpmcls", XrmoptionSepArg, NULL }, { "-rules", "*Tableau.rules", XrmoptionSepArg, NULL }, { "-faceup", "*Tableau.faceup", XrmoptionSepArg, NULL }, { "-facedown", "*Tableau.facedown", XrmoptionSepArg, NULL }, { "-decks", "*Tableau.decks", XrmoptionSepArg, NULL }, { "-slots", "*Tableau.slots", XrmoptionSepArg, NULL }, { "-jokers", "*Tableau.jokers", XrmoptionSepArg, NULL }, { "-tmps", "*Tableau.tmps", XrmoptionSepArg, NULL }, { "-relaxed", "*Tableau.relaxed", XrmoptionSepArg, NULL }, { "-flips", "*Tableau.flips", XrmoptionSepArg, NULL }, { "-turn", "*Tableau.turn", XrmoptionSepArg, NULL }, { "-rotations", "*Tableau.rotations", XrmoptionSepArg, NULL }, { "-rng", "*Tableau.rng", XrmoptionSepArg, NULL }, #ifdef LOADCARDS { "-cards", "*Tableau.cards", XrmoptionSepArg, NULL }, #endif #if defined(GFX1) || defined(LOADCARDS) { "-cb", "*Tableau.cardbackColor",XrmoptionSepArg,NULL }, { "-red", "*Tableau.RedColor", XrmoptionSepArg, NULL }, #endif { "-tb", "*Tableau.background", XrmoptionSepArg,NULL }, { "-arrowcolor", "*Tableau.arrowColor", XrmoptionSepArg, NULL }, { "-markcolor", "*Tableau.markColor", XrmoptionSepArg, NULL }, { "-markwidth", "*Tableau.markWidth", XrmoptionSepArg, NULL }, /* non-tableau resources */ #ifdef SOUND { "-sound", "*Sound.state", XrmoptionNoArg, (XtPointer)"True" }, { "-nosound", "*Sound.state", XrmoptionNoArg, (XtPointer)"False" }, #endif }; static void HintNotify(Widget w, XEvent *xev, String *params, Cardinal *num) { /* check to be sure state is still Set so that user can cancel the action (e.g. by moving outside the window, in the default bindings. */ /* Nope, we cannot do this, the 'set' state is private data */ /* CommandWidget cbw = (CommandWidget)w; if (cbw->command.set) XtCallCallbackList(w, cbw->command.callbacks, (XtPointer) NULL); */ switch (((XButtonPressedEvent *)xev)->button) { case Button1: cmd_NextHint(); break; case Button2: cmd_DoHint(); break; case Button3: cmd_PreviousHint(); } } static XtActionsRec moreActions[] = { { "HintNotify", HintNotify }, { "Cancel", mXtAP_Cancel }, { "Ok", mXtAP_Confirm } }; static void reportfunc(Widget wid, XtPointer client_data, XtPointer call_data) { XawPannerReport *rep; if (game.graphic) cmd_ResetHints(); rep = (XawPannerReport *)call_data; if (geo.w_width != rep->slider_width || geo.w_height != rep->slider_height) { static int busy = 0; /* hope this fixes cycling */ /* size was changed */ if (busy) ;/*printf("doing nothing, I\'m busy!\n"); */ else { geo.w_width = rep->slider_width; geo.w_height = rep->slider_height; if (game.graphic) { int w, h; busy = 1; #ifdef LABER printf("w/h = %d,%d, prf=%d,%d, w_w/h=%d,%d\n", geo.w, geo.h, geo.preferred_width, geo.preferred_height, geo.w_width, geo.w_height); #endif init_layout(); w = max(geo.preferred_width, geo.w_width); h = max(geo.preferred_height, geo.w_height); AskWidgetForResize(w, h); busy = 0; } } } } char *loadfilename; static void process_extra_args(int argc, char *argv[]) { /* check extra args */ game.seed = -1L; loadfilename = NULL; if (argc > 2) { usage: fprintf(stderr, "xpat: invalid argument: %s\n", argv[1]); fprintf(stderr, "usage: xpat2 [options] [seed|loadfilename]\n" "options are all standard X11 toolkit options and\n" "-xpmdir (dir) to set directory for xpm files\n" "-xpmcls (val) to set allowed closeness for colour allocation\n" "-rules (ruleset) to initially use specified rules\n" "-faceup|facedown|decks|slots|jokers|tmps (num) customize parameters\n" "-relaxed|flips|turn|rotations (num) customize parameters\n" "-tb (color) set background color of tableau\n" "-arrowcolor (color) set color of hint arrow\n" "-markcolor (color) set color of frame to mark cards\n" "-markwidth (wdth) set width of frame to mark cards\n" #ifdef SOUND "-sound sound toggle on\n" "-nosound sound toggle off\n" #endif #ifdef LOADCARDS "-cards (filename) to load external bitmap cardset\n" #endif #if defined(GFX1) || defined(LOADCARDS) "-cb (color) set cardback color\n" "-red (color) use a different 'red' for static grey screens\n" #endif ); exit(EXIT_FAILURE); } else if (argc == 2) { /* seed or load file */ if (*argv[1] == '-') goto usage; else if (isdigit(*argv[1])) { game.seed = atol(argv[1]); if (game.seed < 0L || game.seed >= 1000000000L) goto usage; } else { loadfilename = argv[1]; /* printf("loadfile is %s\n", loadfilename); */ } } /* printf("in process_args: seed is %ld\n", game.seed); */ } /*extern void cmd_DebugOut(void); */ extern struct rules *rulepool[]; static void selectrules(Widget w, XtPointer number, XtPointer garbage) { const char *s = XtName(w); /* printf("widget %s has been selected\n", s); */ change_rules(s); } #ifdef SOUND static Widget sound; int checksound(void) { Boolean retval; Arg args[1]; XtSetArg(args[0], XtNstate, &retval); XtGetValues(sound, args, 1); return retval & 0xff; } #endif static Widget paned; int main(int argc, char *argv[]) { Widget buttonpanel; Widget gamebutton, gamemenu, rulesbutton, rulesmenu; int i; struct button { const char *name; void (*func)(void); } *bp; static struct button buttons[] = { { "Cards", cmd_DealCards }, { "Undo", cmd_UndoMove }, { "Redo", cmd_RedoMove }, { "Score", cmd_ShowScore }, { "Hint", cmd_NextHint }, { "Move to Stack", cmd_ToStack }, /* { "Debug", cmd_DebugOut }, */ #ifdef ONLINE_HELP { "Help", popup_help }, #endif { "Save", cmd_SaveGame } }, mbuttons[] = { { "Drop Bookmark", cmd_DropBookmark }, { "Goto Bookmark", cmd_GotoBookmark }, { "Replay", cmd_ReplayGame }, { "Restart", cmd_RestartGame }, { "New Game", rq_AnotherGame }, { "Quit", rq_LeavePat } }; /* use the command line arguments concerning the widgets */ switch_uid(1); /* switch to real uid for reading .Xauthority */ toplevel = XtAppInitialize(&app_con, "XPat", options, XtNumber(options), &argc, argv, fallback_resources, NULL, 0); switch_uid(0); /* back to effective uid */ process_extra_args(argc, argv); XtAppAddActions(app_con, moreActions, XtNumber(moreActions)); graphics_control(Disable); /* basic elements */ paned = XtCreateManagedWidget("paned", panedWidgetClass, toplevel, NULL, 0); buttonpanel = XtCreateManagedWidget("buttonpanel", boxWidgetClass, paned, NULL, 0); messagebox = XtCreateManagedWidget("messages", labelWidgetClass, paned, NULL, 0); show_message(" "); container = XtCreateManagedWidget("container", viewportWidgetClass, paned, NULL, 0); desktop = XtCreateManagedWidget("desktop", tableauWidgetClass, container, NULL, 0); XtAddCallback(container, XtNreportCallback, reportfunc, NULL); /* create the button panel and its menus */ gamebutton = XtCreateManagedWidget("Game", menuButtonWidgetClass, buttonpanel, NULL, 0); gamemenu = XtCreatePopupShell("gamemenu", simpleMenuWidgetClass, gamebutton, NULL, 0); for (bp = mbuttons, i = 0; i < XtNumber(mbuttons); ++i) { Widget w; w = XtCreateManagedWidget(bp->name, smeBSBObjectClass, gamemenu, NULL, 0); if (bp->func) XtAddCallback(w, XtNcallback, perform_command, bp->func); ++bp; } XtVaSetValues(gamebutton, XtNmenuName, "gamemenu", NULL); /* create Rules button just right of the Game button */ rulesbutton = XtCreateManagedWidget("Rules", menuButtonWidgetClass, buttonpanel, NULL, 0); rulesmenu = XtCreatePopupShell("rulesmenu", simpleMenuWidgetClass, rulesbutton, NULL, 0); { struct rules **rp; for (rp = rulepool; *rp; ++rp) { Widget w; w = XtCreateManagedWidget((*rp)->shortname, smeBSBObjectClass, rulesmenu, NULL, 0); XtAddCallback(w, XtNcallback, selectrules, NULL); } } XtVaSetValues(rulesbutton, XtNmenuName, "rulesmenu", NULL); /* rest of the buttons */ for (bp = buttons, i = 0; i < XtNumber(buttons); ++i) { Widget w; w = XtCreateManagedWidget(bp->name, commandWidgetClass, buttonpanel, NULL, 0); if (bp->func) XtAddCallback(w, XtNcallback, perform_command, bp->func); ++bp; } #ifdef SOUND sound = XtCreateManagedWidget("Sound", toggleWidgetClass, buttonpanel, NULL, 0); #endif /* OK. Now do the pop-up shells */ popup = XtCreatePopupShell("prompt", transientShellWidgetClass, toplevel, NULL, 0); dialog = XtCreateManagedWidget("dialog", dialogWidgetClass, popup, NULL, 0); XawDialogAddButton(dialog, "ok", Ok, (XtPointer)dialog); XawDialogAddButton(dialog, "cancel", Cancel, (XtPointer)dialog); #ifdef ONLINE_HELP create_help(); #endif geo.w_width = geo.w_height = 0; XtRealizeWidget(toplevel); mainwindow = XtWindow(toplevel); table = XtWindow(desktop); XtRealizeWidget(popup); graphics_control(Enable); init_layout(); /* position the piles in a nice way */ for (i = 0; i < game.numpiles; ++i) pile_resize(i); show_message(TXT_WELCOME, VERSION); XStoreName(dpy, mainwindow, rules.longname ? rules.longname : rules.shortname); if (!geo.w_width) { geo.w_width = graphic.width; geo.w_height = graphic.height; } /* printf("before mainloop: seed is %ld\n", game.seed); */ XtAppMainLoop(app_con); /* does not return */ return 0; /* keep compiler happy */ } void Force_Resize(XSize_t w, XSize_t h) { #ifdef LABER printf("Force_Resize(%d,%d) called. w/h = %d,%d, w_w/h = %d,%d\n", w, h, graphic.width, graphic.height, geo.w_width, geo.w_height); #endif /* for Xlib, we can use this size. It does include space for buttons and messagebox */ #ifdef useXaw /* for Xaw, we have to add the size of the buttonbox and the message window */ /* note that the result may have a wrong height, due to recalculation of the box widget. A second call to this function will always give the correct result. */ /* The overall size can be computed by adding the difference between the current size of the mainwindow - current size of viewport window */ { Arg args[1]; int hh; Dimension hhh = 0; XtSetArg(args[0], XtNheight, &hhh); XtGetValues(paned, args, 1); hh = hhh; /* unsigned short => int */ h += hh - geo.w_height; /* difference between overall size and Viewport size */ /* sanity check! */ } #endif /* printf("called XResize Window %d,%d\n", w, h); */ XResizeWindow(dpy, mainwindow, w, h); /* or should XSetWMSizeHints() be used? */ } #endif