From: cohrs Date: Fri, 19 Sep 2003 03:15:49 +0000 (+0000) Subject: SAFERHANGUP X-Git-Tag: MOVE2GIT~1773 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=40b5b126739897ee9e0ba152fe08446b9c322589;p=nethack SAFERHANGUP This is an initial round of SAFERHANGUP hangup changes. It introduces SAFERHANGUP, provides the core framework, and enables it for UNIX. Window-port changes are provided for win/tty, win/X11 and win/gnome. Qt changes should be forthcoming after having Warwick look at them. window.doc is updated so windowport maintainers have an clue what needs to be done to support SAFERHANGUP. --- diff --git a/doc/fixes35.0 b/doc/fixes35.0 index aace11f4e..7e2a34e6c 100644 --- a/doc/fixes35.0 +++ b/doc/fixes35.0 @@ -41,6 +41,9 @@ win32gui: better handling of "more" prompt for messages that would have scrolled win32gui: set correct checkmark on "Lock Windows" menu item on startup win32gui: redraw message window on resizing (it does not update properly otherwise) win32gui: fixed copy/paste error in read registry settings function +platforms that support hangup: SAFERHANGUP to avoid losing objects in transit + between lists when hangup occurs, and also avoid cheats due to + well-timed hangups to stop a long melee General New Features diff --git a/doc/window.doc b/doc/window.doc index cbfc57147..2c79965ad 100644 --- a/doc/window.doc +++ b/doc/window.doc @@ -120,6 +120,15 @@ int nhgetch() -- Returns a single character input from the user. will be the routine the OS provides to read a character. Returned character _must_ be non-zero and it must be non meta-zero too (zero with the meta-bit set). + -- If platform uses it, should check program_state.done_hup + and immediately return ASCII 033 (escape) if it is. + This is required if the window-port supports SAFERHANGUP. + -- ASCII 033 must also be returned rather than EOF (applies + mainly to the tty window-port). + -- The program_state.done_hup flag can be set asynchronously + when SAFERHANGUP is defined and in that case, nhgetch() + needs to detect that the value of program_state.done_hup + changed and also return ASCII 033 in this case. int nh_poskey(int *x, int *y, int *mod) -- Returns a single character input from the user or a a positioning event (perhaps from a mouse). If the @@ -133,6 +142,7 @@ int nh_poskey(int *x, int *y, int *mod) The different click types can map to whatever the hardware supports. If no mouse is supported, this routine always returns a non-zero character. + -- Otherwise follows the same behavior as nhgetch(). B. High-level routines: diff --git a/include/unixconf.h b/include/unixconf.h index fe1b00661..34eefe29f 100644 --- a/include/unixconf.h +++ b/include/unixconf.h @@ -256,6 +256,14 @@ #define SUSPEND /* let ^Z suspend the game */ #endif +/* + * Define SAFERHANGUP to delay hangup processing until the main command + * loop. 'safer' because it avoids certain cheats and also avoids losing + * objects being thrown when the hangup occurs. All unix windowports + * support SAFERHANGUP (couldn't define it here otherwise). + */ +#define SAFERHANGUP + #if defined(BSD) || defined(ULTRIX) #include diff --git a/src/cmd.c b/src/cmd.c index 522a2c5b8..562d5630a 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -146,7 +146,7 @@ STATIC_PTR boolean NDECL(minimal_enlightenment); STATIC_DCL void FDECL(enlght_line, (const char *,const char *,const char *)); STATIC_DCL char *FDECL(enlght_combatinc, (const char *,int,int,char *)); -#ifdef UNIX +#if defined(UNIX) || defined(SAFERHANGUP) static void NDECL(end_of_input); #endif @@ -1794,6 +1794,9 @@ register char *cmd; firsttime = (cmd == 0); iflags.menu_requested = FALSE; +#ifdef SAFERHANGUP + if (program_state.done_hup) end_of_input(); +#endif if (firsttime) { flags.nopick = 0; cmd = parse(); @@ -2375,15 +2378,17 @@ parse() return(in_line); } -#ifdef UNIX +#if defined(UNIX) || defined(SAFERHANGUP) static void end_of_input() { exit_nhwindows("End of input?"); #ifndef NOSAVEONHANGUP - if (!program_state.done_hup++ && program_state.something_worth_saving) - (void) dosave0(); +# ifndef SAFERHANGUP + if (!program_state.done_hup++) +#endif + if (program_state.something_worth_saving) (void) dosave0(); #endif clearlocks(); terminate(EXIT_SUCCESS); @@ -2420,8 +2425,14 @@ readchar() } while (--cnt && sym == EOF); } # endif /* NR_OF_EOFS */ - if (sym == EOF) + if (sym == EOF) { +# ifndef SAFERHANGUP end_of_input(); +# else + program_state.done_hup++; + sym = '\033'; +# endif + } #endif /* UNIX */ if(sym == 0) { diff --git a/src/save.c b/src/save.c index 6230788fd..0ce6d7e85 100644 --- a/src/save.c +++ b/src/save.c @@ -91,6 +91,11 @@ int sig_unused; # endif # else /* SAVEONHANGUP */ if (!program_state.done_hup++) { +# ifndef SAFERHANGUP + /* When using SAFERHANGUP, the done_hup flag it tested in rhack + * and actual hangup behavior occurs then. This is 'safer' + * because it disallows certain cheats and also protects + * against losing objects in the process of being thrown. */ if (program_state.something_worth_saving) (void) dosave0(); # ifdef VMS @@ -102,9 +107,9 @@ int sig_unused; clearlocks(); terminate(EXIT_FAILURE); } +# endif /* !SAFERHANGUP */ } # endif - return; } #endif diff --git a/sys/unix/unixmain.c b/sys/unix/unixmain.c index 79cb24ba8..a4b8c86c4 100644 --- a/sys/unix/unixmain.c +++ b/sys/unix/unixmain.c @@ -163,9 +163,28 @@ char *argv[]; * It seems you really want to play. */ u.uhp = 1; /* prevent RIP on early quits */ +#ifdef SA_RESTART + /* don't want reads to restart. If SA_RESTART is defined, we know + * sigaction exists and can be used to ensure reads won't restart. + * If it's not defined, assume reads do not restart. If reads restart + * and a signal occurs, the game won't do anything until the read + * succeeds (or the stream returns EOF, which might not happen if + * reading from, say, a window manager). */ + { + struct sigaction sact; + + (void) memset((char*) &sact, 0, sizeof(struct sigaction)); + sact.sa_handler = (SIG_RET_TYPE)hangup; + (void) sigaction(SIGHUP, &sact, (struct sigaction*)0); +#ifdef SIGXCPU + (void) sigaction(SIGXCPU, &sact, (struct sigaction*)0); +#endif + } +#else (void) signal(SIGHUP, (SIG_RET_TYPE) hangup); #ifdef SIGXCPU (void) signal(SIGXCPU, (SIG_RET_TYPE) hangup); +#endif #endif process_options(argc, argv); /* command line options */ diff --git a/sys/unix/unixunix.c b/sys/unix/unixunix.c index f6c8b4bae..80756df61 100644 --- a/sys/unix/unixunix.c +++ b/sys/unix/unixunix.c @@ -155,19 +155,23 @@ getlock() (void) printf("\nThere is already a game in progress under your name."); (void) printf(" Destroy old game? [yn] "); (void) fflush(stdout); - c = getchar(); - (void) putchar(c); - (void) fflush(stdout); - while (getchar() != '\n') ; /* eat rest of line and newline */ + if ((c = getchar()) != EOF) { + int tmp; + + (void) putchar(c); + (void) fflush(stdout); + while ((tmp = getchar()) != '\n' && tmp != EOF) + ; /* eat rest of line and newline */ + } } - if(c == 'y' || c == 'Y') + if(c == 'y' || c == 'Y') { if(eraseoldlocks()) goto gotlock; else { unlock_file(HLOCK); error("Couldn't destroy old game."); } - else { + } else { unlock_file(HLOCK); error("%s", ""); } diff --git a/win/X11/winX.c b/win/X11/winX.c index d71159cd5..0c37b0988 100644 --- a/win/X11/winX.c +++ b/win/X11/winX.c @@ -55,6 +55,10 @@ #include "patchlevel.h" #endif +#ifndef NO_SIGNAL +#include +#endif + /* Should be defined in but you never know */ #ifndef XtSpecificationRelease #define XtSpecificationRelease 0 @@ -88,6 +92,9 @@ int click_x, click_y, click_button; /* Click position on a map window */ /* (filled by set_button_values()). */ int updated_inventory; +#ifndef NO_SIGNAL +static XtSignalId X11_sig_id; +#endif /* Interface definition, for windows.c */ struct window_procs X11_procs = { @@ -162,7 +169,10 @@ static void FDECL(X11_hangup, (Widget, XEvent*, String*, Cardinal*)); static int FDECL(input_event, (int)); static void FDECL(win_visible, (Widget,XtPointer,XEvent *,Boolean *)); static void NDECL(init_standard_windows); - +#if !defined(NO_SIGNAL) && defined(SAFERHANGUP) +static void FDECL(X11_sig, (int)); +static void FDECL(X11_sig_cb, (XtPointer, XtSignalId*)); +#endif /* * Local variables. @@ -604,6 +614,32 @@ X11_create_nhwindow(type) if (!x_inited) panic("create_nhwindow: windows not initialized"); +#if !defined(NO_SIGNAL) && defined(SAFERHANGUP) + /* set up our own signal handlers on the first call. Can't do this in + * X11_init_nhwindows because unixmain sets its handler after calling + * all the init routines. */ + if (X11_sig_id == 0) { + X11_sig_id = XtAppAddSignal(app_context, X11_sig_cb, (XtPointer)0); +#ifdef SA_RESTART + { + struct sigaction sact; + + (void) memset((char*) &sact, 0, sizeof(struct sigaction)); + sact.sa_handler = (SIG_RET_TYPE)X11_sig; + (void) sigaction(SIGHUP, &sact, (struct sigaction*)0); +#ifdef SIGXCPU + (void) sigaction(SIGXCPU, &sact, (struct sigaction*)0); +#endif + } +#else + (void) signal(SIGHUP, (SIG_RET_TYPE) X11_sig); +#ifdef SIGXCPU + (void) signal(SIGXCPU, (SIG_RET_TYPE) X11_sig); +#endif +#endif + } +#endif + /* * We have already created the standard message, map, and status * windows in the window init routine. The first window of that @@ -1064,6 +1100,36 @@ void X11_exit_nhwindows(dummy) X11_destroy_nhwindow(WIN_MESSAGE); } +#if !defined(NO_SIGNAL) && defined(SAFERHANGUP) +void +X11_sig(sig) /* Unix signal handler */ +int sig; +{ + XtNoticeSignal(X11_sig_id); + hangup(sig); +} + +void +X11_sig_cb(not_used, id) + XtPointer not_used; + XtSignalId* id; +{ + XEvent event; + XClientMessageEvent *mesg; + + /* Set up a fake message to the event handler. */ + mesg = (XClientMessageEvent *) &event; + mesg->type = ClientMessage; + mesg->message_type = XA_STRING; + mesg->format = 8; + + XSendEvent(XtDisplay(window_list[WIN_MAP].w), + XtWindow(window_list[WIN_MAP].w), + False, + NoEventMask, + (XEvent*) mesg); +} +#endif /* delay_output ------------------------------------------------------------ */ diff --git a/win/X11/winmap.c b/win/X11/winmap.c index bef860bd3..3e23214cd 100644 --- a/win/X11/winmap.c +++ b/win/X11/winmap.c @@ -1622,6 +1622,10 @@ x_event(exit_condition) inptr = (inptr+1) % INBUF_SIZE; /* pkey(retval); */ keep_going = FALSE; + } else if (program_state.done_hup) { + retval = '\033'; + inptr = (inptr+1) % INBUF_SIZE; + keep_going = FALSE; } break; case EXIT_ON_KEY_OR_BUTTON_PRESS: @@ -1637,6 +1641,10 @@ x_event(exit_condition) /* pkey(retval); */ } keep_going = FALSE; + } else if (program_state.done_hup) { + retval = '\033'; + inptr = (inptr+1) % INBUF_SIZE; + keep_going = FALSE; } break; default: diff --git a/win/gnome/gnbind.c b/win/gnome/gnbind.c index e97962958..185090ddd 100644 --- a/win/gnome/gnbind.c +++ b/win/gnome/gnbind.c @@ -158,7 +158,7 @@ gnome_player_selection() if (sel >= 0) sel = pickmap[sel]; else if (sel == ROLE_NONE) { /* Quit */ clearlocks(); - gnome_exit_nhwindows(0); + gtk_exit(0); } free(choices); free(pickmap); @@ -212,7 +212,7 @@ gnome_player_selection() if (sel >= 0) sel = pickmap[sel]; else if (sel == ROLE_NONE) { /* Quit */ clearlocks(); - gnome_exit_nhwindows(0); + gtk_exit(0); } flags.initrace = sel; free(choices); @@ -267,7 +267,7 @@ gnome_player_selection() if (sel >= 0) sel = pickmap[sel]; else if (sel == ROLE_NONE) { /* Quit */ clearlocks(); - gnome_exit_nhwindows(0); + gtk_exit(0); } flags.initgend = sel; free(choices); @@ -320,7 +320,7 @@ gnome_player_selection() if (sel >= 0) sel = pickmap[sel]; else if (sel == ROLE_NONE) { /* Quit */ clearlocks(); - gnome_exit_nhwindows(0); + gtk_exit(0); } flags.initalign = sel; free(choices); @@ -350,7 +350,7 @@ void gnome_askname() /* Quit if they want to quit... */ if (ret==-1) { - gnome_exit_nhwindows(0); + gtk_exit(0); } } @@ -369,8 +369,7 @@ void gnome_get_nh_event() */ void gnome_exit_nhwindows(const char *str) { - gtk_exit (0); - terminate(EXIT_SUCCESS); + /* gtk cannot do this without exiting the program, do nothing */ } /* Prepare the window to be suspended. */ @@ -398,19 +397,17 @@ void gnome_resume_nhwindows() winid gnome_create_nhwindow(int type) { + winid i = 0; - winid i = 0; + /* Return the next available winid */ -/* Return the next available winid - */ - - for (i=0; i 0) { int key; diff --git a/win/tty/getline.c b/win/tty/getline.c index cb797b953..973c13782 100644 --- a/win/tty/getline.c +++ b/win/tty/getline.c @@ -58,12 +58,7 @@ getlin_hook_proc hook; (void) fflush(stdout); Sprintf(toplines, "%s ", query); Strcat(toplines, obufp); - if((c = Getchar()) == EOF) { -#ifndef NEWAUTOCOMP - *bufp = 0; -#endif /* not NEWAUTOCOMP */ - break; - } + if((c = Getchar()) == EOF) c = '\033'; if(c == '\033') { *obufp = c; obufp[1] = 0; @@ -183,6 +178,11 @@ register const char *s; /* chars allowed besides return */ while((c = tty_nhgetch()) != '\n') { if(iflags.cbreak) { + if (c == EOF || c == '\033') { + ttyDisplay->dismiss_more = 1; + morc = '\033'; + break; + } if ((s && index(s,c)) || c == x) { morc = (char) c; break; diff --git a/win/tty/wintty.c b/win/tty/wintty.c index 1be353751..2e1a90edb 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -677,7 +677,7 @@ tty_askname() wins[BASE_WINDOW]->cury - 1); ct = 0; while((c = tty_nhgetch()) != '\n') { - if(c == EOF) error("End of input\n"); + if(c == EOF) c = '\033'; if (c == '\033') { ct = 0; break; } /* continue outer loop */ #if defined(WIN32CON) if (c == '\003') bail("^C abort.\n"); @@ -2520,6 +2520,7 @@ tty_nhgetch() i = tgetch(); #endif if (!i) i = '\033'; /* map NUL to ESC since nethack doesn't expect NUL */ + else if (i == EOF) i = '\033'; /* same for EOF */ if (ttyDisplay && ttyDisplay->toplin == 1) ttyDisplay->toplin = 2; return i; @@ -2546,8 +2547,8 @@ tty_nh_poskey(x, y, mod) if (WIN_MESSAGE != WIN_ERR && wins[WIN_MESSAGE]) wins[WIN_MESSAGE]->flags &= ~WIN_STOP; i = ntposkey(x, y, mod); - if (!i && mod && *mod == 0) - i = '\033'; /* map NUL to ESC since nethack doesn't expect NUL */ + if (!i && mod && (*mod == 0 || *mod == EOF)) + i = '\033'; /* map NUL or EOF to ESC, nethack doesn't expect either */ if (ttyDisplay && ttyDisplay->toplin == 1) ttyDisplay->toplin = 2; return i;