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.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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 <sys/time.h>
|
||||
|
||||
21
src/cmd.c
21
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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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", "");
|
||||
}
|
||||
|
||||
@@ -55,6 +55,10 @@
|
||||
#include "patchlevel.h"
|
||||
#endif
|
||||
|
||||
#ifndef NO_SIGNAL
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
/* Should be defined in <X11/Intrinsic.h> 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 ------------------------------------------------------------ */
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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<MAXWINDOWS; i++)
|
||||
if (gnome_windowlist[i].win == NULL)
|
||||
break;
|
||||
if (i == MAXWINDOWS)
|
||||
g_error ("ERROR: No windows available...\n");
|
||||
gnome_create_nhwindow_by_id( type, i);
|
||||
return i;
|
||||
for (i=0; i<MAXWINDOWS; i++)
|
||||
if (gnome_windowlist[i].win == NULL)
|
||||
break;
|
||||
if (i == MAXWINDOWS)
|
||||
g_error ("ERROR: No windows available...\n");
|
||||
gnome_create_nhwindow_by_id( type, i);
|
||||
return i;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -910,8 +907,10 @@ int gnome_nhgetch()
|
||||
|
||||
g_askingQuestion = 1;
|
||||
/* Process events until a key press event arrives. */
|
||||
while ( g_numKeys == 0 )
|
||||
while ( g_numKeys == 0 ) {
|
||||
if (program_state.done_hup) return '\033';
|
||||
gtk_main_iteration();
|
||||
}
|
||||
|
||||
theFirst = g_list_first( g_keyBuffer);
|
||||
g_keyBuffer = g_list_remove_link(g_keyBuffer, theFirst);
|
||||
@@ -944,8 +943,10 @@ int gnome_nh_poskey(int *x, int *y, int *mod)
|
||||
|
||||
g_askingQuestion = 0;
|
||||
/* Process events until a key or map-click arrives. */
|
||||
while ( g_numKeys == 0 && g_numClicks == 0 )
|
||||
while ( g_numKeys == 0 && g_numClicks == 0 ) {
|
||||
if (program_state.done_hup) return '\033';
|
||||
gtk_main_iteration();
|
||||
}
|
||||
|
||||
if (g_numKeys > 0) {
|
||||
int key;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user