1
0
Fork 0
mirror of https://codeberg.org/noisytoot/notnotdnethack.git synced 2024-09-19 22:14:48 +01:00
notnotdnethack/win/tty/wintty.c
Ron Nazarov 43932afa77
Update copyright banner for notnotdNetHack
It now mentions both notdNetHack and dNetHack.  As a result, it's now
5 lines long.  Indentation level was halved (4 spaces -> 2 spaces) to
avoid it becoming too wide.
2024-05-12 12:54:05 +01:00

2631 lines
68 KiB
C

/* SCCS Id: @(#)wintty.c 3.4 2002/09/27 */
/* Copyright (c) David Cohrs, 1991 */
/* NetHack may be freely redistributed. See license for details. */
/*
* Neither a standard out nor character-based control codes should be
* part of the "tty look" windowing implementation.
* h+ 930227
*/
#include "hack.h"
#include "dlb.h"
#include "patchlevel.h"
#ifdef USE_TILES
extern short glyph2tile[];
#endif
#ifdef TTY_GRAPHICS
#include "tcap.h"
#include "wintty.h"
extern char mapped_menu_cmds[]; /* from options.c */
/* Interface definition, for windows.c */
struct window_procs tty_procs = {
"tty",
WC_COLOR|WC_HILITE_PET|WC_INVERSE|WC_EIGHT_BIT_IN,
WC2_DARKGRAY,
tty_init_nhwindows,
tty_player_selection,
tty_askname,
tty_get_nh_event,
tty_exit_nhwindows,
tty_suspend_nhwindows,
tty_resume_nhwindows,
tty_create_nhwindow,
tty_clear_nhwindow,
tty_display_nhwindow,
tty_destroy_nhwindow,
tty_curs,
tty_putstr,
tty_display_file,
tty_start_menu,
tty_add_menu,
tty_end_menu,
tty_select_menu,
tty_message_menu,
tty_update_inventory,
tty_mark_synch,
tty_wait_synch,
tty_cliparound,
#ifdef POSITIONBAR
tty_update_positionbar,
#endif
tty_print_glyph,
tty_raw_print,
tty_raw_print_bold,
tty_nhgetch,
tty_nh_poskey,
tty_nhbell,
tty_doprev_message,
tty_yn_function,
tty_getlin,
tty_get_ext_cmd,
tty_number_pad,
tty_delay_output,
#ifdef CHANGE_COLOR /* the Mac uses a palette device */
tty_change_color,
tty_get_color_string,
#endif
/* other defs that really should go away (they're tty specific) */
tty_start_screen,
tty_end_screen,
genl_outrip,
genl_preference_update,
};
static int maxwin = 0; /* number of windows in use */
winid BASE_WINDOW;
struct WinDesc *wins[MAXWIN];
struct DisplayDesc *ttyDisplay; /* the tty display descriptor */
extern void cmov(int,int); /* from termcap.c */
extern void nocmov(int,int); /* from termcap.c */
#if defined(UNIX)
static char obuf[BUFSIZ]; /* BUFSIZ is defined in stdio.h */
#endif
static char winpanicstr[] = "Bad window id %d";
char defmorestr[] = "--More--";
/** Track if the player is still selecting his character. */
boolean in_character_selection = FALSE;
extern struct menucoloring *menu_colorings;
static boolean clipping = FALSE; /* clipping on? */
static int clipx = 0, clipxmax = 0;
static int clipy = 0, clipymax = 0;
boolean GFlag = FALSE;
boolean HE_resets_AS; /* see termcap.c */
static void getret(void);
static void erase_menu_or_text(winid, struct WinDesc *, boolean);
static void free_window_info(struct WinDesc *, boolean);
static void dmore(struct WinDesc *, const char *);
static void set_item_state(winid, int, tty_menu_item *);
static void set_all_on_page(winid,tty_menu_item *,tty_menu_item *);
static void unset_all_on_page(winid,tty_menu_item *,tty_menu_item *);
static void invert_all_on_page(winid,tty_menu_item *,tty_menu_item *, char);
static void invert_all(winid,tty_menu_item *,tty_menu_item *, char);
static void process_menu_window(winid,struct WinDesc *);
static void process_text_window(winid,struct WinDesc *);
static tty_menu_item *reverse(tty_menu_item *);
const char * compress_str(const char *);
static void tty_putsym(winid, int, int, char);
static char *copy_of(const char *);
static void bail(const char *); /* __attribute__((noreturn)) */
/*
* A string containing all the default commands -- to add to a list
* of acceptable inputs.
*/
static const char default_menu_cmds[] = {
MENU_FIRST_PAGE,
MENU_LAST_PAGE,
MENU_NEXT_PAGE,
MENU_PREVIOUS_PAGE,
MENU_SELECT_ALL,
MENU_UNSELECT_ALL,
MENU_INVERT_ALL,
MENU_SELECT_PAGE,
MENU_UNSELECT_PAGE,
MENU_INVERT_PAGE,
0 /* null terminator */
};
#define TILE_ANSI_COMMAND 'z'
#define AVTC_GLYPH_START 0
#define AVTC_GLYPH_END 1
#define AVTC_SELECT_WINDOW 2
#define AVTC_INLINE_SYNC 3
#ifdef USE_TILES
int vt_tile_current_window = -2;
void
print_vt_code(int i, int c)
{
if (iflags.vt_nethack) {
if (c >= 0) {
if (i == AVTC_SELECT_WINDOW) {
if (c == vt_tile_current_window) return;
vt_tile_current_window = c;
}
printf("\033[%d;%d%c", i, c, TILE_ANSI_COMMAND);
} else {
printf("\033[%d%c", i, TILE_ANSI_COMMAND);
}
}
}
#else
# define print_vt_code(i, c) ;
# error no USE_TILES defined!
#endif /* USE_TILES */
/* clean up and quit */
static void
bail(const char *mesg)
{
clearlocks();
tty_exit_nhwindows(mesg);
terminate(EXIT_SUCCESS);
/*NOTREACHED*/
}
#ifdef SIGWINCH
static void
winch(int sig)
{
int oldLI = LI, oldCO = CO, i;
register struct WinDesc *cw;
getwindowsz();
if((oldLI != LI || oldCO != CO) && ttyDisplay) {
ttyDisplay->rows = LI;
ttyDisplay->cols = CO;
cw = wins[BASE_WINDOW];
cw->rows = ttyDisplay->rows;
cw->cols = ttyDisplay->cols;
if(iflags.window_inited) {
cw = wins[WIN_MESSAGE];
cw->curx = cw->cury = 0;
tty_destroy_nhwindow(WIN_STATUS);
WIN_STATUS = tty_create_nhwindow(NHW_STATUS);
if(u.ux) {
if(CO < COLNO || LI < ROWNO+3) {
setclipped();
tty_cliparound(u.ux, u.uy);
} else {
clipping = FALSE;
clipx = clipy = 0;
}
i = ttyDisplay->toplin;
ttyDisplay->toplin = 0;
docrt();
bot();
ttyDisplay->toplin = i;
flush_screen(1);
if(i) {
addtopl(toplines);
} else
for(i=WIN_INVEN; i < MAXWIN; i++)
if(wins[i] && wins[i]->active) {
/* cop-out */
addtopl("Press Return to continue: ");
break;
}
(void) fflush(stdout);
if(i < 2) flush_screen(1);
}
}
}
}
#endif
/*ARGSUSED*/
void
tty_init_nhwindows(int *argcp, char **argv)
{
int wid, hgt;
/*
* Remember tty modes, to be restored on exit.
*
* gettty() must be called before tty_startup()
* due to ordering of LI/CO settings
* tty_startup() must be called before initoptions()
* due to ordering of graphics settings
*/
#if defined(UNIX)
setbuf(stdout,obuf);
#endif
gettty();
/* to port dependant tty setup */
tty_startup(&wid, &hgt);
setftty(); /* calls start_screen */
/* set up tty descriptor */
ttyDisplay = (struct DisplayDesc*) alloc(sizeof(struct DisplayDesc));
ttyDisplay->toplin = 0;
ttyDisplay->rows = hgt;
ttyDisplay->cols = wid;
ttyDisplay->curx = ttyDisplay->cury = 0;
ttyDisplay->inmore = ttyDisplay->inread = ttyDisplay->intr = 0;
ttyDisplay->dismiss_more = 0;
ttyDisplay->color = NO_COLOR;
ttyDisplay->attrs = 0;
/* set up the default windows */
BASE_WINDOW = tty_create_nhwindow(NHW_BASE);
wins[BASE_WINDOW]->active = 1;
ttyDisplay->lastwin = WIN_ERR;
#ifdef SIGWINCH
(void) signal(SIGWINCH, winch);
#endif
/* add one a space forward menu command alias */
add_menu_cmd_alias(' ', MENU_NEXT_PAGE);
tty_clear_nhwindow(BASE_WINDOW);
tty_putstr(BASE_WINDOW, 0, "");
tty_putstr(BASE_WINDOW, 0, COPYRIGHT_BANNER_A);
tty_putstr(BASE_WINDOW, 0, COPYRIGHT_BANNER_B);
tty_putstr(BASE_WINDOW, 0, COPYRIGHT_BANNER_C);
tty_putstr(BASE_WINDOW, 0, COPYRIGHT_BANNER_D);
tty_putstr(BASE_WINDOW, 0, COPYRIGHT_BANNER_E);
tty_putstr(BASE_WINDOW, 0, "");
tty_display_nhwindow(BASE_WINDOW, FALSE);
}
void
tty_player_selection(void)
{
int i, k, n;
char pick4u = 'n', thisch, lastch = 0;
char pbuf[QBUFSZ], plbuf[QBUFSZ];
winid win;
anything any;
menu_item *selected = 0;
in_character_selection = TRUE;
/*Hackish solution to mysterious crashes*/
youmonst.data = &mons[PM_HUMAN];
/* prevent an unnecessary prompt */
rigid_role_checks();
/* Should we randomly pick for the player? */
if (!flags.randomall &&
(flags.initrole == ROLE_NONE || flags.initrace == ROLE_NONE ||
flags.initgend == ROLE_NONE ||
(flags.initalign == ROLE_NONE && !(flags.initrole != ROLE_NONE && roles[flags.initrole].malenum == PM_EXILE))
)) {
int echoline;
char *prompt = build_plselection_prompt(pbuf, QBUFSZ, flags.initrole,
flags.initrace, flags.descendant, flags.initgend, flags.initalign);
tty_putstr(BASE_WINDOW, 0, "");
echoline = wins[BASE_WINDOW]->cury;
tty_putstr(BASE_WINDOW, 0, prompt);
do {
pick4u = lowc(readchar());
if (index(quitchars, pick4u)) pick4u = 'y';
} while(!index(ynqchars, pick4u));
if ((int)strlen(prompt) + 1 < CO) {
/* Echo choice and move back down line */
tty_putsym(BASE_WINDOW, (int)strlen(prompt)+1, echoline, pick4u);
tty_putstr(BASE_WINDOW, 0, "");
} else
/* Otherwise it's hard to tell where to echo, and things are
* wrapping a bit messily anyway, so (try to) make sure the next
* question shows up well and doesn't get wrapped at the
* bottom of the window.
*/
tty_clear_nhwindow(BASE_WINDOW);
if (pick4u != 'y' && pick4u != 'n') {
give_up: /* Quit */
if (selected) free((void *) selected);
bail((char *)0);
/*NOTREACHED*/
return;
}
}
(void) root_plselection_prompt(plbuf, QBUFSZ - 1,
flags.initrole, flags.initrace, flags.descendant, flags.initgend, flags.initalign);
/* Select a role, if necessary */
/* we'll try to be compatible with pre-selected race/gender/alignment,
* but may not succeed */
if (flags.initrole < 0) {
char rolenamebuf[QBUFSZ];
/* Process the choice */
if (pick4u == 'y' || flags.initrole == ROLE_RANDOM || flags.randomall) {
/* Pick a random role */
flags.initrole = pick_role(flags.initrace, flags.initgend,
flags.initalign, PICK_RANDOM);
if (flags.initrole < 0) {
tty_putstr(BASE_WINDOW, 0, "Incompatible role!");
flags.initrole = randrole(0);
}
} else {
tty_clear_nhwindow(BASE_WINDOW);
tty_putstr(BASE_WINDOW, 0, "Choosing Character's Role");
/* Prompt for a role */
win = create_nhwindow(NHW_MENU);
start_menu(win);
any.a_void = 0; /* zero out all bits */
for (i = 0; roles[i].name.m; i++) {
if (ok_role(i, flags.initrace, flags.initgend,
flags.initalign)) {
any.a_int = i+1; /* must be non-zero */
thisch = lowc(roles[i].name.m[0]);
if (thisch == lastch) thisch = highc(thisch);
if (flags.initgend != ROLE_NONE && flags.initgend != ROLE_RANDOM) {
if (flags.initgend == 1 && roles[i].name.f)
Strcpy(rolenamebuf, roles[i].name.f);
else
Strcpy(rolenamebuf, roles[i].name.m);
} else {
if (roles[i].name.f) {
Strcpy(rolenamebuf, roles[i].name.m);
Strcat(rolenamebuf, "/");
Strcat(rolenamebuf, roles[i].name.f);
} else
Strcpy(rolenamebuf, roles[i].name.m);
}
add_menu(win, NO_GLYPH, &any, thisch,
0, ATR_NONE, an(rolenamebuf), MENU_UNSELECTED);
lastch = thisch;
}
}
any.a_int = pick_role(flags.initrace, flags.initgend,
flags.initalign, PICK_RANDOM)+1;
if (any.a_int == 0) /* must be non-zero */
any.a_int = randrole(0)+1;
add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE,
"Random", MENU_UNSELECTED);
any.a_int = i+1; /* must be non-zero */
add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE,
"Quit", MENU_UNSELECTED);
Sprintf(pbuf, "Pick a role for your %s", plbuf);
end_menu(win, pbuf);
n = select_menu(win, PICK_ONE, &selected);
destroy_nhwindow(win);
/* Process the choice */
if (n != 1 || selected[0].item.a_int == any.a_int)
goto give_up; /* Selected quit */
flags.initrole = selected[0].item.a_int - 1;
free((void *) selected), selected = 0;
}
(void) root_plselection_prompt(plbuf, QBUFSZ - 1,
flags.initrole, flags.initrace, flags.descendant, flags.initgend, flags.initalign);
}
/* Select a race, if necessary */
/* force compatibility with role, try for compatibility with
* pre-selected gender/alignment */
if (flags.initrace < 0 || !validrace(flags.initrole, flags.initrace)) {
/* pre-selected race not valid */
if (pick4u == 'y' || flags.initrace == ROLE_RANDOM || flags.randomall) {
flags.initrace = pick_race(flags.initrole, flags.initgend,
flags.initalign, PICK_RANDOM);
if (flags.initrace < 0) {
tty_putstr(BASE_WINDOW, 0, "Incompatible race!");
flags.initrace = randrace(flags.initrole);
}
} else { /* pick4u == 'n' */
/* Count the number of valid races */
n = 0; /* number valid */
k = 0; /* valid race */
for (i = 0; races[i].noun; i++) {
if (ok_race(flags.initrole, i, flags.initgend,
flags.initalign)) {
n++;
k = i;
}
}
if (n == 0) {
for (i = 0; races[i].noun; i++) {
if (validrace(flags.initrole, i)) {
n++;
k = i;
}
}
}
/* Permit the user to pick, if there is more than one */
if (n > 1) {
tty_clear_nhwindow(BASE_WINDOW);
tty_putstr(BASE_WINDOW, 0, "Choosing Race");
win = create_nhwindow(NHW_MENU);
start_menu(win);
any.a_void = 0; /* zero out all bits */
for (i = 0; races[i].noun; i++)
if (ok_race(flags.initrole, i, flags.initgend,
flags.initalign)) {
any.a_int = i+1; /* must be non-zero */
char selector = races[i].noun[0];
add_menu(win, NO_GLYPH, &any, selector,
0, ATR_NONE, races[i].noun, MENU_UNSELECTED);
}
any.a_int = pick_race(flags.initrole, flags.initgend,
flags.initalign, PICK_RANDOM)+1;
if (any.a_int == 0) /* must be non-zero */
any.a_int = randrace(flags.initrole)+1;
add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE,
"Random", MENU_UNSELECTED);
any.a_int = i+1; /* must be non-zero */
add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE,
"Quit", MENU_UNSELECTED);
Sprintf(pbuf, "Pick the race of your %s", plbuf);
end_menu(win, pbuf);
n = select_menu(win, PICK_ONE, &selected);
destroy_nhwindow(win);
if (n != 1 || selected[0].item.a_int == any.a_int)
goto give_up; /* Selected quit */
k = selected[0].item.a_int - 1;
free((void *) selected), selected = 0;
}
flags.initrace = k;
}
(void) root_plselection_prompt(plbuf, QBUFSZ - 1,
flags.initrole, flags.initrace, flags.descendant, flags.initgend, flags.initalign);
}
/* Select a gender, if necessary */
/* force compatibility with role/race, try for compatibility with
* pre-selected alignment */
if (flags.initgend < 0 || !validgend(flags.initrole, flags.initrace,
flags.initgend)) {
/* pre-selected gender not valid */
if (pick4u == 'y' || flags.initgend == ROLE_RANDOM || flags.randomall) {
flags.initgend = pick_gend(flags.initrole, flags.initrace,
flags.initalign, PICK_RANDOM);
if (flags.initgend < 0) {
tty_putstr(BASE_WINDOW, 0, "Incompatible gender!");
flags.initgend = randgend(flags.initrole, flags.initrace);
}
} else { /* pick4u == 'n' */
/* Count the number of valid genders */
n = 0; /* number valid */
k = 0; /* valid gender */
for (i = 0; i < ROLE_GENDERS; i++) {
if (ok_gend(flags.initrole, flags.initrace, i,
flags.initalign)) {
n++;
k = i;
}
}
if (n == 0) {
for (i = 0; i < ROLE_GENDERS; i++) {
if (validgend(flags.initrole, flags.initrace, i)) {
n++;
k = i;
}
}
}
/* Permit the user to pick, if there is more than one */
if (n > 1) {
tty_clear_nhwindow(BASE_WINDOW);
tty_putstr(BASE_WINDOW, 0, "Choosing Gender");
win = create_nhwindow(NHW_MENU);
start_menu(win);
any.a_void = 0; /* zero out all bits */
for (i = 0; i < ROLE_GENDERS; i++)
if (ok_gend(flags.initrole, flags.initrace, i,
flags.initalign)) {
any.a_int = i+1;
add_menu(win, NO_GLYPH, &any, genders[i].adj[0],
0, ATR_NONE, genders[i].adj, MENU_UNSELECTED);
}
any.a_int = pick_gend(flags.initrole, flags.initrace,
flags.initalign, PICK_RANDOM)+1;
if (any.a_int == 0) /* must be non-zero */
any.a_int = randgend(flags.initrole, flags.initrace)+1;
add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE,
"Random", MENU_UNSELECTED);
any.a_int = i+1; /* must be non-zero */
add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE,
"Quit", MENU_UNSELECTED);
Sprintf(pbuf, "Pick the gender of your %s", plbuf);
end_menu(win, pbuf);
n = select_menu(win, PICK_ONE, &selected);
destroy_nhwindow(win);
if (n != 1 || selected[0].item.a_int == any.a_int)
goto give_up; /* Selected quit */
k = selected[0].item.a_int - 1;
free((void *) selected), selected = 0;
}
flags.initgend = k;
}
(void) root_plselection_prompt(plbuf, QBUFSZ - 1,
flags.initrole, flags.initrace, flags.descendant, flags.initgend, flags.initalign);
}
/* Select an alignment, if necessary */
/* force compatibility with role/race/gender */
if (flags.initalign < 0 || !validalign(flags.initrole, flags.initrace,
flags.initalign)) {
/* pre-selected alignment not valid */
if (pick4u == 'y' || flags.initalign == ROLE_RANDOM || flags.randomall ||
(flags.initrole != ROLE_NONE && roles[flags.initrole].malenum == PM_EXILE)
) {
flags.initalign = pick_align(flags.initrole, flags.initrace,
flags.initgend, PICK_RANDOM);
if (flags.initalign < 0) {
tty_putstr(BASE_WINDOW, 0, "Incompatible alignment!");
flags.initalign = randalign(flags.initrole, flags.initrace);
}
} else { /* pick4u == 'n' */
/* Count the number of valid alignments */
n = 0; /* number valid */
k = 0; /* valid alignment */
for (i = 0; i < ROLE_ALIGNS; i++) {
if (ok_align(flags.initrole, flags.initrace, flags.initgend,
i)) {
n++;
k = i;
}
}
if (n == 0) {
for (i = 0; i < ROLE_ALIGNS; i++) {
if (validalign(flags.initrole, flags.initrace, i)) {
n++;
k = i;
}
}
}
/* Permit the user to pick, if there is more than one */
if (n > 1) {
tty_clear_nhwindow(BASE_WINDOW);
tty_putstr(BASE_WINDOW, 0, "Choosing Alignment");
win = create_nhwindow(NHW_MENU);
start_menu(win);
any.a_void = 0; /* zero out all bits */
for (i = 0; i < ROLE_ALIGNS; i++)
if (ok_align(flags.initrole, flags.initrace,
flags.initgend, i)) {
any.a_int = i+1;
add_menu(win, NO_GLYPH, &any, aligns[i].adj[0],
0, ATR_NONE, aligns[i].adj, MENU_UNSELECTED);
}
any.a_int = pick_align(flags.initrole, flags.initrace,
flags.initgend, PICK_RANDOM)+1;
if (any.a_int == 0) /* must be non-zero */
any.a_int = randalign(flags.initrole, flags.initrace)+1;
add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE,
"Random", MENU_UNSELECTED);
any.a_int = i+1; /* must be non-zero */
add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE,
"Quit", MENU_UNSELECTED);
Sprintf(pbuf, "Pick the alignment of your %s", plbuf);
end_menu(win, pbuf);
n = select_menu(win, PICK_ONE, &selected);
destroy_nhwindow(win);
if (n != 1 || selected[0].item.a_int == any.a_int)
goto give_up; /* Selected quit */
k = selected[0].item.a_int - 1;
free((void *) selected), selected = 0;
}
flags.initalign = k;
}
}
/* Select a species, if necessary */
/* force compatibility with role/race/gender/align */
if (flags.initspecies < 0 || !validspecies(flags.initrole, flags.initrace,
flags.initgend, flags.initspecies)) {
/* pre-selected species not valid */
if (pick4u == 'y' || flags.initspecies == ROLE_RANDOM || flags.randomall
) {
flags.initspecies = pick_species(flags.initrole, flags.initrace,
flags.initgend, PICK_RANDOM);
if (flags.initspecies < 0) {
tty_putstr(BASE_WINDOW, 0, "Incompatible species!");
flags.initspecies = randspecies(flags.initrole, flags.initrace, flags.initgend);
}
} else { /* pick4u == 'n' */
/* Count the number of valid species */
n = 0; /* number valid */
k = 0; /* valid species */
for (i = 0; i < ROLE_SPECIES; i++) {
if (validspecies(flags.initrole, flags.initrace, flags.initgend,
i)) {
n++;
k = i;
}
}
if (n == 0) {
for (i = 0; i < ROLE_SPECIES; i++) {
if (validspecies(flags.initrole, flags.initrace, flags.initgend, i)) {
n++;
k = i;
}
}
}
/* Permit the user to pick, if there is more than one */
if (n > 1) {
tty_clear_nhwindow(BASE_WINDOW);
tty_putstr(BASE_WINDOW, 0, "Choosing Species");
win = create_nhwindow(NHW_MENU);
start_menu(win);
any.a_void = 0; /* zero out all bits */
int valid_count = 0;
for (i = 0; i < ROLE_SPECIES; i++)
if (validspecies(flags.initrole, flags.initrace,
flags.initgend, i)) {
any.a_int = i+1;
add_menu(win, NO_GLYPH, &any, 'a' + valid_count,
0, ATR_NONE, species[i].name, MENU_UNSELECTED);
valid_count++;
}
any.a_int = pick_species(flags.initrole, flags.initrace,
flags.initgend, PICK_RANDOM)+1;
if (any.a_int == 0) /* must be non-zero */
any.a_int = randspecies(flags.initrole, flags.initrace, flags.initgend)+1;
add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE,
"Random", MENU_UNSELECTED);
any.a_int = i+1; /* must be non-zero */
add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE,
"Quit", MENU_UNSELECTED);
Sprintf(pbuf, "Pick the species of your %s", plbuf);
end_menu(win, pbuf);
n = select_menu(win, PICK_ONE, &selected);
destroy_nhwindow(win);
if (n != 1 || selected[0].item.a_int == any.a_int)
goto give_up; /* Selected quit */
k = selected[0].item.a_int - 1;
free((void *) selected), selected = 0;
}
flags.initspecies = k;
}
}
/* Select descendant status, if necessary */
if (flags.descendant < 0) {
if (pick4u == 'y' || flags.descendant == ROLE_RANDOM || flags.randomall || flags.initrole < 0 || !validdescendant(flags.initrole)) {
flags.descendant = 0; // never randomly roll descendant
} else { /* pick4u == 'n' */
tty_clear_nhwindow(BASE_WINDOW);
tty_putstr(BASE_WINDOW, 0, "Choosing inheritance");
win = create_nhwindow(NHW_MENU);
start_menu(win);
any.a_void = 0; /* zero out all bits */
any.a_int = 2;
add_menu(win, NO_GLYPH, &any , 'y', 0, ATR_NONE,
"Inherit from a past adventurer (start with an heirloom artifact but low stats and dangerous foes)", MENU_UNSELECTED);
any.a_int = 1;
add_menu(win, NO_GLYPH, &any , 'n', 0, ATR_NONE, "No past inheritance", MENU_UNSELECTED);
any.a_int = rn2(2)+1;
add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE, "Random", MENU_UNSELECTED);
any.a_int = 4;
add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE, "Quit", MENU_UNSELECTED);
Sprintf(pbuf, "Pick the inheritance of your %s", plbuf);
end_menu(win, pbuf);
n = select_menu(win, PICK_ONE, &selected);
destroy_nhwindow(win);
if (n != 1 || selected[0].item.a_int == any.a_int)
goto give_up; /* Selected quit */
k = selected[0].item.a_int - 1;
free((void *) selected), selected = 0;
flags.descendant = k;
}
(void) root_plselection_prompt(plbuf, QBUFSZ - 1,
flags.initrole, flags.initrace, flags.descendant, flags.initgend, flags.initalign);
}
/* Success! */
in_character_selection = FALSE;
tty_display_nhwindow(BASE_WINDOW, FALSE);
}
/*
* plname is filled either by an option (-u Player or -uPlayer) or
* explicitly (by being the wizard) or by askname.
* It may still contain a suffix denoting the role, etc.
* Always called after init_nhwindows() and before display_gamewindows().
*/
void
tty_askname(void)
{
static char who_are_you[] = "Who are you? ";
register int c, ct, tryct = 0;
tty_putstr(BASE_WINDOW, 0, "");
do {
if (++tryct > 1) {
if (tryct > 10) bail("Giving up after 10 tries.\n");
tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury - 1);
tty_putstr(BASE_WINDOW, 0, "Enter a name for your character...");
/* erase previous prompt (in case of ESC after partial response) */
tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury), cl_end();
}
tty_putstr(BASE_WINDOW, 0, who_are_you);
tty_curs(BASE_WINDOW, (int)(sizeof who_are_you),
wins[BASE_WINDOW]->cury - 1);
ct = 0;
while((c = tty_nhgetch()) != '\n') {
if(c == EOF) error("End of input\n");
if (c == DOESCAPE) { ct = 0; break; } /* continue outer loop */
/* some people get confused when their erase char is not ^H */
if (c == '\b' || c == '\177') {
if(ct) {
ct--;
(void) putchar('\b');
(void) putchar(' ');
(void) putchar('\b');
}
continue;
}
#if defined(UNIX)
if(c != '-' && c != '@')
if(c < 'A' || (c > 'Z' && c < 'a') || c > 'z') c = '_';
#endif
if (ct < (int)(sizeof plname) - 1) {
(void) putchar(c);
plname[ct++] = c;
}
}
plname[ct] = 0;
} while (ct == 0);
/* move to next line to simulate echo of user's <return> */
tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury + 1);
}
void
tty_get_nh_event(void)
{
return;
}
static void
getret(void)
{
xputs("\n");
if(flags.standout)
standoutbeg();
xputs("Hit ");
xputs(iflags.cbreak ? "space" : "return");
xputs(" to continue: ");
if(flags.standout)
standoutend();
xwaitforspace(" ");
}
void
tty_suspend_nhwindows(const char *str)
{
settty(str); /* calls end_screen, perhaps raw_print */
if (!str) tty_raw_print(""); /* calls fflush(stdout) */
}
void
tty_resume_nhwindows(void)
{
gettty();
setftty(); /* calls start_screen */
docrt();
}
void
tty_exit_nhwindows(const char *str)
{
winid i;
tty_suspend_nhwindows(str);
/* Just forget any windows existed, since we're about to exit anyway.
* Disable windows to avoid calls to window routines.
*/
for(i=0; i<MAXWIN; i++)
if (wins[i] && (i != BASE_WINDOW)) {
#ifdef FREE_ALL_MEMORY
free_window_info(wins[i], TRUE);
free((void *) wins[i]);
#endif
wins[i] = 0;
}
tty_shutdown(); /* cleanup termcap/terminfo/whatever */
iflags.window_inited = 0;
}
winid
tty_create_nhwindow(int type)
{
struct WinDesc* newwin;
int i;
int newid;
if(maxwin == MAXWIN)
return WIN_ERR;
newwin = (struct WinDesc*) alloc(sizeof(struct WinDesc));
newwin->type = type;
newwin->flags = 0;
newwin->active = FALSE;
newwin->curx = newwin->cury = 0;
newwin->morestr = 0;
newwin->mlist = (tty_menu_item *) 0;
newwin->plist = (tty_menu_item **) 0;
newwin->npages = newwin->plist_size = newwin->nitems = newwin->how = 0;
switch(type) {
case NHW_BASE:
/* base window, used for absolute movement on the screen */
newwin->offx = newwin->offy = 0;
newwin->rows = ttyDisplay->rows;
newwin->cols = ttyDisplay->cols;
newwin->maxrow = newwin->maxcol = 0;
break;
case NHW_MESSAGE:
/* message window, 1 line long, very wide, top of screen */
newwin->offx = newwin->offy = 0;
/* sanity check */
if(iflags.msg_history < 20) iflags.msg_history = 20;
else if(iflags.msg_history > 400) iflags.msg_history = 400;
newwin->maxrow = newwin->rows = iflags.msg_history;
newwin->maxcol = newwin->cols = 0;
break;
case NHW_STATUS:
/* status window, 3 lines long, full width, bottom of screen */
newwin->offx = 0;
newwin->offy = min((int)ttyDisplay->rows-2, ROWNO+1);
newwin->rows = newwin->maxrow = 3;
newwin->cols = newwin->maxcol = min(ttyDisplay->cols, MAXCO);
break;
case NHW_MAP:
/* map window, ROWNO lines long, full width, below message window */
newwin->offx = 0;
newwin->offy = 1;
newwin->rows = ROWNO;
newwin->cols = COLNO;
newwin->maxrow = 0; /* no buffering done -- let gbuf do it */
newwin->maxcol = 0;
break;
case NHW_MENU:
case NHW_TEXT:
/* inventory/menu window, variable length, full width, top of screen */
/* help window, the same, different semantics for display, etc */
newwin->offx = newwin->offy = 0;
newwin->rows = 0;
newwin->cols = ttyDisplay->cols;
newwin->maxrow = newwin->maxcol = 0;
break;
default:
panic("Tried to create window type %d\n", (int) type);
return WIN_ERR;
}
for(newid = 0; newid<MAXWIN; newid++) {
if(wins[newid] == 0) {
wins[newid] = newwin;
break;
}
}
if(newid == MAXWIN) {
panic("No window slots!");
return WIN_ERR;
}
if(newwin->maxrow) {
newwin->data =
(char **) alloc(sizeof(char *) * (unsigned)newwin->maxrow);
newwin->datlen =
(short *) alloc(sizeof(short) * (unsigned)newwin->maxrow);
if(newwin->maxcol) {
for (i = 0; i < newwin->maxrow; i++) {
newwin->data[i] = (char *) alloc((unsigned)newwin->maxcol);
newwin->datlen[i] = newwin->maxcol;
}
} else {
for (i = 0; i < newwin->maxrow; i++) {
newwin->data[i] = (char *) 0;
newwin->datlen[i] = 0;
}
}
if(newwin->type == NHW_MESSAGE)
newwin->maxrow = 0;
} else {
newwin->data = (char **)0;
newwin->datlen = (short *)0;
}
return newid;
}
static void
erase_menu_or_text(winid window, struct WinDesc *cw, boolean clear)
{
if(cw->offx == 0)
if(cw->offy) {
tty_curs(window, 1, 0);
cl_eos();
} else if (clear)
clear_screen();
else
docrt();
else
docorner((int)cw->offx, cw->maxrow+1);
}
static void
free_window_info(struct WinDesc *cw, boolean free_data)
{
int i;
if (cw->data) {
if (WIN_MESSAGE != WIN_ERR && cw == wins[WIN_MESSAGE] && cw->rows > cw->maxrow)
cw->maxrow = cw->rows; /* topl data */
for(i=0; i<cw->maxrow; i++)
if(cw->data[i]) {
free((void *)cw->data[i]);
cw->data[i] = (char *)0;
if (cw->datlen) cw->datlen[i] = 0;
}
if (free_data) {
free((void *)cw->data);
cw->data = (char **)0;
if (cw->datlen) free((void *)cw->datlen);
cw->datlen = (short *)0;
cw->rows = 0;
}
}
cw->maxrow = cw->maxcol = 0;
if(cw->mlist) {
tty_menu_item *temp;
while ((temp = cw->mlist) != 0) {
cw->mlist = cw->mlist->next;
if (temp->str) free((void *)temp->str);
free((void *)temp);
}
}
if (cw->plist) {
free((void *)cw->plist);
cw->plist = 0;
}
cw->plist_size = cw->npages = cw->nitems = cw->how = 0;
if(cw->morestr) {
free((void *)cw->morestr);
cw->morestr = 0;
}
}
void
tty_clear_nhwindow(winid window)
{
register struct WinDesc *cw = 0;
if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
panic(winpanicstr, window);
ttyDisplay->lastwin = window;
print_vt_code(AVTC_SELECT_WINDOW, window);
switch(cw->type) {
case NHW_MESSAGE:
if(ttyDisplay->toplin) {
home();
cl_end();
if(cw->cury)
docorner(1, cw->cury+1);
ttyDisplay->toplin = 0;
}
break;
case NHW_STATUS:
tty_curs(window, 1, 0);
cl_end();
tty_curs(window, 1, 1);
cl_end();
break;
case NHW_MAP:
/* cheap -- clear the whole thing and tell nethack to redraw botl */
flags.botlx = 1;
/* fall into ... */
case NHW_BASE:
clear_screen();
break;
case NHW_MENU:
case NHW_TEXT:
if(cw->active)
erase_menu_or_text(window, cw, TRUE);
free_window_info(cw, FALSE);
break;
}
cw->curx = cw->cury = 0;
}
static void
dmore(struct WinDesc *cw, const char *s) /* s = valid responses */
{
const char *prompt = cw->morestr ? cw->morestr : defmorestr;
int offset = (cw->type == NHW_TEXT) ? 1 : 2;
tty_curs(BASE_WINDOW,
(int)ttyDisplay->curx + offset, (int)ttyDisplay->cury);
if(flags.standout)
standoutbeg();
xputs(prompt);
ttyDisplay->curx += strlen(prompt);
if(flags.standout)
standoutend();
xwaitforspace(s);
}
static void
set_item_state(winid window, int lineno, tty_menu_item *item)
{
char ch = item->selected ? (item->count == -1L ? '+' : '#') : '-';
tty_curs(window, 4, lineno);
term_start_attr(item->attr);
(void) putchar(ch);
ttyDisplay->curx++;
term_end_attr(item->attr);
}
static void
set_all_on_page(winid window, tty_menu_item *page_start, tty_menu_item *page_end)
{
tty_menu_item *curr;
int n;
for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
if (curr->identifier.a_void && !curr->selected) {
curr->selected = TRUE;
set_item_state(window, n, curr);
}
}
static void
unset_all_on_page(winid window, tty_menu_item *page_start, tty_menu_item *page_end)
{
tty_menu_item *curr;
int n;
for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
if (curr->identifier.a_void && curr->selected) {
curr->selected = FALSE;
curr->count = -1L;
set_item_state(window, n, curr);
}
}
static void
invert_all_on_page(winid window, tty_menu_item *page_start, tty_menu_item *page_end, char acc)
/* acc = group accelerator, 0 => all */
{
tty_menu_item *curr;
int n;
for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
if (curr->identifier.a_void && (acc == 0 || curr->gselector == acc)) {
if (curr->selected) {
curr->selected = FALSE;
curr->count = -1L;
} else
curr->selected = TRUE;
set_item_state(window, n, curr);
}
}
/*
* Invert all entries that match the give group accelerator (or all if
* zero).
*/
static void
invert_all(winid window, tty_menu_item *page_start, tty_menu_item *page_end, char acc)
/* acc = group accelerator, 0 => all */
{
tty_menu_item *curr;
boolean on_curr_page;
struct WinDesc *cw = wins[window];
invert_all_on_page(window, page_start, page_end, acc);
/* invert the rest */
for (on_curr_page = FALSE, curr = cw->mlist; curr; curr = curr->next) {
if (curr == page_start)
on_curr_page = TRUE;
else if (curr == page_end)
on_curr_page = FALSE;
if (!on_curr_page && curr->identifier.a_void
&& (acc == 0 || curr->gselector == acc)) {
if (curr->selected) {
curr->selected = FALSE;
curr->count = -1;
} else
curr->selected = TRUE;
}
}
}
static boolean
get_menu_coloring(char *str, int *color, int *attr)
{
struct menucoloring *tmpmc;
if (iflags.use_menu_color)
for (tmpmc = menu_colorings; tmpmc; tmpmc = tmpmc->next)
if (tmpmc->is_regexp
? regexec(&tmpmc->match, str, 0, NULL, 0) == 0
: pmatch(tmpmc->pattern, str)) {
*color = tmpmc->color;
*attr = tmpmc->attr;
return TRUE;
}
return FALSE;
}
static void
process_menu_window(winid window, struct WinDesc *cw)
{
tty_menu_item *page_start, *page_end, *curr;
long count;
int n, curr_page, page_lines;
boolean finished, counting, reset_count;
char *cp, *rp, resp[QBUFSZ], gacc[QBUFSZ],
*msave, *morestr;
curr_page = page_lines = 0;
page_start = page_end = 0;
msave = cw->morestr; /* save the morestr */
cw->morestr = morestr = (char*) alloc((unsigned) QBUFSZ);
counting = FALSE;
count = 0L;
reset_count = TRUE;
finished = FALSE;
/* collect group accelerators; for PICK_NONE, they're ignored;
for PICK_ONE, only those which match exactly one entry will be
accepted; for PICK_ANY, those which match any entry are okay */
gacc[0] = '\0';
if (cw->how != PICK_NONE) {
int i, gcnt[128];
#define GSELIDX(c) (c & 127) /* guard against `signed char' */
for (i = 0; i < SIZE(gcnt); i++) gcnt[i] = 0;
for (n = 0, curr = cw->mlist; curr; curr = curr->next)
if (curr->gselector && curr->gselector != curr->selector) {
++n;
++gcnt[GSELIDX(curr->gselector)];
}
if (n > 0) /* at least one group accelerator found */
for (rp = gacc, curr = cw->mlist; curr; curr = curr->next)
if (curr->gselector && !index(gacc, curr->gselector) &&
(cw->how == PICK_ANY ||
gcnt[GSELIDX(curr->gselector)] == 1)) {
*rp++ = curr->gselector;
*rp = '\0'; /* re-terminate for index() */
}
}
/* loop until finished */
while (!finished) {
if (reset_count) {
counting = FALSE;
count = 0;
} else
reset_count = TRUE;
if (!page_start) {
/* new page to be displayed */
if (curr_page < 0 || (cw->npages > 0 && curr_page >= cw->npages))
panic("bad menu screen page #%d", curr_page);
/* clear screen */
if (!cw->offx) { /* if not corner, do clearscreen */
if(cw->offy) {
tty_curs(window, 1, 0);
cl_eos();
} else
clear_screen();
}
rp = resp;
if (cw->npages > 0) {
/* collect accelerators */
page_start = cw->plist[curr_page];
page_end = cw->plist[curr_page + 1];
for (page_lines = 0, curr = page_start;
curr != page_end;
page_lines++, curr = curr->next) {
int color = NO_COLOR, attr = ATR_NONE;
boolean menucolr = FALSE;
if (curr->selector)
*rp++ = curr->selector;
tty_curs(window, 1, page_lines);
if (cw->offx) cl_end();
(void) putchar(' ');
++ttyDisplay->curx;
/*
* Don't use xputs() because (1) under unix it calls
* tputstr() which will interpret a '*' as some kind
* of padding information and (2) it calls xputc to
* actually output the character. We're faster doing
* this.
*/
/* add selector for display */
if (curr->selector) {
/* because WIN32CON this must be done in
* a brain-dead way */
putchar(curr->selector); ttyDisplay->curx++;
putchar(' '); ttyDisplay->curx++;
/* set item state */
if (curr->identifier.a_void != 0 && curr->selected) {
if (curr->count == -1L)
(void) putchar('+'); /* all selected */
else
(void) putchar('#'); /* count selected */
} else {
putchar('-');
}
ttyDisplay->curx++;
putchar(' '); ttyDisplay->curx++;
}
if (curr->glyph != NO_GLYPH && iflags.use_menu_glyphs) {
int glyph_color = NO_COLOR;
glyph_t character;
unsigned bgcolor; /* unused */
/* map glyph to character and color */
mapglyph(curr->glyph, &character, &glyph_color, &bgcolor, 0, 0);
print_vt_code(AVTC_GLYPH_START, glyph2tile[curr->glyph]);
if (glyph_color != NO_COLOR) term_start_color(glyph_color);
pututf8char(character);
if (glyph_color != NO_COLOR) term_end_color();
print_vt_code(AVTC_GLYPH_END, -1);
putchar(' ');
ttyDisplay->curx +=2;
}
if (iflags.use_menu_color &&
(menucolr = get_menu_coloring(curr->str, &color,&attr))) {
term_start_attr(attr);
if (color != NO_COLOR) term_start_color(color);
} else
term_start_attr(curr->attr);
for (n = 0, cp = curr->str;
*cp && (int) ++ttyDisplay->curx < (int) ttyDisplay->cols;
cp++, n++)
(void) pututf8char(*cp);
if (iflags.use_menu_color && menucolr) {
if (color != NO_COLOR) term_end_color();
term_end_attr(attr);
} else
term_end_attr(curr->attr);
}
} else {
page_start = 0;
page_end = 0;
page_lines = 0;
}
*rp = 0;
/* corner window - clear extra lines from last page */
if (cw->offx) {
for (n = page_lines + 1; n < cw->maxrow; n++) {
tty_curs(window, 1, n);
cl_end();
}
}
/* set extra chars.. */
Strcat(resp, default_menu_cmds);
Strcat(resp, "0123456789\033\n\r"); /* counts, quit */
Strcat(resp, gacc); /* group accelerators */
Strcat(resp, mapped_menu_cmds);
if (cw->npages > 1)
Sprintf(cw->morestr, "(%d of %d)",
curr_page + 1, (int) cw->npages);
else if (msave)
Strcpy(cw->morestr, msave);
else
Strcpy(cw->morestr, defmorestr);
tty_curs(window, 1, page_lines);
cl_end();
dmore(cw, resp);
} else {
/* just put the cursor back... */
tty_curs(window, (int) strlen(cw->morestr) + 2, page_lines);
xwaitforspace(resp);
}
morc = map_menu_cmd(morc);
switch (morc) {
case '0':
/* special case: '0' is also the default ball class */
if (!counting && index(gacc, morc)) goto group_accel;
/* fall through to count the zero */
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
count = (count * 10L) + (long) (morc - '0');
/*
* It is debatable whether we should allow 0 to
* start a count. There is no difference if the
* item is selected. If not selected, then
* "0b" could mean:
*
* count starting zero: "zero b's"
* ignore starting zero: "select b"
*
* At present I don't know which is better.
*/
if (count != 0L) { /* ignore leading zeros */
counting = TRUE;
reset_count = FALSE;
}
break;
case '\033': /* cancel - from counting or loop */
if (!counting) {
/* deselect everything */
for (curr = cw->mlist; curr; curr = curr->next) {
curr->selected = FALSE;
curr->count = -1L;
}
cw->flags |= WIN_CANCELLED;
finished = TRUE;
}
/* else only stop count */
break;
case '\0': /* finished (commit) */
case '\n':
case '\r':
/* only finished if we are actually picking something */
if (cw->how != PICK_NONE) {
finished = TRUE;
break;
}
/* else fall through */
case MENU_NEXT_PAGE:
if (cw->npages > 0 && curr_page != cw->npages - 1) {
curr_page++;
page_start = 0;
} else
finished = TRUE; /* questionable behavior */
break;
case MENU_PREVIOUS_PAGE:
if (cw->npages > 0 && curr_page != 0) {
--curr_page;
page_start = 0;
}
break;
case MENU_FIRST_PAGE:
if (cw->npages > 0 && curr_page != 0) {
page_start = 0;
curr_page = 0;
}
break;
case MENU_LAST_PAGE:
if (cw->npages > 0 && curr_page != cw->npages - 1) {
page_start = 0;
curr_page = cw->npages - 1;
}
break;
case MENU_SELECT_PAGE:
if (cw->how == PICK_ANY)
set_all_on_page(window, page_start, page_end);
break;
case MENU_UNSELECT_PAGE:
unset_all_on_page(window, page_start, page_end);
break;
case MENU_INVERT_PAGE:
if (cw->how == PICK_ANY)
invert_all_on_page(window, page_start, page_end, 0);
break;
case MENU_SELECT_ALL:
if (cw->how == PICK_ANY) {
set_all_on_page(window, page_start, page_end);
/* set the rest */
for (curr = cw->mlist; curr; curr = curr->next)
if (curr->identifier.a_void && !curr->selected)
curr->selected = TRUE;
}
break;
case MENU_UNSELECT_ALL:
unset_all_on_page(window, page_start, page_end);
/* unset the rest */
for (curr = cw->mlist; curr; curr = curr->next)
if (curr->identifier.a_void && curr->selected) {
curr->selected = FALSE;
curr->count = -1;
}
break;
case MENU_INVERT_ALL:
if (cw->how == PICK_ANY)
invert_all(window, page_start, page_end, 0);
break;
default:
if (cw->how == PICK_NONE || !index(resp, morc)) {
/* unacceptable input received */
tty_nhbell();
break;
} else if (index(gacc, morc)) {
group_accel:
/* group accelerator; for the PICK_ONE case, we know that
it matches exactly one item in order to be in gacc[] */
invert_all(window, page_start, page_end, morc);
if (cw->how == PICK_ONE) finished = TRUE;
break;
}
/* find, toggle, and possibly update */
for (n = 0, curr = page_start;
curr != page_end;
n++, curr = curr->next)
if (morc == curr->selector) {
if (curr->selected) {
if (counting && count > 0) {
curr->count = count;
set_item_state(window, n, curr);
} else { /* change state */
curr->selected = FALSE;
curr->count = -1L;
set_item_state(window, n, curr);
}
} else { /* !selected */
if (counting && count > 0) {
curr->count = count;
curr->selected = TRUE;
set_item_state(window, n, curr);
} else if (!counting) {
curr->selected = TRUE;
set_item_state(window, n, curr);
}
/* do nothing counting&&count==0 */
}
if (cw->how == PICK_ONE) finished = TRUE;
break; /* from `for' loop */
}
break;
}
} /* while */
cw->morestr = msave;
free((void *)morestr);
}
static void
process_text_window(winid window, struct WinDesc *cw)
{
int i, n, attr;
register char *cp;
for (n = 0, i = 0; i < cw->maxrow; i++) {
if (!cw->offx && (n + cw->offy == ttyDisplay->rows - 1)) {
tty_curs(window, 1, n);
cl_end();
dmore(cw, quitchars);
if (morc == DOESCAPE) {
cw->flags |= WIN_CANCELLED;
break;
}
if (cw->offy) {
tty_curs(window, 1, 0);
cl_eos();
} else
clear_screen();
n = 0;
}
tty_curs(window, 1, n++);
if (cw->offx) cl_end();
if (cw->data[i]) {
attr = cw->data[i][0] - 1;
if (cw->offx) {
(void) putchar(' '); ++ttyDisplay->curx;
}
term_start_attr(attr);
for (cp = &cw->data[i][1];
*cp && (int) ++ttyDisplay->curx < (int) ttyDisplay->cols;
cp++)
(void) pututf8char(*cp);
term_end_attr(attr);
}
}
if (i == cw->maxrow) {
tty_curs(BASE_WINDOW, (int)cw->offx + 1,
(cw->type == NHW_TEXT) ? (int) ttyDisplay->rows - 1 : n);
cl_end();
dmore(cw, quitchars);
if (morc == DOESCAPE)
cw->flags |= WIN_CANCELLED;
}
}
/*ARGSUSED*/
void
tty_display_nhwindow(winid window, boolean blocking) /* with ttys, all windows are blocking */
{
register struct WinDesc *cw = 0;
if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
panic(winpanicstr, window);
if(cw->flags & WIN_CANCELLED)
return;
ttyDisplay->lastwin = window;
ttyDisplay->rawprint = 0;
print_vt_code(AVTC_SELECT_WINDOW, window);
switch(cw->type) {
case NHW_MESSAGE:
if(ttyDisplay->toplin == 1) {
more();
ttyDisplay->toplin = 1; /* more resets this */
tty_clear_nhwindow(window);
} else
ttyDisplay->toplin = 0;
cw->curx = cw->cury = 0;
if(!cw->active)
iflags.window_inited = TRUE;
break;
case NHW_MAP:
end_glyphout();
if(blocking) {
if(!ttyDisplay->toplin) ttyDisplay->toplin = 1;
tty_display_nhwindow(WIN_MESSAGE, TRUE);
return;
}
case NHW_BASE:
(void) fflush(stdout);
break;
case NHW_TEXT:
cw->maxcol = ttyDisplay->cols; /* force full-screen mode */
/*FALLTHRU*/
case NHW_MENU:
cw->active = 1;
/* avoid converting to uchar before calculations are finished */
cw->offx = (uchar) (int)
max((int) 10, (int) (ttyDisplay->cols - cw->maxcol - 1));
if(cw->type == NHW_MENU
#ifdef WIN_EDGE
|| iflags.win_edge
#endif
)
cw->offy = 0;
if(ttyDisplay->toplin == 1)
tty_display_nhwindow(WIN_MESSAGE, TRUE);
if(cw->offx == 10 || cw->maxrow >= (int) ttyDisplay->rows
#ifdef WIN_EDGE
|| iflags.win_edge
#endif
) {
cw->offx = 0;
if(cw->offy) {
tty_curs(window, 1, 0);
cl_eos();
} else
clear_screen();
ttyDisplay->toplin = 0;
} else
tty_clear_nhwindow(WIN_MESSAGE);
if (cw->data || !cw->maxrow)
process_text_window(window, cw);
else
process_menu_window(window, cw);
break;
}
cw->active = 1;
}
void
tty_dismiss_nhwindow(winid window)
{
register struct WinDesc *cw = 0;
if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
panic(winpanicstr, window);
print_vt_code(AVTC_SELECT_WINDOW, window);
switch(cw->type) {
case NHW_MESSAGE:
if (ttyDisplay->toplin)
tty_display_nhwindow(WIN_MESSAGE, TRUE);
/*FALLTHRU*/
case NHW_STATUS:
case NHW_BASE:
case NHW_MAP:
/*
* these should only get dismissed when the game is going away
* or suspending
*/
tty_curs(BASE_WINDOW, 1, (int)ttyDisplay->rows-1);
cw->active = 0;
break;
case NHW_MENU:
case NHW_TEXT:
if(cw->active) {
if (iflags.window_inited) {
/* otherwise dismissing the text endwin after other windows
* are dismissed tries to redraw the map and panics. since
* the whole reason for dismissing the other windows was to
* leave the ending window on the screen, we don't want to
* erase it anyway.
*/
erase_menu_or_text(window, cw, FALSE);
}
cw->active = 0;
}
break;
}
cw->flags = 0;
}
void
tty_destroy_nhwindow(winid window)
{
register struct WinDesc *cw = 0;
if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
panic(winpanicstr, window);
if(cw->active)
tty_dismiss_nhwindow(window);
if(cw->type == NHW_MESSAGE)
iflags.window_inited = 0;
if(cw->type == NHW_MAP)
clear_screen();
free_window_info(cw, TRUE);
free((void *)cw);
wins[window] = 0;
}
void
tty_curs(winid window, int x, int y)
/* not xchar: perhaps xchar is unsigned and
curx-x would be unsigned as well */
{
struct WinDesc *cw = 0;
int cx = ttyDisplay->curx;
int cy = ttyDisplay->cury;
if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
panic(winpanicstr, window);
ttyDisplay->lastwin = window;
print_vt_code(AVTC_SELECT_WINDOW, window);
cw->curx = --x; /* column 0 is never used */
cw->cury = y;
#ifdef DEBUG
if(x<0 || y<0 || y >= cw->rows || x > cw->cols) {
const char *s = "[unknown type]";
switch(cw->type) {
case NHW_MESSAGE: s = "[topl window]"; break;
case NHW_STATUS: s = "[status window]"; break;
case NHW_MAP: s = "[map window]"; break;
case NHW_MENU: s = "[corner window]"; break;
case NHW_TEXT: s = "[text window]"; break;
case NHW_BASE: s = "[base window]"; break;
}
impossible("bad curs positioning win %d %s (%d,%d)", window, s, x, y);
return;
}
#endif
x += cw->offx;
y += cw->offy;
if(clipping && window == WIN_MAP) {
x -= clipx;
y -= clipy;
}
if (y == cy && x == cx)
return;
if(cw->type == NHW_MAP)
end_glyphout();
if(!nh_ND && (cx != x || x <= 3)) { /* Extremely primitive */
cmov(x, y); /* bunker!wtm */
return;
}
if((cy -= y) < 0) cy = -cy;
if((cx -= x) < 0) cx = -cx;
if(cy <= 3 && cx <= 3) {
nocmov(x, y);
} else if ((x <= 3 && cy <= 3) || (!nh_CM && x < cx)) {
(void) putchar('\r');
ttyDisplay->curx = 0;
nocmov(x, y);
} else if (!nh_CM) {
nocmov(x, y);
} else
cmov(x, y);
ttyDisplay->curx = x;
ttyDisplay->cury = y;
}
static void
tty_putsym(winid window, int x, int y, char ch)
{
register struct WinDesc *cw = 0;
if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
panic(winpanicstr, window);
print_vt_code(AVTC_SELECT_WINDOW, window);
switch(cw->type) {
case NHW_STATUS:
case NHW_MAP:
case NHW_BASE:
tty_curs(window, x, y);
if (iflags.UTF8graphics) {
pututf8char(ch);
} else {
(void) putchar(ch);
}
ttyDisplay->curx++;
cw->curx++;
break;
case NHW_MESSAGE:
case NHW_MENU:
case NHW_TEXT:
impossible("Can't putsym to window type %d", cw->type);
break;
}
}
const char*
compress_str(const char *str)
{
static char cbuf[BUFSZ];
/* compress in case line too long */
if((int)strlen(str) >= CO) {
register const char *bp0 = str;
register char *bp1 = cbuf;
do {
if(*bp0 != ' ' || bp0[1] != ' ')
*bp1++ = *bp0;
} while(*bp0++);
} else
return str;
return cbuf;
}
void
tty_putstr(winid window, int attr, const char *str)
{
register struct WinDesc *cw = 0;
register char *ob;
register const char *nb;
register long i, j, n0;
/* Assume there's a real problem if the window is missing --
* probably a panic message
*/
if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) {
tty_raw_print(str);
return;
}
if(str == (const char*)0 ||
((cw->flags & WIN_CANCELLED) && (cw->type != NHW_MESSAGE)))
return;
if(cw->type != NHW_MESSAGE)
str = compress_str(str);
ttyDisplay->lastwin = window;
print_vt_code(AVTC_SELECT_WINDOW, window);
switch(cw->type) {
case NHW_MESSAGE:
/* really do this later */
#ifdef USER_SOUNDS
play_sound_for_message(str);
#endif
update_topl(str);
break;
case NHW_STATUS:
ob = &cw->data[cw->cury][j = cw->curx];
if(flags.botlx) *ob = 0;
if(!cw->cury && (int)strlen(str) >= CO) {
/* the characters before "St:" are unnecessary */
nb = index(str, ':');
if(nb && nb > str+2)
str = nb - 2;
}
nb = str;
for(i = cw->curx+1, n0 = cw->cols; i < n0; i++, nb++) {
if(!*nb) {
if(*ob || flags.botlx) {
/* last char printed may be in middle of line */
tty_curs(WIN_STATUS, i, cw->cury);
cl_end();
}
break;
}
if(*ob != *nb)
tty_putsym(WIN_STATUS, i, cw->cury, *nb);
if(*ob) ob++;
}
(void) strncpy(&cw->data[cw->cury][j], str, cw->cols - j - 1);
cw->data[cw->cury][cw->cols-1] = '\0'; /* null terminate */
cw->cury = (cw->cury+1) % 2;
cw->curx = 0;
break;
case NHW_MAP:
tty_curs(window, cw->curx+1, cw->cury);
term_start_attr(attr);
while(*str && (int) ttyDisplay->curx < (int) ttyDisplay->cols-1) {
(void) putchar(*str);
str++;
ttyDisplay->curx++;
}
cw->curx = 0;
cw->cury++;
term_end_attr(attr);
break;
case NHW_BASE:
tty_curs(window, cw->curx+1, cw->cury);
term_start_attr(attr);
while (*str) {
if ((int) ttyDisplay->curx >= (int) ttyDisplay->cols-1) {
cw->curx = 0;
cw->cury++;
tty_curs(window, cw->curx+1, cw->cury);
}
if (iflags.UTF8graphics) {
pututf8char(*str);
} else {
(void) putchar(*str);
}
str++;
ttyDisplay->curx++;
}
cw->curx = 0;
cw->cury++;
term_end_attr(attr);
break;
case NHW_MENU:
case NHW_TEXT:
if(cw->type == NHW_TEXT && cw->cury == ttyDisplay->rows-1) {
/* not a menu, so save memory and output 1 page at a time */
cw->maxcol = ttyDisplay->cols; /* force full-screen mode */
tty_display_nhwindow(window, TRUE);
for(i=0; i<cw->maxrow; i++)
if(cw->data[i]){
free((void *)cw->data[i]);
cw->data[i] = 0;
}
cw->maxrow = cw->cury = 0;
}
/* always grows one at a time, but alloc 12 at a time */
if(cw->cury >= cw->rows) {
char **tmp;
cw->rows += 12;
tmp = (char **) alloc(sizeof(char *) * (unsigned)cw->rows);
for(i=0; i<cw->maxrow; i++)
tmp[i] = cw->data[i];
if(cw->data)
free((void *)cw->data);
cw->data = tmp;
for(i=cw->maxrow; i<cw->rows; i++)
cw->data[i] = 0;
}
if(cw->data[cw->cury])
free((void *)cw->data[cw->cury]);
n0 = strlen(str) + 1;
ob = cw->data[cw->cury] = (char *)alloc((unsigned)n0 + 1);
*ob++ = (char)(attr + 1); /* avoid nuls, for convenience */
Strcpy(ob, str);
if(n0 > cw->maxcol)
cw->maxcol = n0;
if(++cw->cury > cw->maxrow)
cw->maxrow = cw->cury;
if(n0 > CO) {
/* attempt to break the line */
for(i = CO-1; i && str[i] != ' ' && str[i] != '\n';)
i--;
if(i) {
cw->data[cw->cury-1][++i] = '\0';
tty_putstr(window, attr, &str[i]);
}
}
break;
}
}
void
tty_display_file(const char *fname, boolean complain)
{
#ifdef DEF_PAGER /* this implies that UNIX is defined */
{
/* use external pager; this may give security problems */
register int fd = open(fname, 0);
if(fd < 0) {
if(complain) pline("Cannot open %s.", fname);
else docrt();
return;
}
if(child(1)) {
/* Now that child() does a setuid(getuid()) and a chdir(),
we may not be able to open file fname anymore, so make
it stdin. */
(void) close(0);
if(dup(fd)) {
if(complain) raw_printf("Cannot open %s as stdin.", fname);
} else {
(void) execlp(catmore, "page", (char *)0);
if(complain) raw_printf("Cannot exec %s.", catmore);
}
if(complain) sleep(10); /* want to wait_synch() but stdin is gone */
terminate(EXIT_FAILURE);
}
(void) close(fd);
}
#else /* DEF_PAGER */
{
dlb *f;
char buf[BUFSZ];
char *cr;
tty_clear_nhwindow(WIN_MESSAGE);
f = dlb_fopen(fname, "r");
if (!f) {
if(complain) {
home(); tty_mark_synch(); tty_raw_print("");
perror(fname); tty_wait_synch();
pline("Cannot open \"%s\".", fname);
} else if(u.ux) docrt();
} else {
winid datawin = tty_create_nhwindow(NHW_TEXT);
boolean empty = TRUE;
if(complain
&& nh_CD
) {
/* attempt to scroll text below map window if there's room */
wins[datawin]->offy = wins[WIN_STATUS]->offy+3;
if((int) wins[datawin]->offy + 12 > (int) ttyDisplay->rows)
wins[datawin]->offy = 0;
}
while (dlb_fgets(buf, BUFSZ, f)) {
if ((cr = index(buf, '\n')) != 0) *cr = 0;
if (index(buf, '\t') != 0) (void) tabexpand(buf);
empty = FALSE;
tty_putstr(datawin, 0, buf);
if(wins[datawin]->flags & WIN_CANCELLED)
break;
}
if (!empty) tty_display_nhwindow(datawin, FALSE);
tty_destroy_nhwindow(datawin);
(void) dlb_fclose(f);
}
}
#endif /* DEF_PAGER */
}
void
tty_start_menu(winid window)
{
tty_clear_nhwindow(window);
return;
}
/*ARGSUSED*/
/*
* Add a menu item to the beginning of the menu list. This list is reversed
* later.
*/
void
tty_add_menu(
winid window, /* window to use, must be of type NHW_MENU */
int glyph, /* glyph to display with item */
const anything *identifier, /* what to return if selected */
char ch, /* keyboard accelerator (0 = pick our own) */
char gch, /* group accelerator (0 = no group) */
int attr, /* attribute for string (like tty_putstr()) */
const char *str, /* menu string */
boolean preselected) /* item is marked as selected */
{
register struct WinDesc *cw = 0;
tty_menu_item *item;
if (str == (const char*) 0)
return;
if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0
|| cw->type != NHW_MENU)
panic(winpanicstr, window);
cw->nitems++;
item = (tty_menu_item *) alloc(sizeof(tty_menu_item));
item->identifier = *identifier;
item->count = -1L;
item->selected = preselected;
item->selector = ch;
item->gselector = gch;
item->attr = attr;
item->str = copy_of(str);
item->glyph = glyph;
item->next = cw->mlist;
cw->mlist = item;
}
/* Invert the given list, can handle NULL as an input. */
static tty_menu_item *
reverse(tty_menu_item *curr)
{
tty_menu_item *next, *head = 0;
while (curr) {
next = curr->next;
curr->next = head;
head = curr;
curr = next;
}
return head;
}
/*
* End a menu in this window, window must a type NHW_MENU. This routine
* processes the string list. We calculate the # of pages, then assign
* keyboard accelerators as needed. Finally we decide on the width and
* height of the window.
*
* window - menu to use
* prompt - prompt to for menu
*/
void
tty_end_menu(winid window, const char *prompt)
{
struct WinDesc *cw = 0;
tty_menu_item *curr;
short len;
int lmax, n;
char menu_ch;
if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0 ||
cw->type != NHW_MENU)
panic(winpanicstr, window);
/* Reverse the list so that items are in correct order. */
cw->mlist = reverse(cw->mlist);
/* Put the promt at the beginning of the menu. */
if (prompt) {
anything any;
any.a_void = 0; /* not selectable */
tty_add_menu(window, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
tty_add_menu(window, NO_GLYPH, &any, 0, 0, ATR_NONE, prompt, MENU_UNSELECTED);
}
lmax = min(52, (int)ttyDisplay->rows - 1); /* # lines per page */
cw->npages = (cw->nitems + (lmax - 1)) / lmax; /* # of pages */
/* make sure page list is large enough */
if (cw->plist_size < cw->npages+1 /*need 1 slot beyond last*/) {
if (cw->plist) free((void *)cw->plist);
cw->plist_size = cw->npages + 1;
cw->plist = (tty_menu_item **)
alloc(cw->plist_size * sizeof(tty_menu_item *));
}
cw->cols = 0; /* cols is set when the win is initialized... (why?) */
menu_ch = '?'; /* lint suppression */
for (n = 0, curr = cw->mlist; curr; n++, curr = curr->next) {
/* set page boundaries and character accelerators */
if ((n % lmax) == 0) {
menu_ch = 'a';
cw->plist[n/lmax] = curr;
}
if (curr->identifier.a_void && !curr->selector) {
curr->selector = menu_ch;
if (menu_ch++ == 'z') menu_ch = 'A';
}
/* cut off any lines that are too long */
short xl = 2; /* extra space at beg & end */
len = strlen((curr->str ? curr->str : ""));
if (curr->selector) {
/* extra space for keyboard accelerator */
xl += 4;
if (curr->glyph != NO_GLYPH && iflags.use_menu_glyphs) {
/* extra space for glyph */
xl += 2;
}
}
len += xl;
if (len > (int)ttyDisplay->cols) {
curr->str[ttyDisplay->cols-xl] = 0;
len = ttyDisplay->cols;
}
if (len > cw->cols) cw->cols = len;
}
cw->plist[cw->npages] = 0; /* plist terminator */
/*
* If greater than 1 page, morestr is "(x of y) " otherwise, "(end) "
*/
if (cw->npages > 1) {
char buf[QBUFSZ];
/* produce the largest demo string */
Sprintf(buf, "(%ld of %ld) ", cw->npages, cw->npages);
len = strlen(buf);
cw->morestr = copy_of("");
} else {
cw->morestr = copy_of("(end) ");
len = strlen(cw->morestr);
}
if (len > (int)ttyDisplay->cols) {
/* truncate the prompt if its too long for the screen */
if (cw->npages <= 1) /* only str in single page case */
cw->morestr[ttyDisplay->cols] = 0;
len = ttyDisplay->cols;
}
if (len > cw->cols) cw->cols = len;
cw->maxcol = cw->cols;
/*
* The number of lines in the first page plus the morestr will be the
* maximum size of the window.
*/
if (cw->npages > 1)
cw->maxrow = cw->rows = lmax + 1;
else
cw->maxrow = cw->rows = cw->nitems + 1;
}
int
tty_select_menu(winid window, int how, menu_item **menu_list)
{
register struct WinDesc *cw = 0;
tty_menu_item *curr;
menu_item *mi;
int n, cancelled;
if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0
|| cw->type != NHW_MENU)
panic(winpanicstr, window);
*menu_list = (menu_item *) 0;
cw->how = (short) how;
morc = 0;
tty_display_nhwindow(window, TRUE);
cancelled = !!(cw->flags & WIN_CANCELLED);
tty_dismiss_nhwindow(window); /* does not destroy window data */
if (cancelled) {
n = -1;
} else {
for (n = 0, curr = cw->mlist; curr; curr = curr->next)
if (curr->selected) n++;
}
if (n > 0) {
*menu_list = (menu_item *) alloc(n * sizeof(menu_item));
for (mi = *menu_list, curr = cw->mlist; curr; curr = curr->next)
if (curr->selected) {
mi->item = curr->identifier;
mi->count = curr->count;
mi++;
}
}
return n;
}
/* special hack for treating top line --More-- as a one item menu */
char
tty_message_menu(char let, int how, const char *mesg)
{
/* "menu" without selection; use ordinary pline, no more() */
if (how == PICK_NONE) {
pline("%s", mesg);
return 0;
}
ttyDisplay->dismiss_more = let;
morc = 0;
/* barebones pline(); since we're only supposed to be called after
response to a prompt, we'll assume that the display is up to date */
tty_putstr(WIN_MESSAGE, 0, mesg);
/* if `mesg' didn't wrap (triggering --More--), force --More-- now */
if (ttyDisplay->toplin == 1) {
more();
ttyDisplay->toplin = 1; /* more resets this */
tty_clear_nhwindow(WIN_MESSAGE);
}
/* normally <ESC> means skip further messages, but in this case
it means cancel the current prompt; any other messages should
continue to be output normally */
wins[WIN_MESSAGE]->flags &= ~WIN_CANCELLED;
ttyDisplay->dismiss_more = 0;
return ((how == PICK_ONE && morc == let) || morc == DOESCAPE) ? morc : '\0';
}
void
tty_update_inventory(void)
{
return;
}
void
tty_mark_synch(void)
{
(void) fflush(stdout);
}
void
tty_wait_synch(void)
{
/* we just need to make sure all windows are synch'd */
if(!ttyDisplay || ttyDisplay->rawprint) {
getret();
if(ttyDisplay) ttyDisplay->rawprint = 0;
} else {
tty_display_nhwindow(WIN_MAP, FALSE);
if(ttyDisplay->inmore) {
addtopl("--More--");
(void) fflush(stdout);
} else if(ttyDisplay->inread > program_state.gameover) {
/* this can only happen if we were reading and got interrupted */
ttyDisplay->toplin = 3;
/* do this twice; 1st time gets the Quit? message again */
(void) tty_doprev_message();
(void) tty_doprev_message();
ttyDisplay->intr++;
(void) fflush(stdout);
}
}
}
void
docorner(int xmin, int ymax)
{
register int y;
register struct WinDesc *cw = wins[WIN_MAP];
#ifdef SIGWINCH
if(ymax > LI) ymax = LI; /* can happen if window gets smaller */
#endif
for (y = 0; y < ymax; y++) {
tty_curs(BASE_WINDOW, xmin,y); /* move cursor */
cl_end(); /* clear to end of line */
if (y<(int) cw->offy || y+clipy > ROWNO)
continue; /* only refresh board */
row_refresh(xmin+clipx-(int)cw->offx,COLNO-1,y+clipy-(int)cw->offy);
}
end_glyphout();
if (!in_character_selection && /* check for status lines to update */
(ymax >= (int) wins[WIN_STATUS]->offy)) {
/* we have wrecked the bottom line */
flags.botlx = 1;
bot();
}
}
void
end_glyphout(void)
{
if (GFlag) {
GFlag = FALSE;
graph_off();
}
if(ttyDisplay->color != NO_COLOR) {
term_end_color();
ttyDisplay->color = NO_COLOR;
}
}
void
g_putch(int in_ch)
{
register char ch = (char)in_ch;
if (iflags.IBMgraphics || iflags.eight_bit_tty) {
/* IBM-compatible displays don't need other stuff */
(void) putchar(ch);
} else if (ch & 0x80) {
if (!GFlag || HE_resets_AS) {
graph_on();
GFlag = TRUE;
}
(void) putchar((ch ^ 0x80)); /* Strip 8th bit */
} else {
if (GFlag) {
graph_off();
GFlag = FALSE;
}
(void) putchar(ch);
}
return;
}
void
setclipped(void)
{
clipping = TRUE;
clipx = clipy = 0;
clipxmax = CO;
clipymax = LI - 3;
}
void
tty_cliparound(int x, int y)
{
extern boolean restoring;
int oldx = clipx, oldy = clipy;
if (!clipping) return;
if (x < clipx + 5) {
clipx = max(0, x - 20);
clipxmax = clipx + CO;
}
else if (x > clipxmax - 5) {
clipxmax = min(COLNO, clipxmax + 20);
clipx = clipxmax - CO;
}
if (y < clipy + 2) {
clipy = max(0, y - (clipymax - clipy) / 2);
clipymax = clipy + (LI - 3);
}
else if (y > clipymax - 2) {
clipymax = min(ROWNO, clipymax + (clipymax - clipy) / 2);
clipy = clipymax - (LI - 3);
}
if (clipx != oldx || clipy != oldy) {
if (on_level(&u.uz0, &u.uz) && !restoring)
(void) doredraw();
}
}
/*
* tty_print_glyph
*
* Print the glyph to the output device. Don't flush the output device.
*
* Since this is only called from show_glyph(), it is assumed that the
* position and glyph are always correct (checked there)!
*/
void
tty_print_glyph(winid window, xchar x, xchar y, int glyph)
{
glyph_t ch;
boolean reverse_on = FALSE;
int color, bgcolor=NO_COLOR;
unsigned bgcolor_ret;
if(clipping) {
if(x <= clipx || y < clipy || x >= clipxmax || y >= clipymax)
return;
}
/* map glyph to character and color */
mapglyph(glyph, &ch, &color, &bgcolor_ret, x, y);
print_vt_code(AVTC_SELECT_WINDOW, window);
/* Move the cursor. */
tty_curs(window, x,y);
print_vt_code(AVTC_GLYPH_START, glyph2tile[glyph]);
if (ul_hack && ch == '_') { /* non-destructive underscore */
(void) putchar((char) ' ');
backsp();
}
if (color != ttyDisplay->color) {
if(ttyDisplay->color != NO_COLOR)
term_end_color();
ttyDisplay->color = color;
if(color != NO_COLOR)
term_start_color(color);
}
/* must be after color check; term_end_color may turn off inverse too */
if (bgcolor_ret != NO_COLOR) {
bgcolor = bgcolor_ret;
term_start_bgcolor(bgcolor);
}
if (color == bgcolor && color != NO_COLOR) {
if(ttyDisplay->color != NO_COLOR)
term_end_color();
ttyDisplay->color = color;
if(color != NO_COLOR)
term_start_color(color);
term_start_attr(ATR_INVERSE);
reverse_on = TRUE;
}
if (iflags.UTF8graphics) {
pututf8char(get_unicode_codepoint(ch));
} else {
g_putch(ch); /* print the character */
}
if (reverse_on) {
term_end_attr(ATR_INVERSE);
/* turn off color as well, ATR_INVERSE may have done this already */
if(ttyDisplay->color != NO_COLOR) {
term_end_color();
ttyDisplay->color = NO_COLOR;
}
}
print_vt_code(AVTC_GLYPH_END, -1);
if (!reverse_on && (bgcolor != NO_COLOR)) {
term_end_color();
ttyDisplay->color = NO_COLOR;
}
wins[window]->curx++; /* one character over */
ttyDisplay->curx++; /* the real cursor moved too */
}
void
tty_raw_print(const char *str)
{
if(ttyDisplay) ttyDisplay->rawprint++;
puts(str); (void) fflush(stdout);
}
void
tty_raw_print_bold(const char *str)
{
if(ttyDisplay) ttyDisplay->rawprint++;
term_start_raw_bold();
(void) fputs(str, stdout);
term_end_raw_bold();
puts("");
(void) fflush(stdout);
}
int
tty_nhgetch(void)
{
int i;
int tmp;
#ifdef UNIX
/* kludge alert: Some Unix variants return funny values if getc()
* is called, interrupted, and then called again. There
* is non-reentrant code in the internal _filbuf() routine, called by
* getc().
*/
static volatile int nesting = 0;
char nestbuf;
#endif
print_vt_code(AVTC_INLINE_SYNC, -1);
(void) fflush(stdout);
/* Note: if raw_print() and wait_synch() get called to report terminal
* initialization problems, then wins[] and ttyDisplay might not be
* available yet. Such problems will probably be fatal before we get
* here, but validate those pointers just in case...
*/
if (WIN_MESSAGE != WIN_ERR && wins[WIN_MESSAGE])
wins[WIN_MESSAGE]->flags &= ~WIN_STOP;
if (iflags.debug_fuzzer) {
i = randomkey();
} else {
#ifdef UNIX
i = ((++nesting == 1) ? tgetch() :
(read(fileno(stdin), (void *)&nestbuf,1) == 1 ? (int)nestbuf :
EOF));
--nesting;
#else
i = tgetch();
#endif
}
if (!i) i = DOESCAPE; /* map NUL to ESC since nethack doesn't expect NUL */
if (ttyDisplay && ttyDisplay->toplin == 1)
ttyDisplay->toplin = 2;
tmp = vt_tile_current_window;
vt_tile_current_window++;
print_vt_code(AVTC_SELECT_WINDOW, tmp);
return i;
}
/*
* return a key, or 0, in which case a mouse button was pressed
* mouse events should be returned as character postitions in the map window.
* Since normal tty's don't have mice, just return a key.
*/
/*ARGSUSED*/
int
tty_nh_poskey(int *x, int *y, int *mod)
{
return tty_nhgetch();
}
void
win_tty_init(void)
{
return;
}
#ifdef POSITIONBAR
void
tty_update_positionbar(char *posbar)
{
}
#endif
/*
* Allocate a copy of the given string. If null, return a string of
* zero length.
*
* This is an exact duplicate of copy_of() in X11/winmenu.c.
*/
static char *
copy_of(const char *s)
{
if (!s) s = "";
return strcpy((char *) alloc((unsigned) (strlen(s) + 1)), s);
}
#endif /* TTY_GRAPHICS */
/*wintty.c*/