Merge branch 'win-curses' into macosx-curses-fall2018

This commit is contained in:
nhmall
2018-11-18 09:10:26 -05:00
40 changed files with 8421 additions and 156 deletions

View File

@@ -774,6 +774,9 @@ to support:
| wraptext | WC2_WRAPTEXT | wc2_wraptext |boolean |
| selectsaved | WC2_SELECTSAVED | wc2_selectsaved |boolean |
| hitpointbar | WC2_HITPOINTBAR | wc2_hitpointbar |boolean |
| term_cols | WC2_TERM_COLS | wc2_term_cols |int |
| term_rows | WC2_TERM_ROWS | wc2_term_rows |int |
| windowborders | WC2_WINDOWBORDERS | wc2_windowborders |int |
+--------------------+--------------------+--------------------+--------+
more wincap2 for STATUS_HILITES support and control
@@ -820,6 +823,8 @@ scroll_margin -- port should scroll the display when the hero or cursor
selectsaved -- if port can display a menu of the user's saved games do so.
softkeyboard -- handhelds should display an on-screen keyboard if possible.
splash_screen -- port should/should not display an opening splashscreen.
term_cols -- Terminal should size itself to specified width, if possible.
term_rows -- Terminal should size itself to specified height, if possible.
tiled_map -- port should display a tiled map if it can.
tile_width -- port should display tiles with this width or round to closest
if it can.
@@ -830,6 +835,8 @@ tile_file -- open this alternative tile file. The file name is likely to be
use_inverse -- port should display inverse when NetHack asks for it.
vary_msgcount -- port should display this number of messages at a time in
the message window.
windowborders -- port should display borders around main NetHack windows.
Can be set to (1) on, (2) off, or (3) auto.
windowcolors
-- port should use these colors for window foreground/background
colors. Syntax:

View File

@@ -45,6 +45,7 @@
#if !defined(NOTTYGRAPHICS)
#define TTY_GRAPHICS /* good old tty based graphics */
#endif
/* #define CURSES_GRAPHICS *//* Curses interface - Karl Garrison*/
/* #define X11_GRAPHICS */ /* X11 interface */
/* #define QT_GRAPHICS */ /* Qt interface */
/* #define GNOME_GRAPHICS */ /* Gnome interface */
@@ -118,6 +119,12 @@
#define DEFAULT_WINDOW_SYS "tty"
#endif
#ifdef CURSES_GRAPHICS
#ifndef DEFAULT_WINDOW_SYS
#define DEFAULT_WINDOW_SYS "curses"
#endif
#endif
#ifdef X11_GRAPHICS
/*
* There are two ways that X11 tiles may be defined. (1) using a custom

View File

@@ -312,6 +312,7 @@ struct instance_flags {
boolean rlecomp; /* alternative to zerocomp; run-length encoding
* compression of levels when writing savefile */
uchar num_pad_mode;
boolean cursesgraphics; /* Use portable curses extended characters */
#if 0 /* XXXgraphics superseded by symbol sets */
boolean DECgraphics; /* use DEC VT-xxx extended character set */
boolean IBMgraphics; /* use IBM extended character set */
@@ -323,6 +324,8 @@ struct instance_flags {
uchar bouldersym; /* symbol for boulder display */
#ifdef TTY_GRAPHICS
char prevmsg_window; /* type of old message window to use */
#endif
#if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS)
boolean extmenu; /* extended commands use menu interface */
#endif
#ifdef MFLOPPY
@@ -363,8 +366,12 @@ struct instance_flags {
#ifdef TTY_TILES_ESCCODES
boolean vt_tiledata; /* output console codes for tile support in TTY */
#endif
boolean wizweight; /* display weight of everything in wizard mode */
boolean wizweight; /* display weight of everything in wizard mode */
boolean cmdassist; /* provide detailed assistance for some commands */
boolean clicklook; /* allow right-clicking for look */
boolean msg_is_alert; /* suggest windowport should grab player's attention
* and request <TAB> acknowlegement */
int statuslines; /* default = 2, code support for alternative 3 */
/*
* Window capability support.
*/
@@ -416,8 +423,11 @@ struct instance_flags {
boolean wc2_selectsaved; /* display a menu of user's saved games */
boolean wc2_darkgray; /* try to use dark-gray color for black glyphs */
boolean wc2_hitpointbar; /* show graphical bar representing hit points */
boolean cmdassist; /* provide detailed assistance for some commands */
boolean clicklook; /* allow right-clicking for look */
int wc2_term_cols; /* terminal width, in characters */
int wc2_term_rows; /* terminal height, in characters */
int wc2_windowborders; /* display borders on NetHack windows */
int wc2_petattr; /* text attributes for pet */
boolean wc2_guicolor; /* allow colours in gui (outside map) */
boolean obsolete; /* obsolete options can point at this, it isn't used */
struct autopickup_exception *autopickup_exceptions[2];
#define AP_LEAVE 0
@@ -438,6 +448,7 @@ struct instance_flags {
short soko_prize_type1; /* bag of holding or */
short soko_prize_type2; /* amulet of reflection */
struct debug_flags debug;
boolean windowtype_locked; /* windowtype can't change from configfile */
};
/*

View File

@@ -93,6 +93,7 @@ extern void FDECL(interject, (int));
* Compiler-specific adjustments
*===============================================
*/
#ifdef _MSC_VER
#if (_MSC_VER > 1000)
/* Visual C 8 warning elimination */
@@ -227,7 +228,9 @@ open(const char _FAR *__path, int __access, ... /*unsigned mode*/);
long _RTLENTRY _EXPFUNC lseek(int __handle, long __offset, int __fromwhere);
int _RTLENTRY _EXPFUNC read(int __handle, void _FAR *__buf, unsigned __len);
#endif
#include <conio.h>
#ifndef CURSES_GRAPHICS
#include <conio.h> /* conflicting definitions with curses.h */
#endif
#undef kbhit /* Use our special NT kbhit */
#define kbhit (*nt_kbhit)

View File

@@ -108,7 +108,7 @@ enum levl_typ_types {
/*
* The screen symbols may be the default or defined at game startup time.
* See drawing.c for defaults.
* Note: {ibm|dec}_graphics[] arrays (also in drawing.c) must be kept in
* Note: {ibm|dec|curses}_graphics[] arrays (also in drawing.c) must be kept in
* synch.
*/
@@ -294,9 +294,10 @@ struct symsetentry {
* Must match the order of the known_handlers strings
* in drawing.c
*/
#define H_UNK 0
#define H_IBM 1
#define H_DEC 2
#define H_UNK 0
#define H_IBM 1
#define H_DEC 2
#define H_CURS 3
extern const struct symdef defsyms[MAXPCHARS]; /* defaults */
extern const struct symdef def_warnsyms[WARNCOUNT];

310
include/wincurs.h Normal file
View File

@@ -0,0 +1,310 @@
#ifndef WINCURS_H
#define WINCURS_H
/* Global declarations for curses interface */
int term_rows, term_cols; /* size of underlying terminal */
WINDOW *base_term; /* underlying terminal window */
WINDOW *mapwin, *statuswin, *messagewin; /* Main windows */
int orig_cursor; /* Preserve initial cursor state */
boolean counting; /* Count window is active */
#define TEXTCOLOR /* Allow color */
#define NHW_END 19
#define OFF 0
#define ON 1
#define NONE -1
#define KEY_ESC 0x1b
#define DIALOG_BORDER_COLOR CLR_MAGENTA
#define ALERT_BORDER_COLOR CLR_RED
#define SCROLLBAR_COLOR CLR_MAGENTA
#define SCROLLBAR_BACK_COLOR CLR_BLACK
#define HIGHLIGHT_COLOR CLR_WHITE
#define MORECOLOR CLR_ORANGE
#define STAT_UP_COLOR CLR_GREEN
#define STAT_DOWN_COLOR CLR_RED
#define MESSAGE_WIN 1
#define STATUS_WIN 2
#define MAP_WIN 3
#define INV_WIN 4
#define NHWIN_MAX 5
#define MESG_HISTORY_MAX 200
#if !defined(__APPLE__) || !defined(NCURSES_VERSION)
# define USE_DARKGRAY /* Allow "bright" black; delete if not visible */
#endif /* !__APPLE__ && !PDCURSES */
#define CURSES_DARK_GRAY 17
#define MAP_SCROLLBARS
#ifdef PDCURSES
# define getmouse nc_getmouse
# ifndef NCURSES_MOUSE_VERSION
# define NCURSES_MOUSE_VERSION
# endif
#endif
typedef enum orient_type
{
CENTER,
UP,
DOWN,
RIGHT,
LEFT,
UNDEFINED
} orient;
/* cursmain.c */
extern struct window_procs curses_procs;
extern void curses_init_nhwindows(int* argcp, char** argv);
extern void curses_player_selection(void);
extern void curses_askname(void);
extern void curses_get_nh_event(void);
extern void curses_exit_nhwindows(const char *str);
extern void curses_suspend_nhwindows(const char *str);
extern void curses_resume_nhwindows(void);
extern winid curses_create_nhwindow(int type);
extern void curses_clear_nhwindow(winid wid);
extern void curses_display_nhwindow(winid wid, BOOLEAN_P block);
extern void curses_destroy_nhwindow(winid wid);
extern void curses_curs(winid wid, int x, int y);
extern void curses_putstr(winid wid, int attr, const char *text);
extern void curses_display_file(const char *filename,BOOLEAN_P must_exist);
extern void curses_start_menu(winid wid);
extern void curses_add_menu(winid wid, int glyph, const ANY_P * identifier,
CHAR_P accelerator, CHAR_P group_accel, int attr,
const char *str, BOOLEAN_P presel);
extern void curses_end_menu(winid wid, const char *prompt);
extern int curses_select_menu(winid wid, int how, MENU_ITEM_P **selected);
extern void curses_update_inventory(void);
extern void curses_mark_synch(void);
extern void curses_wait_synch(void);
extern void curses_cliparound(int x, int y);
extern void curses_print_glyph(winid wid,XCHAR_P x,XCHAR_P y,int glyph, int bkglyph);
extern void curses_raw_print(const char *str);
extern void curses_raw_print_bold(const char *str);
extern int curses_nhgetch(void);
extern int curses_nh_poskey(int *x, int *y, int *mod);
extern void curses_nhbell(void);
extern int curses_doprev_message(void);
extern char curses_yn_function(const char *question, const char *choices, CHAR_P def);
extern void curses_getlin(const char *question, char *input);
extern int curses_get_ext_cmd(void);
extern void curses_number_pad(int state);
extern void curses_delay_output(void);
extern void curses_start_screen(void);
extern void curses_end_screen(void);
extern void curses_outrip(winid wid, int how);
extern void genl_outrip(winid tmpwin, int how, time_t when);
extern void curses_preference_update(const char *pref);
/* curswins.c */
extern WINDOW *curses_create_window(int width, int height, orient orientation);
extern void curses_destroy_win(WINDOW *win);
extern WINDOW *curses_get_nhwin(winid wid);
extern void curses_add_nhwin(winid wid, int height, int width, int y,
int x, orient orientation, boolean border);
extern void curses_add_wid(winid wid);
extern void curses_refresh_nhwin(winid wid);
extern void curses_refresh_nethack_windows(void);
extern void curses_del_nhwin(winid wid);
extern void curses_del_wid(winid wid);
extern void curses_putch(winid wid, int x, int y, int ch, int color, int attrs);
extern void curses_get_window_size(winid wid, int *height, int *width);
extern boolean curses_window_has_border(winid wid);
extern boolean curses_window_exists(winid wid);
extern int curses_get_window_orientation(winid wid);
extern void curses_get_window_xy(winid wid, int *x, int *y);
extern void curses_puts(winid wid, int attr, const char *text);
extern void curses_clear_nhwin(winid wid);
extern void curses_alert_win_border(winid wid, boolean onoff);
extern void curses_alert_main_borders(boolean onoff);
extern void curses_draw_map(int sx, int sy, int ex, int ey);
extern boolean curses_map_borders(int *sx, int *sy, int *ex, int *ey,
int ux, int uy);
/* cursmisc.c */
extern int curses_read_char(void);
extern void curses_toggle_color_attr(WINDOW *win, int color, int attr, int onoff);
extern void curses_bail(const char *mesg);
extern winid curses_get_wid(int type);
extern char *curses_copy_of(const char *s);
extern int curses_num_lines(const char *str, int width);
extern char *curses_break_str(const char *str, int width, int line_num);
extern char *curses_str_remainder(const char *str, int width, int line_num);
extern boolean curses_is_menu(winid wid);
extern boolean curses_is_text(winid wid);
extern int curses_convert_glyph(int ch, int glyph);
extern void curses_move_cursor(winid wid, int x, int y);
extern void curses_prehousekeeping(void);
extern void curses_posthousekeeping(void);
extern void curses_view_file(const char *filename, boolean must_exist);
extern void curses_rtrim(char *str);
extern int curses_get_count(int first_digit);
extern int curses_convert_attr(int attr);
extern int curses_read_attrs(char *attrs);
extern int curses_convert_keys(int key);
extern int curses_get_mouse(int *mousex, int *mousey, int *mod);
/* cursdial.c */
extern void curses_line_input_dialog(const char *prompt, char *answer, int buffer);
extern int curses_character_input_dialog(const char *prompt, const char *choices, CHAR_P def);
extern int curses_ext_cmd(void);
extern void curses_create_nhmenu(winid wid);
extern void curses_add_nhmenu_item(winid wid, int glyph, const ANY_P *identifier,
CHAR_P accelerator, CHAR_P group_accel, int attr, const char *str,
BOOLEAN_P presel);
extern void curses_finalize_nhmenu(winid wid, const char *prompt);
extern int curses_display_nhmenu(winid wid, int how, MENU_ITEM_P **_selected);
extern boolean curses_menu_exists(winid wid);
extern void curses_del_menu(winid wid);
/* cursstat.c */
extern void curses_status_init(void);
extern void curses_status_update(int, genericptr_t, int, int, int, unsigned long *);
/* extern attr_t curses_color_attr(int nh_color, int bg_color); */
/* extern void curses_update_stats(void); */
/* extern void curses_decrement_highlight(void); */
/* cursinvt.c */
extern void curses_update_inv(void);
extern void curses_add_inv(int, int, CHAR_P, attr_t, const char *);
/* cursinit.c */
extern void curses_create_main_windows(void);
extern void curses_init_nhcolors(void);
extern void curses_choose_character(void);
extern int curses_character_dialog(const char** choices, const char *prompt);
extern void curses_init_options(void);
extern void curses_display_splash_window(void);
extern void curses_cleanup(void);
/* cursmesg.c */
extern void curses_message_win_puts(const char *message, boolean recursed);
extern int curses_block(boolean require_tab); /* for MSGTYPE=STOP */
extern int curses_more(void);
extern void curses_clear_unhighlight_message_window(void);
extern void curses_message_win_getline(const char *prompt, char *answer, int buffer);
extern void curses_last_messages(void);
extern void curses_init_mesg_history(void);
extern void curses_prev_mesg(void);
extern void curses_count_window(const char *count_text);
#endif /* WINCURS_H */

View File

@@ -216,7 +216,11 @@ extern
after updating status window fields */
#define WC2_RESET_STATUS 0x0100L /* 09 call status_update(BL_RESET) to indicate
draw everything */
/* 23 free bits */
#define WC2_TERM_SIZE 0x0100L /* 10 support setting terminal size */
#define WC2_WINDOWBORDERS 0x0200L /* 11 display borders on nh windows */
#define WC2_PETATTR 0x0400L /* 12 attributes for hilite_pet */
#define WC2_GUICOLOR 0x0800L /* 13 display colours outside map win */
/* 19 free bits */
#define ALIGN_LEFT 1
#define ALIGN_RIGHT 2

View File

@@ -533,7 +533,7 @@ doextlist(VOID_ARGS)
return 0;
}
#ifdef TTY_GRAPHICS
#if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS)
#define MAX_EXT_CMD 200 /* Change if we ever have more ext cmds */
/*

View File

@@ -264,6 +264,10 @@ void NDECL((*ibmgraphics_mode_callback)) = 0; /* set in tty_start_screen() */
void NDECL((*ascgraphics_mode_callback)) = 0; /* set in tty_start_screen() */
#endif
#ifdef CURSES_GRAPHICS
void NDECL((*cursesgraphics_mode_callback)) = 0;
#endif
/*
* Convert the given character to an object class. If the character is not
* recognized, then MAXOCLASSES is returned. Used in detect.c, invent.c,
@@ -495,6 +499,10 @@ int nondefault;
if (SYMHANDLING(H_DEC) && decgraphics_mode_callback)
(*decgraphics_mode_callback)();
#endif
# ifdef CURSES_GRAPHICS
if (SYMHANDLING(H_CURS) && cursesgraphics_mode_callback)
(*cursesgraphics_mode_callback)();
# endif
} else
init_symbols();
}
@@ -544,9 +552,10 @@ boolean name_too;
* to this array at the matching offset.
*/
const char *known_handling[] = {
"UNKNOWN", /* H_UNK */
"IBM", /* H_IBM */
"DEC", /* H_DEC */
"UNKNOWN", /* H_UNK */
"IBM", /* H_IBM */
"DEC", /* H_DEC */
"CURS", /* H_CURS */
(const char *) 0,
};

View File

@@ -28,6 +28,10 @@ NEARDATA struct instance_flags iflags; /* provide linkage */
#define PREFER_TILED FALSE
#endif
#ifdef CURSES_GRAPHICS
extern int curses_read_attrs(char *attrs);
#endif
enum window_option_types {
MESSAGE_OPTION = 1,
STATUS_OPTION,
@@ -110,7 +114,7 @@ static struct Bool_Opt {
{ "confirm", &flags.confirm, TRUE, SET_IN_GAME },
{ "dark_room", &flags.dark_room, TRUE, SET_IN_GAME },
{ "eight_bit_tty", &iflags.wc_eight_bit_input, FALSE, SET_IN_GAME }, /*WC*/
#ifdef TTY_GRAPHICS
#if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS)
{ "extmenu", &iflags.extmenu, FALSE, SET_IN_GAME },
#else
{ "extmenu", (boolean *) 0, FALSE, SET_IN_FILE },
@@ -130,6 +134,7 @@ static struct Bool_Opt {
{ "force_invmenu", &iflags.force_invmenu, FALSE, SET_IN_GAME },
{ "fullscreen", &iflags.wc2_fullscreen, FALSE, SET_IN_FILE },
{ "goldX", &iflags.goldX, FALSE, SET_IN_GAME },
{ "guicolor", &iflags.wc2_guicolor, TRUE, SET_IN_GAME},
{ "help", &flags.help, TRUE, SET_IN_GAME },
{ "herecmd_menu", &iflags.herecmd_menu, FALSE, SET_IN_GAME },
{ "hilite_pet", &iflags.wc_hilite_pet, FALSE, SET_IN_GAME }, /*WC*/
@@ -161,7 +166,11 @@ static struct Bool_Opt {
{ "menu_overlay", (boolean *) 0, FALSE, SET_IN_FILE },
#endif
{ "monpolycontrol", &iflags.mon_polycontrol, FALSE, SET_IN_WIZGAME },
#ifdef CURSES_GRAPHICS
{ "mouse_support", &iflags.wc_mouse_support, FALSE, DISP_IN_GAME }, /*WC*/
#else
{ "mouse_support", &iflags.wc_mouse_support, TRUE, DISP_IN_GAME }, /*WC*/
#endif
#ifdef NEWS
{ "news", &iflags.news, TRUE, DISP_IN_GAME },
#else
@@ -326,7 +335,7 @@ static struct Comp_Opt {
{ "monsters", "the symbols to use for monsters", MAXMCLASSES,
SET_IN_FILE },
{ "msghistory", "number of top line messages to save", 5, DISP_IN_GAME },
#ifdef TTY_GRAPHICS
#if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS)
{ "msg_window", "the type of message window required", 1, SET_IN_GAME },
#else
{ "msg_window", "the type of message window required", 1, SET_IN_FILE },
@@ -352,6 +361,7 @@ static struct Comp_Opt {
#endif
{ "paranoid_confirmation", "extra prompting in certain situations", 28,
SET_IN_GAME },
{ "petattr", "attributes for highlighting pets", 12, SET_IN_FILE },
{ "pettype", "your preferred initial pet type", 4, DISP_IN_GAME },
{ "pickup_burden", "maximum burden picked up before prompt", 20,
SET_IN_GAME },
@@ -386,6 +396,13 @@ static struct Comp_Opt {
20, SET_IN_GAME },
#else
{ "statushilites", "highlight control", 20, SET_IN_FILE },
#endif
#ifdef CURSES_GRAPHICS
{ "statuslines",
"0,1,2 = classic behavior, 3 = alternative behavior",
20, DISP_IN_GAME },
#else
{ "statuslines", "# of status lines", 20, SET_IN_FILE },
#endif
{ "symset", "load a set of display symbols from the symbols file", 70,
SET_IN_GAME },
@@ -427,6 +444,9 @@ static struct Comp_Opt {
#ifdef BACKWARD_COMPAT
{ "DECgraphics", "load DECGraphics display symbols", 70, SET_IN_FILE },
{ "IBMgraphics", "load IBMGraphics display symbols", 70, SET_IN_FILE },
#ifdef CURSES_GRAPHICS
{"cursesgraphics", "load curses display symbols", 70, SET_IN_FILE},
#endif
#ifdef MAC_GRAPHICS_ENV
{ "Macgraphics", "load MACGraphics display symbols", 70, SET_IN_FILE },
#endif
@@ -3462,6 +3482,8 @@ boolean tinitial, tfrom_file;
*/
fullname = "windowtype";
if (match_optname(opts, fullname, 3, TRUE)) {
if (iflags.windowtype_locked)
return retval;
if (duplicate)
complain_about_duplicate(opts, 1);
if (negated) {
@@ -3509,6 +3531,74 @@ boolean tinitial, tfrom_file;
bad_negation(fullname, TRUE);
return retval;
}
#ifdef CURSES_GRAPHICS
/* WINCAP2
* term_cols:amount */
fullname = "term_cols";
if (match_optname(opts, fullname, sizeof("term_cols")-1, TRUE)) {
op = string_for_opt(opts, negated);
iflags.wc2_term_cols = atoi(op);
if (negated)
bad_negation(fullname, FALSE);
return retval;
}
/* WINCAP2
* term_rows:amount */
fullname = "term_rows";
if (match_optname(opts, fullname, sizeof("term_rows")-1, TRUE)) {
op = string_for_opt(opts, negated);
iflags.wc2_term_rows = atoi(op);
if (negated)
bad_negation(fullname, FALSE);
return retval;
}
/* WINCAP2
* petattr:string */
fullname = "petattr";
if (match_optname(opts, fullname, sizeof("petattr")-1, TRUE)) {
op = string_for_opt(opts, negated);
if (op && !negated) {
#ifdef CURSES_GRAPHICS
iflags.wc2_petattr = curses_read_attrs(op);
if (!curses_read_attrs(op))
config_error_add("Unknown %s parameter '%s'", fullname, opts);
return FALSE;
#else
/* non-curses windowports will not use this flag anyway
* but the above will not compile if we don't have curses.
* Just set it to a sensible default: */
iflags.wc2_petattr = ATR_INVERSE
#endif
} else if (negated) bad_negation(fullname, TRUE);
return retval;
}
/* WINCAP2
* windowborders:n */
fullname = "windowborders";
if (match_optname(opts, fullname, sizeof("windowborders")-1, TRUE)) {
op = string_for_opt(opts, negated);
if (negated && op)
bad_negation(fullname, TRUE);
else {
if (negated)
iflags.wc2_windowborders = 2; /* Off */
else if (!op)
iflags.wc2_windowborders = 1; /* On */
else /* Value supplied */
iflags.wc2_windowborders = atoi(op);
if ((iflags.wc2_windowborders > 3)
|| (iflags.wc2_windowborders < 1)) {
iflags.wc2_windowborders = 0;
config_error_add(
"Badoption - windowborders %s.", opts);
}
}
return retval;
}
#endif
/* menustyle:traditional or combination or full or partial */
fullname = "menustyle";
@@ -3852,6 +3942,10 @@ boolean tinitial, tfrom_file;
status_initialize(REASSESS_ONLY);
need_redraw = TRUE;
#endif
#ifdef CURSES_GRAPHICS
} else if ((boolopt[i].addr) == &iflags.cursesgraphics) {
need_redraw = TRUE;
#endif
#ifdef TEXTCOLOR
} else if (boolopt[i].addr == &iflags.use_color) {
need_redraw = TRUE;
@@ -5514,6 +5608,18 @@ char *buf;
symset[PRIMARY].name ? symset[PRIMARY].name : "default");
if (currentgraphics == PRIMARY && symset[PRIMARY].name)
Strcat(buf, ", active");
#ifdef CURSES_GRAPHICS
} else if (!strcmp(optname, "term_cols")) {
if (iflags.wc2_term_cols)
Sprintf(buf, "%d", iflags.wc2_term_cols);
else
Strcpy(buf, defopt);
} else if (!strcmp(optname, "term_rows")) {
if (iflags.wc2_term_rows)
Sprintf(buf, "%d",iflags.wc2_term_rows);
else
Strcpy(buf, defopt);
#endif
} else if (!strcmp(optname, "tile_file")) {
Sprintf(buf, "%s",
iflags.wc_tile_file ? iflags.wc_tile_file : defopt);
@@ -5550,6 +5656,13 @@ char *buf;
ttycolors[CLR_YELLOW], ttycolors[CLR_BRIGHT_BLUE],
ttycolors[CLR_BRIGHT_MAGENTA], ttycolors[CLR_BRIGHT_CYAN]);
#endif /* VIDEOSHADES */
#ifdef CURSES_GRAPHICS
} else if (!strcmp(optname,"windowborders")) {
Sprintf(buf, "%s",
iflags.wc2_windowborders == 1 ? "1=on" :
iflags.wc2_windowborders == 2 ? "2=off" :
iflags.wc2_windowborders == 3 ? "3=auto" : defopt);
#endif
} else if (!strcmp(optname, "windowtype")) {
Sprintf(buf, "%s", windowprocs.name);
} else if (!strcmp(optname, "windowcolors")) {
@@ -6217,6 +6330,9 @@ static struct wc_Opt wc2_options[] = {
{ "status hilite rules", WC2_HILITE_STATUS },
/* statushilites doesn't have its own bit */
{ "statushilites", WC2_HILITE_STATUS },
#ifdef CURSES_GRAPHICS
{"windowborders", WC2_WINDOWBORDERS},
#endif
{ (char *) 0, 0L }
};

View File

@@ -6,7 +6,7 @@
#include "hack.h"
#if defined(TTY_GRAPHICS) || defined(X11_GRAPHICS) || defined(GEM_GRAPHICS) \
|| defined(MSWIN_GRAPHICS) || defined(DUMPLOG)
|| defined(MSWIN_GRAPHICS) || defined(DUMPLOG) || defined(CURSES_GRAPHICS)
#define TEXT_TOMBSTONE
#endif
#if defined(mac) || defined(__BEOS__) || defined(WIN32_GRAPHICS)

View File

@@ -6,6 +6,9 @@
#ifdef TTY_GRAPHICS
#include "wintty.h"
#endif
#ifdef CURSES_GRAPHICS
extern struct window_procs curses_procs;
#endif
#ifdef X11_GRAPHICS
/* Cannot just blindly include winX.h without including all of X11 stuff
and must get the order of include files right. Don't bother. */
@@ -92,6 +95,9 @@ static struct win_choices {
#ifdef TTY_GRAPHICS
{ &tty_procs, win_tty_init CHAINR(0) },
#endif
#ifdef CURSES_GRAPHICS
{ &curses_procs, 0 },
#endif
#ifdef X11_GRAPHICS
{ &X11_procs, win_X11_init CHAINR(0) },
#endif

View File

@@ -98,8 +98,12 @@ char *argv[];
nethack_enter(argc, argv);
sys_early_init();
#ifdef WIN32
#if defined(WIN32) && defined(TTY_GRAPHICS)
Strcpy(default_window_sys, "tty");
#else
#if defined(CURSES_GRAPHICS)
Strcpy(default_window_sys, "curses");
#endif
#endif
resuming = pcmain(argc, argv);
@@ -173,6 +177,10 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/
choose_windows(DEFAULT_WINDOW_SYS);
#else
choose_windows(default_window_sys);
if (argc > 1
&& !strcmpi(default_window_sys, "mswin")
&& strstri(argv[0], "nethackw.exe"))
iflags.windowtype_locked = TRUE;
#endif
#if !defined(AMIGA) && !defined(GNUDOS)
@@ -500,9 +508,9 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/
} else {
iflags.use_background_glyph = TRUE;
}
#endif
#endif
#endif
#endif /* TTY_GRAPHICS */
#endif /* WIN32 */
#endif /* MSDOS || WIN32 */
#if defined(MSDOS) || defined(WIN32)
init_nhwindows(&argc, argv);

View File

@@ -146,6 +146,15 @@ SYSOBJ = ioctl.o unixmain.o unixtty.o unixunix.o unixres.o
#LINK = gcc
#LFLAGS = -Xlinker -soname=_APP_
# Compile with PDCurses installed in a separate directory that doesn't
# conflict with the system curses/ncurses library
#CFLAGS = -O -I../include -I/usr/local/include/pdcurses
# Same as above, but for XCurses
#CFLAGS = -O -DXCURSES -I../include -I/usr/local/include/pdcurses
# Compile against system curses library, such as ncurses
#CFLAGS = -O -I../include
# Only used for the Gnome interface.
# When including the Gnome interface, you need to include gnome specific
# directories. The ones given below is the usual spot for linux systems.
@@ -190,6 +199,14 @@ WINTTYSRC = ../win/tty/getline.c ../win/tty/termcap.c ../win/tty/topl.c \
../win/tty/wintty.c
WINTTYOBJ = getline.o termcap.o topl.o wintty.o
#
# Files for curses interface
WINCURSESSRC = ../win/curses/cursmain.c ../win/curses/curswins.c \
../win/curses/cursmisc.c ../win/curses/cursdial.c \
../win/curses/cursstat.c ../win/curses/cursinit.c \
../win/curses/cursmesg.c ../win/curses/cursinvt.c
WINCURSESOBJ = cursmain.o curswins.o cursmisc.o cursdial.o cursstat.o \
cursinit.o cursmesg.o cursinvt.o
#
# files for an X11 port
# (tile.c is a generated source file)
WINX11SRC = ../win/X11/Window.c ../win/X11/dialogs.c ../win/X11/winX.c \
@@ -243,12 +260,15 @@ WINBEOBJ =
#WINBESRC = ../win/BeOS/winbe.cpp ../win/BeOS/NHWindow.cpp \
# ../win/BeOS/NHMenuWindow.cpp ../win/BeOS/NHMapWindow.cpp tile.c
#WINBEOBJ = winbe.o NHWindow.o NHMenuWindow.o NHMapWindow.o tile.o
#
#
#WINSRC = $(WINTTYSRC)
#WINOBJ = $(WINTTYOBJ)
#
# Curses - Karl Garrison, Tangles
#WINSRC = $(WINCURSESSRC)
#WINOBJ = $(WINCURSESOBJ)
#
# on some systems the termcap library is in -ltermcap or -lcurses
# on 386 Xenix, the -ltermlib tputs() seems not to work; use -lcurses instead
# Sysatt uses shared library in lieu of this option
@@ -272,6 +292,7 @@ WINX11LIB = -lXaw -lXmu -lXext -lXt -lX11
# WINX11LIB = -lXaw -lXmu -lXext -lXt -lXpm -lX11 -lm
# WINX11LIB = -lXaw -lXmu -lXpm -lXext -lXt -lX11 -lSM -lICE -lm # BSD/OS 2.0
#
#
# libraries for Qt 3
WINQTLIB = -L$(QTDIR)/lib -lqt
#
@@ -292,9 +313,20 @@ WINGEMLIB = -le_gem -lgem
#
# libraries for BeOS
WINBELIB = -lbe
#
# libraries for curses port
# link with ncurses
WINCURSESLIB = -lncurses
# link with pdcurses for SDL, installed in a separate directory
#WINCURSESLIB = -L/usr/local/lib/pdcurses -lpdcurses -lSDL
# same as above, for XCurses
#WINCURSESLIB = -L/usr/local/lib/pdcurses -lXCurses -lXawM -lXmu -lXext -lXt -lX11
#
#WINLIB = $(WINTTYLIB)
#
# For Curses
#WINLIB = $(WINCURSESLIB)
#
# any other strange libraries your system needs (for Sysunix only -- the more
# specialized targets should already be right)
#
@@ -743,6 +775,29 @@ topl.o: ../win/tty/topl.c $(HACK_H) ../include/tcap.h
$(CC) $(CFLAGS) -c ../win/tty/topl.c
wintty.o: ../win/tty/wintty.c $(HACK_H) ../include/dlb.h ../include/tcap.h
$(CC) $(CFLAGS) -c ../win/tty/wintty.c
cursmain.o: ../win/curses/cursmain.c $(HACK_H) ../include/wincurs.h
$(CC) $(CFLAGS) -c ../win/curses/cursmain.c
curswins.o: ../win/curses/curswins.c $(HACK_H) ../include/func_tab.h \
../include/wincurs.h ../win/curses/curswins.h
$(CC) $(CFLAGS) -c ../win/curses/curswins.c
cursmisc.o: ../win/curses/cursmisc.c $(HACK_H) ../include/wincurs.h \
../win/curses/cursmisc.h
$(CC) $(CFLAGS) -c ../win/curses/cursmisc.c
cursdial.o: ../win/curses/cursdial.c $(HACK_H) ../include/func_tab.h \
../include/wincurs.h ../win/curses/cursdial.h
$(CC) $(CFLAGS) -c ../win/curses/cursdial.c
cursstat.o: ../win/curses/cursstat.c $(HACK_H) ../include/wincurs.h \
../win/curses/cursstat.h
$(CC) $(CFLAGS) -c ../win/curses/cursstat.c
cursinit.o: ../win/curses/cursinit.c $(HACK_H) ../include/wincurs.h \
../win/curses/cursinit.h
$(CC) $(CFLAGS) -c ../win/curses/cursinit.c
cursinvt.o: ../win/curses/cursinvt.c $(HACK_H) ../include/wincurs.h \
../win/curses/cursinvt.h
$(CC) $(CFLAGS) -c ../win/curses/cursinvt.c
cursmesg.o: ../win/curses/cursmesg.c $(HACK_H) ../include/wincurs.h \
../win/curses/cursmesg.h
$(CC) $(CFLAGS) -c ../win/curses/cursmesg.c
Window.o: ../win/X11/Window.c ../include/xwindowp.h ../include/xwindow.h \
$(CONFIG_H) ../include/lint.h
$(CC) $(CFLAGS) -c ../win/X11/Window.c

View File

@@ -27,14 +27,15 @@ CFLAGS+=-DTIMED_DELAY
CFLAGS+=-DHACKDIR=\"$(HACKDIR)\"
CFLAGS+=-DDUMPLOG
CFLAGS+=-DCONFIG_ERROR_SECURE=FALSE
CFLAGS+=-DCURSES_GRAPHICS
LINK=$(CC)
# Only needed for GLIBC stack trace:
LFLAGS=-rdynamic
WINSRC = $(WINTTYSRC)
WINOBJ = $(WINTTYOBJ)
WINLIB = $(WINTTYLIB)
WINSRC = $(WINTTYSRC) $(WINCURSESSRC)
WINOBJ = $(WINTTYOBJ) $(WINCURSESOBJ)
WINLIB = $(WINTTYLIB) $(WINCURSESLIB)
WINTTYLIB=-lcurses

View File

@@ -21,11 +21,13 @@
WANT_WIN_TTY=1
#WANT_WIN_X11=1
#WANT_WIN_QT=1
#WANT_WIN_CURSES=1
# 1a. What is the default window system?
WANT_DEFAULT=tty
#WANT_DEFAULT=x11
#WANT_DEFAULT=qt
#WANT_DEFAULT=curses
# 1b. If you set WANT_WIN_QT, you need to
# A) set QTDIR either here or in the environment to point to the Qt2 or Qt3
@@ -96,6 +98,13 @@ else # !WANT_WIN_TTY
CFLAGS += -DNOTTYGRAPHICS
endif # !WANT_WIN_TTY
ifdef WANT_WIN_CURSES
CFLAGS += -DCURSES_GRAPHICS
WINSRC += $(WINCURSESSRC)
WINOBJ += $(WINCURSESOBJ)
WINLIB += -lncurses
endif
ifdef WANT_WIN_X11
WINSRC += $(WINX11SRC)
WINOBJ += $(WINX11OBJ)

View File

@@ -5,7 +5,7 @@
NetHack 3.6 on a Windows system
(Windows 7/8.x/10 or later only. XP may work but is untested)
==============================================================
Last revision: $NHDT-Date: 1524317622 2018/04/21 13:33:42 $
Last revision: $NHDT-Date: 1542545993 2018/11/18 12:59:53 $
Credit for the porting of NetHack to the Win32 Console Subsystem goes to
the NT Porting Team started by Michael Allison.
@@ -47,6 +47,10 @@ of NetHack you wish to run.
The Visual Studio 2017 NetHack solution file can be found here:
win\win32\vs2017\NetHack.sln
Before executing the steps to build listed in the next paragraph,
decide if you want to include optional curses window-port. See
the note just below entitled "Optional curses window-port support."
So the steps are:
1. Launch the IDE.
2. Open the appropriate solution file.
@@ -60,6 +64,26 @@ using a "build.bat" batch file found in the same directory as the solution.
Open a developer command prompt for the version of Visual Studio you are
using. Change to the directory win\win32\vs2017 and run "build.bat".
<Optional curses window-port support>
Starting with 3.6.2, the community patch for a window-port that uses
curses was incorporated into the NetHack source code tree. That window-port,
which evolved from work originally done by Karl Garrison, has been used in
several NetHack variants and on nethack.alt.org and on
www.hardfought.org/nethack/.
If you want to include the curses window-port support in your Visual Studio
build, you will have to first obtain the PDCurses sources from
https://github.com/wmcbrine/PDCurses
and have them available prior to building NetHack. There are two ways to
enable curses window-port support during the VS build: Either set the
environment variable PDCURSES to a folder containing a PDCurses
repository/source-tree
OR
Place the PDCurses folder alongside the NetHack source repository prior
to proceeding with steps 1 through 5 above.
/-----------------------------------\
| Building Using Make |
\-----------------------------------/
@@ -151,7 +175,26 @@ Setting Up
source tree.
cd src
2. Make sure all the NetHack files are in the appropriate directory
2. Starting with 3.6.2, the community patch for a window-port that uses
curses was incorporated into the NetHack source code tree. That
window-port, which evolved from work originally done by Karl Garrison,
has been used in several NetHack variants and on nethack.alt.org and
on www.hardfought.org/nethack/.
If you want to include the optional curses window-port support in your
command line Makefile build, you will have to first obtain the
PDCurses sources from https://github.com/wmcbrine/PDCurses
and have that source code tree available prior to building NetHack.
Edit your Makefile and in Question 4 of the four decisions you can
make in there, uncomment these two lines:
ADD_CURSES=Y
PDCURSES_TOP=..\..\pdcurses
Adjust the PDCURSES_TOP macro so that it points to the correct
location for the top of the PDCurses source tree if it differs from
the path shown.
3. Make sure all the NetHack files are in the appropriate directory
structure. You should have a main directory with subdirectories
dat, doc, include, src, sys\share, sys\winnt, util, and binary (The
"binary" directory was created by nhsetup.bat earlier if you
@@ -164,26 +207,26 @@ Setting Up
they are not necessary for building the TTY version for the Win32
console subsystem. You can delete them to save space.
Required Directories for a Win32 Console NetHack:
Required Directories for a Win32 Console NetHack build:
top
|
----------------------------------------------------/ /-----
| | | | | | | |
util dat doc include src sys win binary
| |
------ -----
| | |
share winnt tty
top -------------(optional) ----------------
| |
------------------------------------------------- pdcurses-top
| | | | | | | | |
util dat doc include src sys win pdcurses wincon
| |
------ -----
| | |
share winnt tty
Required Directories for a Win32 Graphical NetHack:
top
|
----------------------------------------------------/ /-----
| | | | | | | |
util dat doc include src sys win binary
-------------------------------------------------
| | | | | | |
util dat doc include src sys win
| |
------ -----
| | |
@@ -200,7 +243,7 @@ Setting Up
trouble with them, so you may need to convert them. The compiler
should not have any problems with them however.
3. Now go to the include subdirectory to check a couple of the header
4. Now go to the include subdirectory to check a couple of the header
files there. Things *should* work as they are, but since you have
probably set up your system in some sort of custom configuration it
doesn't hurt to check out the following:

View File

@@ -32,11 +32,12 @@
#========================================================================================
# BUILD DECISIONS SECTION
#
# There are currently only 3 decisions that you can choose to make, and none are
# required:
# There are currently only 4 decisions that you can choose to make, and none are
# absolutely required because defaults are in place:
# 1. Where do you want your build to end up?
# 2. Do you want debug information in the executable?
# 3. Do you want to explicitly override auto-detection of a 32-bit or 64-bit target?
# 4. Do you want to include the optional curses port?
#
#-----------------------------------------------------------------------------------------
#=========================================================================================
@@ -61,6 +62,20 @@ DEBUGINFO = Y
#TARGET_CPU=x64
#TARGET_CPU=x86
#---------------------------------------------------------------
# OPTIONAL - Curses window port support
#
# 4. Uncomment these and set them appropriately if you want to
# include curses port support alongside TTY support in your
# NetHack.exe binary.
#
# You'll have to set PDCURSES_H to the correct location of the
# PDCurses header (.h) files and PDCURSES_C to the location
# of your PDCurses C files.
#
#ADD_CURSES=Y
#PDCURSES_TOP=..\..\pdcurses
#
#==============================================================================
# This marks the end of the BUILD DECISIONS section.
#==============================================================================
@@ -85,16 +100,17 @@ DEBUGINFO = Y
# Source directories. Makedefs hardcodes these, don't change them.
#
INCL = ..\include # NetHack include files
DAT = ..\dat # NetHack data files
DOC = ..\doc # NetHack documentation files
UTIL = ..\util # Utility source
SRC = ..\src # Main source
SSYS = ..\sys\share # Shared system files
MSWSYS= ..\sys\winnt # mswin specific files
TTY = ..\win\tty # window port files (tty)
MSWIN = ..\win\win32 # window port files (WIN32)
WSHR = ..\win\share # Tile support files
INCL = ..\include # NetHack include files
DAT = ..\dat # NetHack data files
DOC = ..\doc # NetHack documentation files
UTIL = ..\util # Utility source
SRC = ..\src # Main source
SSYS = ..\sys\share # Shared system files
MSWSYS = ..\sys\winnt # mswin specific files
TTY = ..\win\tty # window port files (tty)
MSWIN = ..\win\win32 # window port files (win32)
WCURSES = ..\win\curses # window port files (curses)
WSHR = ..\win\share # Tile support files
#
# Object directory.
@@ -201,8 +217,6 @@ VSVER=2999 #untested future version
!include <win32.mak>
! ENDIF
#----------------------------------------------------------------
#These will be in the environment variables with one of the VS2017
#developer command prompts.
#VSCMD_ARG_HOST_ARCH=x64
@@ -232,94 +246,6 @@ CL_RECENT=-sdl
! ENDIF
!ENDIF
ccommon= -c -nologo -D"_CONSOLE" -D"_CRT_NONSTDC_NO_DEPRECATE" -D"_CRT_SECURE_NO_DEPRECATE" \
-D"_LIB" -D"_SCL_SECURE_NO_DEPRECATE" -D"_VC80_UPGRADE=0x0600" -D"DLB" -D"_MBCS" \
-DCRTAPI1=_cdecl -DCRTAPI2=_cdecl -D"NDEBUG" -D"YY_NO_UNISTD_H" -EHsc -fp:precise -Gd -GF -GS -Gy \
$(CL_RECENT) -WX- -Zc:forScope -Zc:wchar_t -Zi
cdebug= -analyze- -D"_DEBUG" -Gm -MTd -RTC1 -Od
crelease= -analyze- -D"_MBCS" -errorReport:prompt -Gm- -MT -O2 -Ot -Ox -Oy
lcommon= /NOLOGO /INCREMENTAL:NO
!IF "$(DEBUGINFO)" == "Y"
ldebug = /DEBUG
cflags1=$(ccommon) $(cdebug)
lflags1=$(lcommon) $(ldebug)
!ELSE
ldebug= /DEBUG
cflags1=$(ccommon) $(crelease)
lflags1=$(lcommon) $(ldebug)
!ENDIF
lflags= $(lflags1)
!IF "$(TARGET_CPU)" == "x86"
cflags = $(cflags1) -D_X86_=1 -DWIN32 -D_WIN32 -W3
scall = -Gz
!ELSEIF "$(TARGET_CPU)" == "x64"
cflags = $(cflags1) -D_AMD64_=1 -DWIN64 -D_WIN64 -DWIN32 -D_WIN32 -W4
scall =
!ENDIF
!IF ($(VSVER) >= 2012)
cflags = $(cflags:-W4=-W3)
!ENDIF
#More verbose warning output options below
#cflags = $(cflags:-W4=-wd4131
#cflags = $(cflags:-W4=-Wall)
#cflags = $(cflags:-W3=-wd4131
#cflags = $(cflags:-W3=-Wall)
# declarations for use on Intel x86 systems
!IF "$(TARGET_CPU)" == "x86"
DLLENTRY = @12
EXEVER=5.01
MACHINE=/MACHINE:X86
!ENDIF
# declarations for use on AMD64 systems
!IF "$(TARGET_CPU)" == "x64"
DLLENTRY =
EXEVER=5.02
MACHINE=/MACHINE:X64
!ENDIF
# for Windows applications
conlflags = $(lflags) -subsystem:console,$(EXEVER)
guilflags = $(lflags) -subsystem:windows,$(EXEVER)
dlllflags = $(lflags) -entry:_DllMainCRTStartup$(DLLENTRY) -dll
# basic subsystem specific libraries, less the C Run-Time
baselibs = kernel32.lib $(optlibs) $(winsocklibs) advapi32.lib gdi32.lib
winlibs = $(baselibs) user32.lib comdlg32.lib winspool.lib
# for Windows applications that use the C Run-Time libraries
conlibs = $(baselibs)
guilibs = $(winlibs)
#
INCLDIR= /I..\include /I..\sys\winnt
#==========================================
# Util builds
#==========================================
cflagsBuild = $(cflags) $(INCLDIR) $(WINPFLAG) $(DLBFLG)
lflagsBuild = $(lflags) $(conlibs) $(MACHINE)
#==========================================
# - Game build
#==========================================
LIBS= user32.lib winmm.lib $(ZLIB)
! IF ("$(USE_DLB)"=="Y")
DLB = nhdat
! ELSE
DLB =
! ENDIF
#==========================================
#================ MACROS ==================
@@ -406,6 +332,13 @@ REGEX = $(O)cppregex.o
TTYOBJ = $(O)topl.o $(O)getline.o $(O)wintty.o
!IFNDEF ADD_CURSES
CURSESOBJ=
!ELSE
CURSESOBJ= $(O)cursdial.o $(O)cursinit.o $(O)cursinvt.o $(O)cursmain.o \
$(O)cursmesg.o $(O)cursmisc.o $(O)cursstat.o $(O)curswins.o
!ENDIF
SOBJ = $(O)winnt.o $(O)pcsys.o $(O)pcunix.o \
$(SOUND) $(O)nhlan.o
@@ -414,7 +347,8 @@ OBJS = $(VOBJ01) $(VOBJ02) $(VOBJ03) $(VOBJ04) $(VOBJ05) \
$(VOBJ11) $(VOBJ12) $(VOBJ13) $(VOBJ14) $(VOBJ15) \
$(VOBJ16) $(VOBJ17) $(VOBJ18) $(VOBJ19) $(VOBJ20) \
$(VOBJ21) $(VOBJ22) $(VOBJ23) $(VOBJ24) $(VOBJ25) \
$(VOBJ26) $(VOBJ27) $(VOBJ28) $(VOBJ29) $(REGEX)
$(VOBJ26) $(VOBJ27) $(VOBJ28) $(VOBJ29) $(REGEX) \
$(CURSESOBJ)
GUIOBJ = $(O)mhaskyn.o $(O)mhdlg.o \
$(O)mhfont.o $(O)mhinput.o $(O)mhmain.o $(O)mhmap.o \
@@ -444,6 +378,35 @@ ALLOBJ = $(SOBJ) $(DLBOBJ) $(WOBJ) $(OBJS) $(VVOBJ)
OPTIONS_FILE = $(DAT)\options
!IF "$(ADD_CURSES)" == "Y"
#==========================================
# PDCurses build macros
#==========================================
PDCURSES_CURSES_H = $(PDCURSES_TOP)\curses.h
PDCURSES_CURSPRIV_H = $(PDCURSES_TOP)\curspriv.h
PDCURSES_HEADERS = $(PDCURSES_CURSES_H) $(PDCURSES_CURSPRIV_H)
PDCSRC = $(PDCURSES_TOP)\pdcurses
PDCWINCON = $(PDCURSES_TOP)\wincon
PDCLIBOBJS = $(O)addch.o $(O)addchstr.o $(O)addstr.o $(O)attr.o $(O)beep.o \
$(O)bkgd.o $(O)border.o $(O)clear.o $(O)color.o $(O)delch.o $(O)deleteln.o \
$(O)deprec.o $(O)getch.o $(O)getstr.o $(O)getyx.o $(O)inch.o $(O)inchstr.o \
$(O)initscr.o $(O)inopts.o $(O)insch.o $(O)insstr.o $(O)instr.o $(O)kernel.o \
$(O)keyname.o $(O)mouse.o $(O)move.o $(O)outopts.o $(O)overlay.o $(O)pad.o \
$(O)panel.o $(O)printw.o $(O)refresh.o $(O)scanw.o $(O)scr_dump.o $(O)scroll.o \
$(O)slk.o $(O)termattr.o $(O)terminfo.o $(O)touch.o $(O)util.o $(O)window.o \
$(O)debug.o
PDCOBJS = $(O)pdcclip.o $(O)pdcdisp.o $(O)pdcgetsc.o $(O)pdckbd.o $(O)pdcscrn.o \
$(O)pdcsetsc.o $(O)pdcutil.o
PDCLIB = $(O)\pdcurses.lib
PDCINCL = /I$(PDCURSES_TOP) /I$(PDCSRC) /I$(PDCWINCON)
!ELSE
PDCLIB =
!ENDIF
#==========================================
# Header file macros
#==========================================
@@ -481,6 +444,114 @@ TILE_H = ..\win\share\tile.h
DATABASE = $(DAT)\data.base
#==========================================
# More compiler setup post-macros
#==========================================
#----------------------------------------------------------------
!IF "$(ADD_CURSES)" == "Y"
#CURSESDEF=-D"PDC_DLL_BUILD" -D"CURSES_GRAPHICS" -D"CURSES_BRIEF_INCLUDE"
CURSESDEF=-D"CURSES_GRAPHICS" -D"CURSES_BRIEF_INCLUDE"
!ELSE
CURSDEF=
CURSESLIB=
CURSESINCL=
!ENDIF
ccommon= -c -nologo -D"_CONSOLE" -D"_CRT_NONSTDC_NO_DEPRECATE" -D"_CRT_SECURE_NO_DEPRECATE" \
-D"_LIB" -D"_SCL_SECURE_NO_DEPRECATE" -D"_VC80_UPGRADE=0x0600" -D"DLB" -D"_MBCS" \
-DCRTAPI1=_cdecl -DCRTAPI2=_cdecl -D"NDEBUG" -D"YY_NO_UNISTD_H" $(CURSESDEF) \
-EHsc -fp:precise -Gd -GF -GS -Gy \
$(CL_RECENT) -WX- -Zc:forScope -Zc:wchar_t -Zi
cdebug= -analyze- -D"_DEBUG" -Gm -MTd -RTC1 -Od
crelease= -analyze- -D"_MBCS" -errorReport:prompt -Gm- -MT -O2 -Ot -Ox -Oy
lcommon= /NOLOGO /INCREMENTAL:NO
!IF "$(DEBUGINFO)" == "Y"
ldebug = /DEBUG
cflags1=$(ccommon) $(cdebug)
lflags1=$(lcommon) $(ldebug)
!ELSE
ldebug= /DEBUG
cflags1=$(ccommon) $(crelease)
lflags1=$(lcommon) $(ldebug)
!ENDIF
lflags= $(lflags1)
!IF "$(TARGET_CPU)" == "x86"
cflags = $(cflags1) -D_X86_=1 -DWIN32 -D_WIN32 -W3
scall = -Gz
!ELSEIF "$(TARGET_CPU)" == "x64"
cflags = $(cflags1) -D_AMD64_=1 -DWIN64 -D_WIN64 -DWIN32 -D_WIN32 -W4
scall =
!ENDIF
!IF ($(VSVER) >= 2012)
cflags = $(cflags:-W4=-W3)
!ENDIF
#More verbose warning output options below
#cflags = $(cflags:-W4=-wd4131
#cflags = $(cflags:-W4=-Wall)
#cflags = $(cflags:-W3=-wd4131
#cflags = $(cflags:-W3=-Wall)
# declarations for use on Intel x86 systems
!IF "$(TARGET_CPU)" == "x86"
DLLENTRY = @12
EXEVER=5.01
MACHINE=/MACHINE:X86
!ENDIF
# declarations for use on AMD64 systems
!IF "$(TARGET_CPU)" == "x64"
DLLENTRY =
EXEVER=5.02
MACHINE=/MACHINE:X64
!ENDIF
# for Windows applications
conlflags = $(lflags) -subsystem:console,$(EXEVER)
guilflags = $(lflags) -subsystem:windows,$(EXEVER)
dlllflags = $(lflags) -entry:_DllMainCRTStartup$(DLLENTRY) -dll
# basic subsystem specific libraries, less the C Run-Time
baselibs = kernel32.lib $(optlibs) $(winsocklibs) advapi32.lib gdi32.lib
winlibs = $(baselibs) user32.lib comdlg32.lib winspool.lib
# for Windows applications that use the C Run-Time libraries
conlibs = $(baselibs)
guilibs = $(winlibs)
#
!IFNDEF ADD_CURSES
INCLDIR= /I..\include /I..\sys\winnt
!ELSE
INCLDIR= /I..\include /I..\sys\winnt /I$(CURSESINCL)
!ENDIF
#==========================================
# Util builds
#==========================================
cflagsBuild = $(cflags) $(INCLDIR) $(WINPFLAG) $(DLBFLG)
lflagsBuild = $(lflags) $(conlibs) $(MACHINE)
#==========================================
# - Game build
#==========================================
LIBS= user32.lib winmm.lib $(ZLIB) $(CURSESLIB)
! IF ("$(USE_DLB)"=="Y")
DLB = nhdat
! ELSE
DLB =
! ENDIF
#==========================================
#================ RULES ==================
#==========================================
@@ -552,6 +623,29 @@ DATABASE = $(DAT)\data.base
{$(MSWIN)}.c{$(OBJ)}.o:
@$(cc) $(cflagsBuild) -Fo$@ $<
#==========================================
# Rules for files in win\curses
#==========================================
{$(WCURSES)}.c{$(OBJ)}.o:
@$(cc) $(PDCINCL) $(cflagsBuild) -Fo$@ $<
#{$(WCURSES)}.txt{$(DAT)}.txt:
# @copy $< $@
#==========================================
# Rules for files in PDCurses
#==========================================
{$(PDCURSES_TOP)}.c{$(OBJ)}.o:
@$(cc) $(PDCINCL) $(cflagsBuild) -Fo$@ $<
{$(PDCSRC)}.c{$(OBJ)}.o:
@$(cc) $(PDCINCL) $(cflagsBuild) -Fo$@ $<
{$(PDCWINCON)}.c{$(OBJ)}.o:
@$(cc) $(PDCINCL) $(cflagsBuild) -Fo$@ $<
#==========================================
#=============== TARGETS ==================
#==========================================
@@ -592,7 +686,7 @@ $(O)install.tag: $(DAT)\data $(DAT)\rumors $(DAT)\dungeon \
if exist $(DOC)\nethack.txt copy $(DOC)\nethack.txt $(GAMEDIR)\NetHack.txt
@if exist $(GAMEDIR)\NetHack.PDB echo NOTE: You may want to remove $(GAMEDIR:\=/)/NetHack.PDB to conserve space
@if exist $(GAMEDIR)\NetHackW.PDB echo NOTE: You may want to remove $(GAMEDIR:\=/)/NetHackW.PDB to conserve space
-copy $(MSWSYS)\defaults.nh $(GAMEDIR)\defaults.nh
-if not exist $(GAMEDIR)\defaults.nh copy $(MSWSYS)\defaults.nh $(GAMEDIR)\defaults.nh
-if not exist $(GAMEDIR)\record. goto>$(GAMEDIR)\record.
echo install done > $@
@@ -702,12 +796,12 @@ GAMEOBJ=$(GAMEOBJ:^ =^
# objs: $(GAMEOBJ) $(TTYOBJ) $(O)tile.o $(O)guistub.o
$(GAMEDIR)\NetHack.exe : $(O)gamedir.tag $(O)tile.o $(O)nttty.o $(O)guistub.o \
$(GAMEDIR)\NetHack.exe : $(O)gamedir.tag $(PDCLIB) $(O)tile.o $(O)nttty.o $(O)guistub.o \
$(ALLOBJ) $(TTYOBJ) $(GUIOBJ) $(O)console.res $(KEYDLLS)
@if not exist $(GAMEDIR)\*.* mkdir $(GAMEDIR)
@echo Linking $(@:\=/)
$(link) $(lflagsBuild) $(conlflags) /STACK:2048 /PDB:$(GAMEDIR)\$(@B).PDB /MAP:$(O)$(@B).MAP \
$(LIBS) $(conlibs) -out:$@ @<<$(@B).lnk
$(LIBS) $(PDCLIB) $(conlibs) -out:$@ @<<$(@B).lnk
$(GAMEOBJ)
$(TTYOBJ)
$(O)nttty.o
@@ -730,7 +824,7 @@ $(GAMEDIR)\NetHackW.exe : $(O)gamedir.tag $(O)tile.o $(O)ttystub.o \
@if not exist $(GAMEDIR)\*.* mkdir $(GAMEDIR)
@echo Linking $(@:\=/)
$(link) $(lflagsBuild) $(guilflags) /STACK:2048 /PDB:$(GAMEDIR)\$(@B).PDB \
/MAP:$(O)$(@B).MAP $(LIBS) $(guilibs) $(COMCTRL) -out:$@ @<<$(@B).lnk
/MAP:$(O)$(@B).MAP $(LIBS) $(PDCLIB) $(guilibs) $(COMCTRL) -out:$@ @<<$(@B).lnk
$(GAMEOBJ)
$(GUIOBJ)
$(O)tile.o
@@ -987,6 +1081,12 @@ $(O)envchk.tag: $(O)obj.tag
! ELSE
@echo Windows x86 32-bit target build
! ENDIF
!IFDEF TTYOBJ
@echo tty window support included
! IF "$(ADD_CURSES)"=="Y"
@echo curses window support also included
! ENDIF
!ENDIF
! IF "$(CL)"!=""
# @echo Warning, the CL Environment variable is defined:
# @echo CL=$(CL)
@@ -1157,6 +1257,13 @@ $(O)tile2bmp.o: $(WSHR)\tile2bmp.c $(HACK_H) $(TILE_H) $(MSWSYS)\win32api.h
$(O)til2bm32.o: $(WSHR)\tile2bmp.c $(HACK_H) $(TILE_H) $(MSWSYS)\win32api.h
@$(cc) $(cflagsBuild) -I$(WSHR) /DPACKED_FILE /DTILE_X=32 /DTILE_Y=32 /Fo$@ $(WSHR)\tile2bmp.c
#==========================================
# PDCurses
#==========================================
$(O)\pdcurses.lib : $(PDCLIBOBJS) $(PDCOBJS)
lib -nologo /out:$@ $(PDCLIBOBJS) $(PDCOBJS)
#==========================================
# Housekeeping
#==========================================
@@ -1241,6 +1348,9 @@ spotless: clean
if exist $(U)tilemap.exe del $(U)tilemap.exe
if exist $(U)uudecode.exe del $(U)uudecode.exe
if exist $(U)dlb_main.exe del $(U)dlb_main.exe
!IF "$(ADD_CURSES)" == "Y"
if exist $(O)pdcurses.lib del $(O)pdcurses.lib
!ENDIF
clean:
if exist $(O)*.o del $(O)*.o
if exist $(O)utility.tag del $(O)utility.tag
@@ -1372,6 +1482,17 @@ $(O)panic.o: $(U)panic.c $(CONFIG_H)
(O)cppregex.o: $(O)cppregex.cpp $(HACK_H)
@$(CC) $(cflagsBuild) -Fo$@ ..\sys\share\cppregex.cpp
#
# curses window port dependencies
#
$(O)\cursdial.o: $(WCURSES)\cursdial.c $(WCURSES)\cursdial.h $(INCL)\wincurs.h
$(O)\cursinit.c: $(WCURSES)\cursinit.c $(WCURSES)\cursinit.h $(INCL)\wincurs.h
$(O)\cursinvt.c: $(WCURSES)\cursinvt.c $(WCURSES)\cursinvt.h $(INCL)\wincurs.h
$(O)\cursmain.c: $(WCURSES)\cursmain.c $(WCURSES)\cursmain.h $(INCL)\wincurs.h
$(O)\cursmesg.c: $(WCURSES)\cursmesg.c $(WCURSES)\cursmesg.h $(INCL)\wincurs.h
$(O)\cursmisc.c: $(WCURSES)\cursmisc.c $(WCURSES)\cursmisc.h $(INCL)\wincurs.h
$(O)\cursstat.c: $(WCURSES)\cursstat.c $(WCURSES)\cursstat.h $(INCL)\wincurs.h
$(O)\curswins.c: $(WCURSES)\curswins.c $(WCURSES)\curswins.h $(INCL)\wincurs.h
#
# The rest are stolen from sys/unix/Makefile.src,
# with the following changes:
@@ -1420,9 +1541,9 @@ $(O)topl.o: ..\win\tty\topl.c $(HACK_H) $(INCL)\tcap.h
@$(CC) $(cflagsBuild) -Fo$@ ..\win\tty\topl.c
$(O)wintty.o: ..\win\tty\wintty.c $(HACK_H) $(INCL)\dlb.h $(INCL)\tcap.h
@$(CC) $(cflagsBuild) -Fo$@ ..\win\tty\wintty.c
$(O)Window.o: ..\win\X11\Window.c $(INCL)\xwindowp.h $(INCL)\xwindow.h \
$(CONFIG_H)
@$(CC) $(cflagsBuild) -Fo$@ ..\win\X11\Window.c
#$(O)Window.o: ..\win\X11\Window.c $(INCL)\xwindowp.h $(INCL)\xwindow.h \
# $(CONFIG_H)
# @$(CC) $(cflagsBuild) -Fo$@ ..\win\X11\Window.c
$(O)dialogs.o: ..\win\X11\dialogs.c $(CONFIG_H)
@$(CC) $(cflagsBuild) -Fo$@ ..\win\X11\dialogs.c
$(O)winX.o: ..\win\X11\winX.c $(HACK_H) $(INCL)\winX.h $(INCL)\dlb.h \
@@ -1620,7 +1741,7 @@ $(O)vision.o: vision.c $(HACK_H) $(INCL)\vis_tab.h
$(O)weapon.o: weapon.c $(HACK_H)
$(O)were.o: were.c $(HACK_H)
$(O)wield.o: wield.c $(HACK_H)
$(O)windows.o: windows.c $(HACK_H) $(INCL)\wingem.h $(INCL)\winGnome.h
#$(O)windows.o: windows.c $(HACK_H) $(INCL)\wingem.h $(INCL)\winGnome.h
$(O)wizard.o: wizard.c $(HACK_H) $(INCL)\qtext.h
$(O)worm.o: worm.c $(HACK_H) $(INCL)\lev.h
$(O)worn.o: worn.c $(HACK_H)

View File

@@ -1650,6 +1650,9 @@ static struct win_info window_opts[] = {
#ifdef TTY_GRAPHICS
{ "tty", "traditional tty-based graphics" },
#endif
#ifdef CURSES_GRAPHICS
{ "curses", "terminal-based graphics using curses libraries" },
#endif
#ifdef X11_GRAPHICS
{ "X11", "X11" },
#endif

13
win/curses/Bugs.txt Normal file
View File

@@ -0,0 +1,13 @@
Here is a list of known issues with the curses interface at the time of
this writing. Send any others you discover to me (Karl Garrison) at
kgarrison@obox.com, along with how to reproduce the problem, if
possible. Missing features are listed in the file Todo.txt.
* Resizing a window to 80 columns or less causes a crash (PDCurses for
SDL and X11 only). Windows starting at this size and below do not
cause a crash, however.
* Cursor position is wrong on map for smaller terminal windows in all
versions of PDCurses (smaller than 80 width or 24 height). This is
due to an incomplete workaround for an issue with the wmove() function
in PDCurses.

116
win/curses/Readme.txt Normal file
View File

@@ -0,0 +1,116 @@
INTRO
=====
The "curses" windowport is a new text-based interface for NetHack,
using high-level curses routines to control the display. Currently, it
has been compiled and tested on Linux and Windows, but it should also
be portable to a number of other systems, such as other forms of UNIX,
Mac OS X, MSDOS, and OS/2.
Some features of this interface compared to the traditional tty
interface include:
* Dynamic window resizing (e.g. maximizing a terminal window)
* Dynamic configurable placement of status and message windows,
relative to the map
* Makes better use of larger terminal windows
* Fancier display (e.g. window borders, optional popup dialogs)
* "cursesgraphics" option for fancier line-drawing characters for
drawing the dungeon - this should work on most terminals/platforms
BUILDING
========
As of this writing code has been compiled on Linux and Windows.
UNIX/Linux build instructions: Follow the instructions in
sys/unix/Install.unx. By default, the Makefile is setup to compile
against ncurses. Edit Makefile.src if you wish to compile against a
different curses library, such as PDCurses for SDL.
Windows build instructions: If you are using Mingw32 as your compiler,
then follow the instructions in sys/winnt/Install.nt with the following
changes:
* After running nhsetup, manually copy the file cursmake.gcc to the
src/ subdirectory
* Instead of typing "mingw32-make -f Makefile.gcc install" you will
type "mingw32-make -f cursmake.gcc install"
If you are using a different compiler, you will have to manually modify
the appropriate Makefile to include the curses windowport files.
GAMEPLAY
========
Gameplay should be similar to the tty interface for NetHack; the
differences are primarily visual. This windowport supports dymanic
resizing of the terminal window, so you can play with it to see how it
looks best to you during a game. Also, the align_status and
align_message options may be set during the game, so you can experiment
to see what arraingement looks best to you.
For menus, in addition to the normal configurable keybindings for menu
navigation descrived in the Guidebook, you can use the right and left
arrows to to forward or backward one page, respectively, and the home
and end keys to go to the first and last pages, respectively.
Some configuration options that are specific to or relevant to the
curses windowport are shown below. Copy any of these that you like to
your nethack configuration file (e.g. .nethackrc for UNIX or
NetHack.cnf for Windows):
#
# Use this if the binary was compiled with multiple window interfaces,
# and curses is not the default
OPTIONS=windowtype:curses
#
# Set this for Windows systems, or for PDCurses for SDL on any system.
# The latter uses a cp437 font, which works with this option
#OPTIONS=IBMgraphics
#
# Set this if IBMgraphics above won't work for your system. Mutually
# exclusive with the above option, and should work on nearly any
# system.
OPTIONS=cursesgraphics
#
# Optionally specify the alignment of the message and status windows
# relative to the map window. If not specified, the code will default
# to the locations used in the tty interface: message window on top,
# and status window on bottom. Placing either of these on the right or
# left really only works well for winder terminal windows.
OPTIONS=align_message:bottom,align_status:right
#
# Use a small popup "window" for short prompts, e.g. "Really save?".
# If this is not set, the message window will be used for these as is
# done for the tty interface.
OPTIONS=popup_dialog
#
# Specify the initial window size for NetHack in units of characters.
# This is supported on PDCurses for SDL as well as PDCurses for
# Windows.
OPTIONS=term_cols:110,term_rows:32
#
# Controls the usage of window borders for the main NetHack windows
# (message, map, and status windows). A value of 1 forces the borders
# to be drawn, a value of 2 forces them to be off, and a value of 3
# allows the code to decide if they should be drawn based on the size
# of the terminal window.
OPTIONS=windowborders:3
CONTACT
=======
Please send any bug reports, suggestions, patches, or miscellaneous
feedback to me (Karl Garrison) at: kgarrison@pobox.com. Note that as
of this writing, I only have sporatic Internet access, so I may not get
back to you right away.
Happy Hacking!
Karl Garrison
March, 2009

146
win/curses/Todo.txt Normal file
View File

@@ -0,0 +1,146 @@
Below are some things I would like to see
NETHACK INTERFACE
=================
(These are the functions in cursmain.c called by the core NetHack code)
* Implement curses_rip for optional fancier color tombstone, as well
as one that will display correctly on smaller terminals.
* I am confused as to how mark_synch, wait_synch, and delay_output
should work. Help, please?
* Both PDCurses and Ncurses have mouse support, so the poskey function
could probably be implemented easily enough.
* raw_print is supposed to be able to work before the windowing system
has been initialized, as well as after, so I am unsure if curses
functions should be used here. Maybe check to see if initscr() has
been called, and use curses functions if so, and call initscr() from
there is not? Right now it is just a call to puts() with no support
for bold text.
DISPLAY
=======
* Consolidate refreshes of the display for smoother output.
* Horizontal scrollbar to show position for displays < 80 columns.
* Calls to getch() should probably be turned into wgetch() for the
appropriate window. This causes quirty cursor behavior under
PDCurses, however.
* Animation effects do not display properly - this could probably be
fixed with a correct implementation of the delay_output function.
* Support option to set forground and background colors for individual
windows
MENUS
=====
(cursdial.c)
* Menus need to be able to accept a count as input, e.g. to specifiy
how many items to drop.
* Currently the "preselected" flag for an individual menu item is
ignored. This should eventually be implemented.
* Menus probably should never overlap with the message window, since
the user sometmes needs to be able to see the messages while the menu
is active, e.g. when identifying multiple items one at a time.
* Perhaps allow for keyboard navigation of individual items, e.g.
using the up and down arrows to move among the selectable items, and
selecting individual items with the spacebar. Perhaps the tab key
could jump to the first selectable item after the next heading, and
shift-tab could jump to the first item of the previous heading.
MESSAGE WINDOW
==============
(cursmesg.c)
* Hitting Esc at the more prompt (which is '>>' for the curses
interface) should suppress the display of any further messages for
that turn like the tty interface does.
MAP WINDOW
==========
(curswins.c)
* The map window would probably benefit from a total redesign. Right
now, it uses a pad instead of a regular curses window, which causes a
number of special cases in the code to account for it, and a seperate
window behind it just to draw the border. It feels kludgy and
annoying!
STATUS WINDOW
=============
(cursstat.c)
* If the status window is on the right or left, then we have much more
room to work with for each item horizontally. Expand out some of the
labels for clarity. We can also list the current dungeon (e.g.
Gnomish Mines) and perhaps show thermometer bars for hit points and
magical power.
* Conversely, if we have a narrower dislay, compress some of the
labels to save space, and do not display some items that never or
rarely change (e.g. name, level and title, and alignment). Perhaps
display changes to these fields in the message window if they do
happen to change (e.g. converting to a new alignment).
* Maybe add some configuration options for what colors are used and
the like.
OTHER DIALOGS
=============
(cursdial.c)
* curses_yn_function needs to accept a count if a '#' is present in
choices string.
* Extended commands should be enterable letter-by-letter via a '#'
prompt if user does not have the extmenu command set to TRUE.
* Character selection should allow for a random selection of any or
all choices.
OTHER PLATFORMS
===============
* PDCurses also work on DOS and OS/2. PDCurses for SDL and ncurses
exist for Mac OS X. Porting the curses interface to these platforms
should not be too difficult.
MISC
====
* Update documentation and in-game help to describe the newly-added
options: cursesgraphics, term_rows, term_cols, and windowborders.
* Recognize "Alt" key in a platform-independant way to allow its use
to select extended commands. Currently this works for PDCurses. For
Ncurses, the Alt key works in an xterm or rxvt if the -meta8 flag is
passed, but I'd like to see a general way of detecting it.
* PDCurses has a function named "addrawch" to output the visual
representation of a control character to the screen without having the
control character affect the display otherwise. I would like to find
a way to accomplish the same thing via Ncurses to e.g. be able to use
a font like nh10 with the correct symbol mappings in an xterm or the
like.

1387
win/curses/cursdial.c Normal file

File diff suppressed because it is too large Load Diff

23
win/curses/cursdial.h Normal file
View File

@@ -0,0 +1,23 @@
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
#ifndef CURSDIAL_H
# define CURSDIAL_H
/* Global declarations */
void curses_line_input_dialog(const char *prompt, char *answer, int buffer);
int curses_character_input_dialog(const char *prompt, const char *choices,
CHAR_P def);
int curses_ext_cmd(void);
void curses_create_nhmenu(winid wid);
void curses_add_nhmenu_item(winid wid, int glyph, const ANY_P * identifier,
CHAR_P accelerator, CHAR_P group_accel, int attr,
const char *str, BOOLEAN_P presel);
void curses_finalize_nhmenu(winid wid, const char *prompt);
int curses_display_nhmenu(winid wid, int how, MENU_ITEM_P ** _selected);
boolean curses_menu_exists(winid wid);
void curses_del_menu(winid wid);
#endif /* CURSDIAL_H */

923
win/curses/cursinit.c Normal file
View File

@@ -0,0 +1,923 @@
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
#include "curses.h"
#include "hack.h"
#include "wincurs.h"
#include "cursinit.h"
#include "patchlevel.h"
#include <ctype.h>
/* Initialization and startup functions for curses interface */
/* Private declarations */
static void set_window_position(int *, int *, int *, int *, int,
int *, int *, int *, int *, int,
int, int);
/* array to save initial terminal colors for later restoration */
typedef struct nhrgb_type {
short r;
short g;
short b;
} nhrgb;
nhrgb orig_yellow;
nhrgb orig_white;
nhrgb orig_darkgray;
nhrgb orig_hired;
nhrgb orig_higreen;
nhrgb orig_hiyellow;
nhrgb orig_hiblue;
nhrgb orig_himagenta;
nhrgb orig_hicyan;
nhrgb orig_hiwhite;
/* Banners used for an optional ASCII splash screen */
#define NETHACK_SPLASH_A \
" _ _ _ _ _ _ "
#define NETHACK_SPLASH_B \
"| \\ | | | | | | | | | | "
#define NETHACK_SPLASH_C \
"| \\| | ___ | |_ | |__| | __ _ ___ | | __"
#define NETHACK_SPLASH_D \
"| . ` | / _ \\| __|| __ | / _` | / __|| |/ /"
#define NETHACK_SPLASH_E \
"| |\\ || __/| |_ | | | || (_| || (__ | < "
#define NETHACK_SPLASH_F \
"|_| \\_| \\___| \\__||_| |_| \\__,_| \\___||_|\\_\\"
/* win* is size and placement of window to change, x/y/w/h is baseline which can
decrease depending on alignment of win* in orientation.
Negative minh/minw: as much as possible, but at least as much as specified. */
static void
set_window_position(int *winx, int *winy, int *winw, int *winh, int orientation,
int *x, int *y, int *w, int *h, int border_space,
int minh, int minw)
{
*winw = *w;
*winh = *h;
/* Set window height/width */
if (orientation == ALIGN_TOP || orientation == ALIGN_BOTTOM) {
if (minh < 0) {
*winh = (*h - ROWNO - border_space);
if (-minh > *winh)
*winh = -minh;
} else
*winh = minh;
*h -= (*winh + border_space);
} else {
if (minw < 0) {
*winw = (*w - COLNO - border_space);
if (-minw > *winw)
*winw = -minw;
} else
*winw = minw;
*w -= (*winw + border_space);
}
*winx = *w + border_space + *x;
*winy = *h + border_space + *y;
/* Set window position */
if (orientation != ALIGN_RIGHT) {
*winx = *x;
if (orientation == ALIGN_LEFT)
*x += *winw + border_space;
}
if (orientation != ALIGN_BOTTOM) {
*winy = *y;
if (orientation == ALIGN_TOP)
*y += *winh + border_space;
}
}
/* Create the "main" nonvolitile windows used by nethack */
void
curses_create_main_windows()
{
int min_message_height = 1;
int message_orientation = 0;
int status_orientation = 0;
int border_space = 0;
int hspace = term_cols - 80;
boolean borders = FALSE;
switch (iflags.wc2_windowborders) {
case 1: /* On */
borders = TRUE;
break;
case 2: /* Off */
borders = FALSE;
break;
case 3: /* Auto */
if ((term_cols > 81) && (term_rows > 25)) {
borders = TRUE;
}
break;
default:
borders = FALSE;
}
if (borders) {
border_space = 2;
hspace -= border_space;
}
if ((term_cols - border_space) < COLNO) {
min_message_height++;
}
/* Determine status window orientation */
if (!iflags.wc_align_status || (iflags.wc_align_status == ALIGN_TOP)
|| (iflags.wc_align_status == ALIGN_BOTTOM)) {
if (!iflags.wc_align_status) {
iflags.wc_align_status = ALIGN_BOTTOM;
}
status_orientation = iflags.wc_align_status;
} else { /* left or right alignment */
/* Max space for player name and title horizontally */
if ((hspace >= 26) && (term_rows >= 24)) {
status_orientation = iflags.wc_align_status;
hspace -= (26 + border_space);
} else {
status_orientation = ALIGN_BOTTOM;
}
}
/* Determine message window orientation */
if (!iflags.wc_align_message || (iflags.wc_align_message == ALIGN_TOP)
|| (iflags.wc_align_message == ALIGN_BOTTOM)) {
if (!iflags.wc_align_message) {
iflags.wc_align_message = ALIGN_TOP;
}
message_orientation = iflags.wc_align_message;
} else { /* left or right alignment */
if ((hspace - border_space) >= 25) { /* Arbitrary */
message_orientation = iflags.wc_align_message;
} else {
message_orientation = ALIGN_TOP;
}
}
/* Figure out window positions and placements. Status and message area can be aligned
based on configuration. The priority alignment-wise is: status > msgarea > game.
Define everything as taking as much space as possible and shrink/move based on
alignment positions. */
int message_x = 0;
int message_y = 0;
int status_x = 0;
int status_y = 0;
int inv_x = 0;
int inv_y = 0;
int map_x = 0;
int map_y = 0;
int message_height = 0;
int message_width = 0;
int status_height = 0;
int status_width = 0;
int inv_height = 0;
int inv_width = 0;
int map_height = (term_rows - border_space);
int map_width = (term_cols - border_space);
boolean status_vertical = FALSE;
boolean msg_vertical = FALSE;
if (status_orientation == ALIGN_LEFT ||
status_orientation == ALIGN_RIGHT)
status_vertical = TRUE;
if (message_orientation == ALIGN_LEFT ||
message_orientation == ALIGN_RIGHT)
msg_vertical = TRUE;
int statusheight = 3;
if (iflags.statuslines < 3)
statusheight = 2;
/* Vertical windows have priority. Otherwise, priotity is:
status > inv > msg */
if (status_vertical)
set_window_position(&status_x, &status_y, &status_width, &status_height,
status_orientation, &map_x, &map_y, &map_width, &map_height,
border_space, statusheight, 26);
if (iflags.perm_invent) {
/* Take up all width unless msgbar is also vertical. */
int width = -25;
if (msg_vertical)
width = 25;
set_window_position(&inv_x, &inv_y, &inv_width, &inv_height,
ALIGN_RIGHT, &map_x, &map_y, &map_width, &map_height,
border_space, -1, width);
}
if (msg_vertical)
set_window_position(&message_x, &message_y, &message_width, &message_height,
message_orientation, &map_x, &map_y, &map_width, &map_height,
border_space, -1, -25);
/* Now draw horizontal windows */
if (!status_vertical)
set_window_position(&status_x, &status_y, &status_width, &status_height,
status_orientation, &map_x, &map_y, &map_width, &map_height,
border_space, statusheight, 26);
if (!msg_vertical)
set_window_position(&message_x, &message_y, &message_width, &message_height,
message_orientation, &map_x, &map_y, &map_width, &map_height,
border_space, -1, -25);
if (map_width > COLNO)
map_width = COLNO;
if (map_height > ROWNO)
map_height = ROWNO;
if (curses_get_nhwin(STATUS_WIN)) {
curses_del_nhwin(STATUS_WIN);
curses_del_nhwin(MESSAGE_WIN);
curses_del_nhwin(MAP_WIN);
curses_del_nhwin(INV_WIN);
clear();
}
curses_add_nhwin(STATUS_WIN, status_height, status_width, status_y,
status_x, status_orientation, borders);
curses_add_nhwin(MESSAGE_WIN, message_height, message_width, message_y,
message_x, message_orientation, borders);
if (iflags.perm_invent)
curses_add_nhwin(INV_WIN, inv_height, inv_width, inv_y, inv_x,
ALIGN_RIGHT, borders);
curses_add_nhwin(MAP_WIN, map_height, map_width, map_y, map_x, 0, borders);
refresh();
curses_refresh_nethack_windows();
/*
if (iflags.window_inited) {
curses_update_stats();
if (iflags.perm_invent)
curses_update_inventory();
} else {
iflags.window_inited = TRUE;
}
*/
}
/* Initialize curses colors to colors used by NetHack */
void
curses_init_nhcolors()
{
#ifdef TEXTCOLOR
if (has_colors()) {
use_default_colors();
init_pair(1, COLOR_BLACK, -1);
init_pair(2, COLOR_RED, -1);
init_pair(3, COLOR_GREEN, -1);
init_pair(4, COLOR_YELLOW, -1);
init_pair(5, COLOR_BLUE, -1);
init_pair(6, COLOR_MAGENTA, -1);
init_pair(7, COLOR_CYAN, -1);
init_pair(8, -1, -1);
{
int i;
int clr_remap[16] = {
COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW,
COLOR_BLUE,
COLOR_MAGENTA, COLOR_CYAN, -1, COLOR_WHITE,
COLOR_RED + 8, COLOR_GREEN + 8, COLOR_YELLOW + 8,
COLOR_BLUE + 8,
COLOR_MAGENTA + 8, COLOR_CYAN + 8, COLOR_WHITE + 8
};
for (i = 0; i < (COLORS >= 16 ? 16 : 8); i++) {
init_pair(17 + (i * 2) + 0, clr_remap[i], COLOR_RED);
init_pair(17 + (i * 2) + 1, clr_remap[i], COLOR_BLUE);
}
boolean hicolor = FALSE;
if (COLORS >= 16)
hicolor = TRUE;
/* Work around the crazy definitions above for more background colors... */
for (i = 0; i < (COLORS >= 16 ? 16 : 8); i++) {
init_pair((hicolor ? 49 : 9) + i, clr_remap[i], COLOR_GREEN);
init_pair((hicolor ? 65 : 33) + i, clr_remap[i], COLOR_YELLOW);
init_pair((hicolor ? 81 : 41) + i, clr_remap[i], COLOR_MAGENTA);
init_pair((hicolor ? 97 : 49) + i, clr_remap[i], COLOR_CYAN);
init_pair((hicolor ? 113 : 57) + i, clr_remap[i], COLOR_WHITE);
}
}
if (COLORS >= 16) {
init_pair(9, COLOR_WHITE, -1);
init_pair(10, COLOR_RED + 8, -1);
init_pair(11, COLOR_GREEN + 8, -1);
init_pair(12, COLOR_YELLOW + 8, -1);
init_pair(13, COLOR_BLUE + 8, -1);
init_pair(14, COLOR_MAGENTA + 8, -1);
init_pair(15, COLOR_CYAN + 8, -1);
init_pair(16, COLOR_WHITE + 8, -1);
}
if (can_change_color()) {
/* Preserve initial terminal colors */
color_content(COLOR_YELLOW, &orig_yellow.r, &orig_yellow.g,
&orig_yellow.b);
color_content(COLOR_WHITE, &orig_white.r, &orig_white.g,
&orig_white.b);
/* Set colors to appear as NetHack expects */
init_color(COLOR_YELLOW, 500, 300, 0);
init_color(COLOR_WHITE, 600, 600, 600);
if (COLORS >= 16) {
/* Preserve initial terminal colors */
color_content(COLOR_RED + 8, &orig_hired.r,
&orig_hired.g, &orig_hired.b);
color_content(COLOR_GREEN + 8, &orig_higreen.r,
&orig_higreen.g, &orig_higreen.b);
color_content(COLOR_YELLOW + 8, &orig_hiyellow.r,
&orig_hiyellow.g, &orig_hiyellow.b);
color_content(COLOR_BLUE + 8, &orig_hiblue.r,
&orig_hiblue.g, &orig_hiblue.b);
color_content(COLOR_MAGENTA + 8, &orig_himagenta.r,
&orig_himagenta.g, &orig_himagenta.b);
color_content(COLOR_CYAN + 8, &orig_hicyan.r,
&orig_hicyan.g, &orig_hicyan.b);
color_content(COLOR_WHITE + 8, &orig_hiwhite.r,
&orig_hiwhite.g, &orig_hiwhite.b);
/* Set colors to appear as NetHack expects */
init_color(COLOR_RED + 8, 1000, 500, 0);
init_color(COLOR_GREEN + 8, 0, 1000, 0);
init_color(COLOR_YELLOW + 8, 1000, 1000, 0);
init_color(COLOR_BLUE + 8, 0, 0, 1000);
init_color(COLOR_MAGENTA + 8, 1000, 0, 1000);
init_color(COLOR_CYAN + 8, 0, 1000, 1000);
init_color(COLOR_WHITE + 8, 1000, 1000, 1000);
# ifdef USE_DARKGRAY
if (COLORS > 16) {
color_content(CURSES_DARK_GRAY, &orig_darkgray.r,
&orig_darkgray.g, &orig_darkgray.b);
init_color(CURSES_DARK_GRAY, 300, 300, 300);
/* just override black colorpair entry here */
init_pair(1, CURSES_DARK_GRAY, -1);
}
# endif
} else {
/* Set flag to use bold for bright colors */
}
}
}
#endif
}
/* Allow player to pick character's role, race, gender, and alignment.
Borrowed from the Gnome window port. */
void
curses_choose_character()
{
int n, i, sel, count_off, pick4u;
int count = 0;
int cur_character = 0;
const char **choices;
int *pickmap;
char *prompt;
char pbuf[QBUFSZ];
char choice[QBUFSZ];
char tmpchoice[QBUFSZ];
prompt = build_plselection_prompt(pbuf, QBUFSZ, flags.initrole,
flags.initrace, flags.initgend,
flags.initalign);
/* This part is irritating: we have to strip the choices off of
the string and put them in a separate string in order to use
curses_character_input_dialog for this prompt. */
while (cur_character != '[') {
cur_character = prompt[count];
count++;
}
count_off = count;
while (cur_character != ']') {
tmpchoice[count - count_off] = prompt[count];
count++;
cur_character = prompt[count];
}
tmpchoice[count - count_off] = '\0';
lcase(tmpchoice);
while (!isspace(prompt[count_off])) {
count_off--;
}
prompt[count_off] = '\0';
sprintf(choice, "%s%c", tmpchoice, '\033');
if (strchr(tmpchoice, 't')) { /* Tutorial mode */
mvaddstr(0, 1, "New? Press t to enter a tutorial.");
}
/* Add capital letters as choices that aren't displayed */
for (count = 0; tmpchoice[count]; count++) {
tmpchoice[count] = toupper(tmpchoice[count]);
}
sprintf(choice, "%s%s", choice, tmpchoice);
/* prevent an unnecessary prompt */
rigid_role_checks();
if (!flags.randomall &&
(flags.initrole == ROLE_NONE || flags.initrace == ROLE_NONE ||
flags.initgend == ROLE_NONE || flags.initalign == ROLE_NONE)) {
pick4u = tolower(curses_character_input_dialog(prompt, choice, 'y'));
} else {
pick4u = 'y';
}
if (pick4u == 'q') { /* Quit or cancelled */
clearlocks();
curses_bail(0);
}
if (pick4u == 'y') {
flags.randomall = TRUE;
}
clear();
refresh();
if (!flags.randomall && flags.initrole < 0) {
/* select a role */
for (n = 0; roles[n].name.m; n++)
continue;
choices = (const char **) alloc(sizeof (char *) * (n + 1));
pickmap = (int *) alloc(sizeof (int) * (n + 1));
for (;;) {
for (n = 0, i = 0; roles[i].name.m; i++) {
if (ok_role(i, flags.initrace, flags.initgend, flags.initalign)) {
if (flags.initgend >= 0 && flags.female && roles[i].name.f)
choices[n] = roles[i].name.f;
else
choices[n] = roles[i].name.m;
pickmap[n++] = i;
}
}
if (n > 0)
break;
else if (flags.initalign >= 0)
flags.initalign = -1; /* reset */
else if (flags.initgend >= 0)
flags.initgend = -1;
else if (flags.initrace >= 0)
flags.initrace = -1;
else
panic("no available ROLE+race+gender+alignment combinations");
}
choices[n] = (const char *) 0;
if (n > 1)
sel =
curses_character_dialog(choices,
"Choose one of the following roles:");
else
sel = 0;
if (sel >= 0)
sel = pickmap[sel];
else if (sel == ROLE_NONE) { /* Quit */
clearlocks();
curses_bail(0);
}
free((genericptr_t) choices);
free((genericptr_t) pickmap);
} else if (flags.initrole < 0)
sel = ROLE_RANDOM;
else
sel = flags.initrole;
if (sel == ROLE_RANDOM) { /* Random role */
sel = pick_role(flags.initrace, flags.initgend,
flags.initalign, PICK_RANDOM);
if (sel < 0)
sel = randrole();
}
flags.initrole = sel;
/* 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)) {
if (flags.initrace == ROLE_RANDOM || flags.randomall) {
flags.initrace = pick_race(flags.initrole, flags.initgend,
flags.initalign, PICK_RANDOM);
if (flags.initrace < 0)
flags.initrace = randrace(flags.initrole);
} else {
/* Count the number of valid races */
n = 0; /* number valid */
for (i = 0; races[i].noun; i++) {
if (ok_race(flags.initrole, i, flags.initgend, flags.initalign))
n++;
}
if (n == 0) {
for (i = 0; races[i].noun; i++) {
if (validrace(flags.initrole, i))
n++;
}
}
choices = (const char **) alloc(sizeof (char *) * (n + 1));
pickmap = (int *) alloc(sizeof (int) * (n + 1));
for (n = 0, i = 0; races[i].noun; i++) {
if (ok_race(flags.initrole, i, flags.initgend, flags.initalign)) {
choices[n] = races[i].noun;
pickmap[n++] = i;
}
}
choices[n] = (const char *) 0;
/* Permit the user to pick, if there is more than one */
if (n > 1)
sel =
curses_character_dialog(choices,
"Choose one of the following races:");
else
sel = 0;
if (sel >= 0)
sel = pickmap[sel];
else if (sel == ROLE_NONE) { /* Quit */
clearlocks();
curses_bail(0);
}
flags.initrace = sel;
free((genericptr_t) choices);
free((genericptr_t) pickmap);
}
if (flags.initrace == ROLE_RANDOM) { /* Random role */
sel = pick_race(flags.initrole, flags.initgend,
flags.initalign, PICK_RANDOM);
if (sel < 0)
sel = randrace(flags.initrole);
flags.initrace = sel;
}
}
/* 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)) {
if (flags.initgend == ROLE_RANDOM || flags.randomall) {
flags.initgend = pick_gend(flags.initrole, flags.initrace,
flags.initalign, PICK_RANDOM);
if (flags.initgend < 0)
flags.initgend = randgend(flags.initrole, flags.initrace);
} else {
/* Count the number of valid genders */
n = 0; /* number valid */
for (i = 0; i < ROLE_GENDERS; i++) {
if (ok_gend(flags.initrole, flags.initrace, i, flags.initalign))
n++;
}
if (n == 0) {
for (i = 0; i < ROLE_GENDERS; i++) {
if (validgend(flags.initrole, flags.initrace, i))
n++;
}
}
choices = (const char **) alloc(sizeof (char *) * (n + 1));
pickmap = (int *) alloc(sizeof (int) * (n + 1));
for (n = 0, i = 0; i < ROLE_GENDERS; i++) {
if (ok_gend(flags.initrole, flags.initrace, i, flags.initalign)) {
choices[n] = genders[i].adj;
pickmap[n++] = i;
}
}
choices[n] = (const char *) 0;
/* Permit the user to pick, if there is more than one */
if (n > 1)
sel =
curses_character_dialog(choices,
"Choose one of the following genders:");
else
sel = 0;
if (sel >= 0)
sel = pickmap[sel];
else if (sel == ROLE_NONE) { /* Quit */
clearlocks();
curses_bail(0);
}
flags.initgend = sel;
free((genericptr_t) choices);
free((genericptr_t) pickmap);
}
if (flags.initgend == ROLE_RANDOM) { /* Random gender */
sel = pick_gend(flags.initrole, flags.initrace,
flags.initalign, PICK_RANDOM);
if (sel < 0)
sel = randgend(flags.initrole, flags.initrace);
flags.initgend = sel;
}
}
/* Select an alignment, if necessary */
/* force compatibility with role/race/gender */
if (flags.initalign < 0 ||
!validalign(flags.initrole, flags.initrace, flags.initalign)) {
if (flags.initalign == ROLE_RANDOM || flags.randomall) {
flags.initalign = pick_align(flags.initrole, flags.initrace,
flags.initgend, PICK_RANDOM);
if (flags.initalign < 0)
flags.initalign = randalign(flags.initrole, flags.initrace);
} else {
/* Count the number of valid alignments */
n = 0; /* number valid */
for (i = 0; i < ROLE_ALIGNS; i++) {
if (ok_align(flags.initrole, flags.initrace, flags.initgend, i))
n++;
}
if (n == 0) {
for (i = 0; i < ROLE_ALIGNS; i++)
if (validalign(flags.initrole, flags.initrace, i))
n++;
}
choices = (const char **) alloc(sizeof (char *) * (n + 1));
pickmap = (int *) alloc(sizeof (int) * (n + 1));
for (n = 0, i = 0; i < ROLE_ALIGNS; i++) {
if (ok_align(flags.initrole, flags.initrace, flags.initgend, i)) {
choices[n] = aligns[i].adj;
pickmap[n++] = i;
}
}
choices[n] = (const char *) 0;
/* Permit the user to pick, if there is more than one */
if (n > 1)
sel =
curses_character_dialog(choices,
"Choose one of the following alignments:");
else
sel = 0;
if (sel >= 0)
sel = pickmap[sel];
else if (sel == ROLE_NONE) { /* Quit */
clearlocks();
curses_bail(0);
}
flags.initalign = sel;
free((genericptr_t) choices);
free((genericptr_t) pickmap);
}
if (flags.initalign == ROLE_RANDOM) {
sel = pick_align(flags.initrole, flags.initrace,
flags.initgend, PICK_RANDOM);
if (sel < 0)
sel = randalign(flags.initrole, flags.initrace);
flags.initalign = sel;
}
}
}
/* Prompt user for character race, role, alignment, or gender */
int
curses_character_dialog(const char **choices, const char *prompt)
{
int count, count2, ret, curletter;
char used_letters[52];
anything identifier;
menu_item *selected = NULL;
winid wid = curses_get_wid(NHW_MENU);
identifier.a_void = 0;
curses_start_menu(wid);
for (count = 0; choices[count]; count++) {
curletter = tolower(choices[count][0]);
for (count2 = 0; count2 < count; count2++) {
if (curletter == used_letters[count2]) {
curletter = toupper(curletter);
}
}
identifier.a_int = (count + 1); /* Must be non-zero */
curses_add_menu(wid, NO_GLYPH, &identifier, curletter, 0,
A_NORMAL, choices[count], FALSE);
used_letters[count] = curletter;
}
/* Random Selection */
identifier.a_int = ROLE_RANDOM;
curses_add_menu(wid, NO_GLYPH, &identifier, '*', 0, A_NORMAL, "Random",
FALSE);
/* Quit prompt */
identifier.a_int = ROLE_NONE;
curses_add_menu(wid, NO_GLYPH, &identifier, 'q', 0, A_NORMAL, "Quit",
FALSE);
curses_end_menu(wid, prompt);
ret = curses_select_menu(wid, PICK_ONE, &selected);
if (ret == 1) {
ret = (selected->item.a_int);
} else { /* Cancelled selection */
ret = ROLE_NONE;
}
if (ret > 0) {
ret--;
}
free((genericptr_t) selected);
return ret;
}
/* Initialize and display options appropriately */
void
curses_init_options()
{
set_wc_option_mod_status(WC_ALIGN_MESSAGE | WC_ALIGN_STATUS | WC_COLOR |
WC_HILITE_PET | WC_POPUP_DIALOG, SET_IN_GAME);
set_wc2_option_mod_status(WC2_GUICOLOR, SET_IN_GAME);
/* Remove a few options that are irrelevant to this windowport */
/*set_option_mod_status("DECgraphics", SET_IN_FILE); */
set_option_mod_status("eight_bit_tty", SET_IN_FILE);
/* Add those that are */
set_option_mod_status("statuslines", SET_IN_GAME);
/* Make sure that DECgraphics is not set to true via the config
file, as this will cause display issues. We can't disable it in
options.c in case the game is compiled with both tty and curses. */
if (!symset[PRIMARY].name || !strcmpi(symset[PRIMARY].name, "DECgraphics")) {
load_symset("curses",PRIMARY);
load_symset("default",ROGUESET);
}
#ifdef PDCURSES
/* PDCurses for SDL, win32 and OS/2 has the ability to set the
terminal size programatically. If the user does not specify a
size in the config file, we will set it to a nice big 110x32 to
take advantage of some of the nice features of this windowport. */
if (iflags.wc2_term_cols == 0) {
iflags.wc2_term_cols = 110;
}
if (iflags.wc2_term_rows == 0) {
iflags.wc2_term_rows = 32;
}
resize_term(iflags.wc2_term_rows, iflags.wc2_term_cols);
getmaxyx(base_term, term_rows, term_cols);
/* This is needed for an odd bug with PDCurses-SDL */
/* How to deal with this?
switch_graphics(ASCII_GRAPHICS);
if (iflags.IBMgraphics) {
switch_graphics(IBM_GRAPHICS);
} else if (iflags.cursesgraphics) {
switch_graphics(CURS_GRAPHICS);
} else {
switch_graphics(ASCII_GRAPHICS);
}
*/
#endif /* PDCURSES */
if (!iflags.wc2_windowborders) {
iflags.wc2_windowborders = 3; /* Set to auto if not specified */
}
if (!iflags.wc2_petattr) {
iflags.wc2_petattr = A_REVERSE;
} else { /* Pet attribute specified, so hilite_pet should be true */
iflags.hilite_pet = TRUE;
}
#ifdef NCURSES_MOUSE_VERSION
if (iflags.wc_mouse_support) {
mousemask(BUTTON1_CLICKED, NULL);
}
#endif
}
/* Display an ASCII splash screen if the splash_screen option is set */
void
curses_display_splash_window()
{
int x_start;
int y_start;
curses_get_window_xy(MAP_WIN, &x_start, &y_start);
if ((term_cols < 70) || (term_rows < 20)) {
iflags.wc_splash_screen = FALSE; /* No room for s.s. */
}
curses_toggle_color_attr(stdscr, CLR_WHITE, A_NORMAL, ON);
if (iflags.wc_splash_screen) {
mvaddstr(y_start, x_start, NETHACK_SPLASH_A);
mvaddstr(y_start + 1, x_start, NETHACK_SPLASH_B);
mvaddstr(y_start + 2, x_start, NETHACK_SPLASH_C);
mvaddstr(y_start + 3, x_start, NETHACK_SPLASH_D);
mvaddstr(y_start + 4, x_start, NETHACK_SPLASH_E);
mvaddstr(y_start + 5, x_start, NETHACK_SPLASH_F);
y_start += 7;
}
curses_toggle_color_attr(stdscr, CLR_WHITE, A_NORMAL, OFF);
#ifdef COPYRIGHT_BANNER_A
mvaddstr(y_start, x_start, COPYRIGHT_BANNER_A);
y_start++;
#endif
#ifdef COPYRIGHT_BANNER_B
mvaddstr(y_start, x_start, COPYRIGHT_BANNER_B);
y_start++;
#endif
#ifdef COPYRIGHT_BANNER_C
mvaddstr(y_start, x_start, COPYRIGHT_BANNER_C);
y_start++;
#endif
#ifdef COPYRIGHT_BANNER_D /* Just in case */
mvaddstr(y_start, x_start, COPYRIGHT_BANNER_D);
y_start++;
#endif
refresh();
}
/* Resore colors and cursor state before exiting */
void
curses_cleanup()
{
#ifdef TEXTCOLOR
if (has_colors() && can_change_color()) {
init_color(COLOR_YELLOW, orig_yellow.r, orig_yellow.g, orig_yellow.b);
init_color(COLOR_WHITE, orig_white.r, orig_white.g, orig_white.b);
if (COLORS >= 16) {
init_color(COLOR_RED + 8, orig_hired.r, orig_hired.g, orig_hired.b);
init_color(COLOR_GREEN + 8, orig_higreen.r, orig_higreen.g,
orig_higreen.b);
init_color(COLOR_YELLOW + 8, orig_hiyellow.r,
orig_hiyellow.g, orig_hiyellow.b);
init_color(COLOR_BLUE + 8, orig_hiblue.r, orig_hiblue.g,
orig_hiblue.b);
init_color(COLOR_MAGENTA + 8, orig_himagenta.r,
orig_himagenta.g, orig_himagenta.b);
init_color(COLOR_CYAN + 8, orig_hicyan.r, orig_hicyan.g,
orig_hicyan.b);
init_color(COLOR_WHITE + 8, orig_hiwhite.r, orig_hiwhite.g,
orig_hiwhite.b);
# ifdef USE_DARKGRAY
if (COLORS > 16) {
init_color(CURSES_DARK_GRAY, orig_darkgray.r,
orig_darkgray.g, orig_darkgray.b);
}
# endif
}
}
#endif
}

17
win/curses/cursinit.h Normal file
View File

@@ -0,0 +1,17 @@
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
#ifndef CURSINIT_H
# define CURSINIT_H
/* Global declarations */
void curses_create_main_windows(void);
void curses_init_nhcolors(void);
void curses_choose_character(void);
int curses_character_dialog(const char **choices, const char *prompt);
void curses_init_options(void);
void curses_display_splash_window(void);
void curses_cleanup(void);
#endif /* CURSINIT_H */

111
win/curses/cursinvt.c Normal file
View File

@@ -0,0 +1,111 @@
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
#include "curses.h"
#include "hack.h"
#include "wincurs.h"
#include "cursinvt.h"
/* Permanent inventory for curses interface */
/* Runs when the game indicates that the inventory has been updated */
void
curses_update_inv(void)
{
WINDOW *win = curses_get_nhwin(INV_WIN);
/* Check if the inventory window is enabled in first place */
if (!win) {
/* It's not. Re-initialize the main windows if the
option was enabled. */
if (iflags.perm_invent) {
curses_create_main_windows();
curses_last_messages();
doredraw();
}
return;
}
boolean border = curses_window_has_border(INV_WIN);
/* Figure out drawing area */
int x = 0;
int y = 0;
if (border) {
x++;
y++;
}
/* Clear the window as it is at the moment. */
werase(win);
wmove(win, y, x);
attr_t attr = A_UNDERLINE;
wattron(win, attr);
wprintw(win, "Inventory:");
wattroff(win, attr);
/* The actual inventory will override this if we do carry stuff */
wmove(win, y + 1, x);
wprintw(win, "Not carrying anything");
display_inventory(NULL, FALSE);
if (border)
box(win, 0, 0);
wnoutrefresh(win);
}
/* Adds an inventory item. */
void
curses_add_inv(int y, int glyph, CHAR_P accelerator, attr_t attr,
const char *str)
{
WINDOW *win = curses_get_nhwin(INV_WIN);
/* Figure out where to draw the line */
int x = 0;
if (curses_window_has_border(INV_WIN)) {
x++;
y++;
}
wmove(win, y, x);
if (accelerator) {
attr_t bold = A_BOLD;
wattron(win, bold);
waddch(win, accelerator);
wattroff(win, bold);
wprintw(win, ") ");
}
#if 0 // FIXME: MENU GLYPHS
if (accelerator && glyph != NO_GLYPH && iflags.use_menu_glyphs) {
unsigned dummy = 0; /* Not used */
int color = 0;
int symbol = 0;
mapglyph(glyph, &symbol, &color, &dummy,
u.ux, u.uy);
attr_t glyphclr = curses_color_attr(color, 0);
wattron(win, glyphclr);
wprintw(win, "%c ", symbol);
wattroff(win, glyphclr);
}
#endif
int color = NO_COLOR;
if (accelerator && /* Don't colorize categories */
iflags.use_menu_color) {
boolean menu_color = FALSE;
char str_mutable[BUFSZ];
Strcpy(str_mutable, str);
attr = 0;
get_menu_coloring(str_mutable, &color, &attr);
attr = curses_convert_attr(attr);
}
if (color == NO_COLOR) color = NONE;
curses_toggle_color_attr(win, color, attr, ON);
//wattron(win, attr);
wprintw(win, "%s", str);
//wattroff(win, attr);
curses_toggle_color_attr(win, color, attr, OFF);
wclrtoeol(win);
}

11
win/curses/cursinvt.h Normal file
View File

@@ -0,0 +1,11 @@
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
#ifndef CURSINVT_H
# define CURSINVT_H
/* Global declarations */
void curses_update_inv(void);
#endif /* CURSINVT_H */

820
win/curses/cursmain.c Normal file
View File

@@ -0,0 +1,820 @@
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
#include "curses.h"
#include "hack.h"
#include "patchlevel.h"
#include "color.h"
#include "wincurs.h"
/* Public functions for curses NetHack interface */
/* Interface definition, for windows.c */
struct window_procs curses_procs = {
"curses",
WC_ALIGN_MESSAGE | WC_ALIGN_STATUS | WC_COLOR | WC_HILITE_PET |
WC_PERM_INVENT | WC_POPUP_DIALOG | WC_SPLASH_SCREEN,
WC2_DARKGRAY | WC2_HITPOINTBAR |
#if defined(STATUS_HILITES)
WC2_HILITE_STATUS |
#endif
WC2_HITPOINTBAR | WC2_FLUSH_STATUS |
WC2_TERM_SIZE | WC2_WINDOWBORDERS | WC2_PETATTR | WC2_GUICOLOR,
curses_init_nhwindows,
curses_player_selection,
curses_askname,
curses_get_nh_event,
curses_exit_nhwindows,
curses_suspend_nhwindows,
curses_resume_nhwindows,
curses_create_nhwindow,
curses_clear_nhwindow,
curses_display_nhwindow,
curses_destroy_nhwindow,
curses_curs,
curses_putstr,
genl_putmixed,
curses_display_file,
curses_start_menu,
curses_add_menu,
curses_end_menu,
curses_select_menu,
genl_message_menu,
curses_update_inventory,
curses_mark_synch,
curses_wait_synch,
#ifdef CLIPPING
curses_cliparound,
#endif
#ifdef POSITIONBAR
donull,
#endif
curses_print_glyph,
curses_raw_print,
curses_raw_print_bold,
curses_nhgetch,
curses_nh_poskey,
curses_nhbell,
curses_doprev_message,
curses_yn_function,
curses_getlin,
curses_get_ext_cmd,
curses_number_pad,
curses_delay_output,
#ifdef CHANGE_COLOR /* only a Mac option currently */
donull,
donull,
#endif
curses_start_screen,
curses_end_screen,
genl_outrip,
curses_preference_update,
genl_getmsghistory,
genl_putmsghistory,
curses_status_init,
genl_status_finish,
genl_status_enablefield,
curses_status_update,
genl_can_suspend_yes,
};
/* Track if we're performing an update to the permanent window.
Needed since we aren't using the normal menu functions to handle
the inventory window. */
static int inv_update = 0;
/*
init_nhwindows(int* argcp, char** argv)
-- Initialize the windows used by NetHack. This can also
create the standard windows listed at the top, but does
not display them.
-- Any commandline arguments relevant to the windowport
should be interpreted, and *argcp and *argv should
be changed to remove those arguments.
-- When the message window is created, the variable
iflags.window_inited needs to be set to TRUE. Otherwise
all plines() will be done via raw_print().
** Why not have init_nhwindows() create all of the "standard"
** windows? Or at least all but WIN_INFO? -dean
*/
void
curses_init_nhwindows(int *argcp, char **argv)
{
#ifdef PDCURSES
char window_title[BUFSZ];
#endif
#ifdef XCURSES
base_term = Xinitscr(*argcp, argv);
#else
base_term = initscr();
#endif
#ifdef TEXTCOLOR
if (has_colors()) {
start_color();
curses_init_nhcolors();
} else {
iflags.use_color = FALSE;
set_option_mod_status("color", SET_IN_FILE);
iflags.wc2_guicolor = FALSE;
set_wc2_option_mod_status(WC2_GUICOLOR, SET_IN_FILE);
}
#else
iflags.use_color = FALSE;
set_option_mod_status("color", SET_IN_FILE);
iflags.wc2_guicolor = FALSE;
set_wc2_option_mod_status(WC2_GUICOLOR, SET_IN_FILE);
#endif
noecho();
raw();
meta(stdscr, TRUE);
orig_cursor = curs_set(0);
keypad(stdscr, TRUE);
#ifdef NCURSES_VERSION
# ifdef __APPLE__
ESCDELAY = 25;
# else
set_escdelay(25);
# endif/* __APPLE__ */
#endif /* NCURSES_VERSION */
#ifdef PDCURSES
# ifdef DEF_GAME_NAME
# ifdef VERSION_STRING
sprintf(window_title, "%s %s", DEF_GAME_NAME, VERSION_STRING);
# else
sprintf(window_title, "%s", DEF_GAME_NAME);
# endif
/* VERSION_STRING */
# else
# ifdef VERSION_STRING
sprintf(window_title, "%s %s", "NetHack", VERSION_STRING);
# else
sprintf(window_title, "%s", "NetHack");
# endif
/* VERSION_STRING */
# endif/* DEF_GAME_NAME */
PDC_set_title(window_title);
PDC_set_blink(TRUE); /* Only if the user asks for it! */
timeout(1);
(void) getch();
timeout(-1);
#endif /* PDCURSES */
getmaxyx(base_term, term_rows, term_cols);
counting = FALSE;
curses_init_options();
if ((term_rows < 15) || (term_cols < 40)) {
panic("Terminal too small. Must be minumum 40 width and 15 height");
}
curses_create_main_windows();
curses_init_mesg_history();
curses_display_splash_window();
}
/* Do a window-port specific player type selection. If player_selection()
offers a Quit option, it is its responsibility to clean up and terminate
the process. You need to fill in pl_character[0].
*/
void
curses_player_selection()
{
curses_choose_character();
}
/* Ask the user for a player name. */
void
curses_askname()
{
curses_line_input_dialog("Who are you?", plname, PL_NSIZ);
}
/* Does window event processing (e.g. exposure events).
A noop for the tty and X window-ports.
*/
void
curses_get_nh_event()
{
#ifdef PDCURSES
if (is_termresized()) {
resize_term(0, 0);
getmaxyx(base_term, term_rows, term_cols);
curses_create_main_windows();
curses_last_messages();
doredraw();
}
#endif
#ifdef NCURSES_VERSION /* Is there a better way to detect ncurses? */
if (is_term_resized(term_rows, term_cols)) {
if (!isendwin()) {
endwin();
}
refresh();
getmaxyx(base_term, term_rows, term_cols);
curses_create_main_windows();
curses_last_messages();
doredraw();
}
#endif
}
/* Exits the window system. This should dismiss all windows,
except the "window" used for raw_print(). str is printed if possible.
*/
void
curses_exit_nhwindows(const char *str)
{
curses_cleanup();
curs_set(orig_cursor);
endwin();
iflags.window_inited = 0;
if (str != NULL) {
raw_print(str);
}
}
/* Prepare the window to be suspended. */
void
curses_suspend_nhwindows(const char *str)
{
endwin();
}
/* Restore the windows after being suspended. */
void
curses_resume_nhwindows()
{
curses_refresh_nethack_windows();
}
/* Create a window of type "type" which can be
NHW_MESSAGE (top line)
NHW_STATUS (bottom lines)
NHW_MAP (main dungeon)
NHW_MENU (inventory or other "corner" windows)
NHW_TEXT (help/text, full screen paged window)
*/
winid
curses_create_nhwindow(int type)
{
winid wid = curses_get_wid(type);
if (curses_is_menu(wid) || curses_is_text(wid)) {
curses_start_menu(wid);
curses_add_wid(wid);
}
return wid;
}
/* Clear the given window, when asked to. */
void
curses_clear_nhwindow(winid wid)
{
if (wid != NHW_MESSAGE) {
curses_clear_nhwin(wid);
}
}
/* -- Display the window on the screen. If there is data
pending for output in that window, it should be sent.
If blocking is TRUE, display_nhwindow() will not
return until the data has been displayed on the screen,
and acknowledged by the user where appropriate.
-- All calls are blocking in the tty window-port.
-- Calling display_nhwindow(WIN_MESSAGE,???) will do a
--more--, if necessary, in the tty window-port.
*/
void
curses_display_nhwindow(winid wid, BOOLEAN_P block)
{
menu_item *selected = NULL;
if (curses_is_menu(wid) || curses_is_text(wid)) {
curses_end_menu(wid, "");
curses_select_menu(wid, PICK_NONE, &selected);
return;
}
/* don't overwrite the splash screen first time through */
if (!iflags.window_inited && wid == MAP_WIN)
iflags.window_inited = TRUE;
else {
/* actually display the window */
wnoutrefresh(curses_get_nhwin(wid));
/* flush pending writes from other windows too */
doupdate();
}
if ((wid == MAP_WIN) && block) {
(void) curses_more();
}
if ((wid == MESSAGE_WIN) && block) {
(void) curses_block(TRUE);
}
}
/* Destroy will dismiss the window if the window has not
* already been dismissed.
*/
void
curses_destroy_nhwindow(winid wid)
{
curses_del_nhwin(wid);
}
/* Next output to window will start at (x,y), also moves
displayable cursor to (x,y). For backward compatibility,
1 <= x < cols, 0 <= y < rows, where cols and rows are
the size of window.
*/
void
curses_curs(winid wid, int x, int y)
{
curses_move_cursor(wid, x, y);
}
/*
putstr(window, attr, str)
-- Print str on the window with the given attribute. Only
printable ASCII characters (040-0126) must be supported.
Multiple putstr()s are output on separate lines.
Attributes
can be one of
ATR_NONE (or 0)
ATR_ULINE
ATR_BOLD
ATR_BLINK
ATR_INVERSE
If a window-port does not support all of these, it may map
unsupported attributes to a supported one (e.g. map them
all to ATR_INVERSE). putstr() may compress spaces out of
str, break str, or truncate str, if necessary for the
display. Where putstr() breaks a line, it has to clear
to end-of-line.
-- putstr should be implemented such that if two putstr()s
are done consecutively the user will see the first and
then the second. In the tty port, pline() achieves this
by calling more() or displaying both on the same line.
*/
void
curses_putstr(winid wid, int attr, const char *text)
{
int curses_attr = curses_convert_attr(attr);
/* We need to convert NetHack attributes to curses attributes */
curses_puts(wid, curses_attr, text);
}
/* Display the file named str. Complain about missing files
iff complain is TRUE.
*/
void
curses_display_file(const char *filename, BOOLEAN_P must_exist)
{
curses_view_file(filename, must_exist);
}
/* Start using window as a menu. You must call start_menu()
before add_menu(). After calling start_menu() you may not
putstr() to the window. Only windows of type NHW_MENU may
be used for menus.
*/
void
curses_start_menu(winid wid)
{
if (inv_update)
return;
curses_create_nhmenu(wid);
}
/*
add_menu(winid wid, int glyph, const anything identifier,
char accelerator, char groupacc,
int attr, char *str, boolean preselected)
-- Add a text line str to the given menu window. If identifier
is 0, then the line cannot be selected (e.g. a title).
Otherwise, identifier is the value returned if the line is
selected. Accelerator is a keyboard key that can be used
to select the line. If the accelerator of a selectable
item is 0, the window system is free to select its own
accelerator. It is up to the window-port to make the
accelerator visible to the user (e.g. put "a - " in front
of str). The value attr is the same as in putstr().
Glyph is an optional glyph to accompany the line. If
window port cannot or does not want to display it, this
is OK. If there is no glyph applicable, then this
value will be NO_GLYPH.
-- All accelerators should be in the range [A-Za-z].
-- It is expected that callers do not mix accelerator
choices. Either all selectable items have an accelerator
or let the window system pick them. Don't do both.
-- Groupacc is a group accelerator. It may be any character
outside of the standard accelerator (see above) or a
number. If 0, the item is unaffected by any group
accelerator. If this accelerator conflicts with
the menu command (or their user defined alises), it loses.
The menu commands and aliases take care not to interfere
with the default object class symbols.
-- If you want this choice to be preselected when the
menu is displayed, set preselected to TRUE.
*/
void
curses_add_menu(winid wid, int glyph, const ANY_P * identifier,
CHAR_P accelerator, CHAR_P group_accel, int attr,
const char *str, BOOLEAN_P presel)
{
int curses_attr = curses_convert_attr(attr);
if (inv_update) {
curses_add_inv(inv_update, glyph, accelerator, curses_attr, str);
inv_update++;
return;
}
curses_add_nhmenu_item(wid, glyph, identifier, accelerator, group_accel,
curses_attr, str, presel);
}
/*
end_menu(window, prompt)
-- Stop adding entries to the menu and flushes the window
to the screen (brings to front?). Prompt is a prompt
to give the user. If prompt is NULL, no prompt will
be printed.
** This probably shouldn't flush the window any more (if
** it ever did). That should be select_menu's job. -dean
*/
void
curses_end_menu(winid wid, const char *prompt)
{
if (inv_update)
return;
curses_finalize_nhmenu(wid, prompt);
}
/*
int select_menu(winid window, int how, menu_item **selected)
-- Return the number of items selected; 0 if none were chosen,
-1 when explicitly cancelled. If items were selected, then
selected is filled in with an allocated array of menu_item
structures, one for each selected line. The caller must
free this array when done with it. The "count" field
of selected is a user supplied count. If the user did
not supply a count, then the count field is filled with
-1 (meaning all). A count of zero is equivalent to not
being selected and should not be in the list. If no items
were selected, then selected is NULL'ed out. How is the
mode of the menu. Three valid values are PICK_NONE,
PICK_ONE, and PICK_N, meaning: nothing is selectable,
only one thing is selectable, and any number valid items
may selected. If how is PICK_NONE, this function should
never return anything but 0 or -1.
-- You may call select_menu() on a window multiple times --
the menu is saved until start_menu() or destroy_nhwindow()
is called on the window.
-- Note that NHW_MENU windows need not have select_menu()
called for them. There is no way of knowing whether
select_menu() will be called for the window at
create_nhwindow() time.
*/
int
curses_select_menu(winid wid, int how, MENU_ITEM_P ** selected)
{
if (inv_update)
return 0;
return curses_display_nhmenu(wid, how, selected);
}
void
curses_update_inventory(void)
{
/* Don't do anything if perm_invent is off unless we
changed the option. */
if (!iflags.perm_invent) {
if (curses_get_nhwin(INV_WIN)) {
curses_create_main_windows();
curses_last_messages();
doredraw();
}
return;
}
/* Update inventory sidebar. NetHack uses normal menu functions
when drawing the inventory, and we don't want to change the
underlying code. So instead, track if an inventory update is
being performed with a static variable. */
inv_update = 1;
curses_update_inv();
inv_update = 0;
}
/*
mark_synch() -- Don't go beyond this point in I/O on any channel until
all channels are caught up to here. Can be an empty call
for the moment
*/
void
curses_mark_synch()
{
}
/*
wait_synch() -- Wait until all pending output is complete (*flush*() for
streams goes here).
-- May also deal with exposure events etc. so that the
display is OK when return from wait_synch().
*/
void
curses_wait_synch()
{
}
/*
cliparound(x, y)-- Make sure that the user is more-or-less centered on the
screen if the playing area is larger than the screen.
-- This function is only defined if CLIPPING is defined.
*/
void
curses_cliparound(int x, int y)
{
int sx, sy, ex, ey;
boolean redraw = curses_map_borders(&sx, &sy, &ex, &ey, x, y);
if (redraw) {
curses_draw_map(sx, sy, ex, ey);
}
}
/*
print_glyph(window, x, y, glyph, bkglyph)
-- Print the glyph at (x,y) on the given window. Glyphs are
integers at the interface, mapped to whatever the window-
port wants (symbol, font, color, attributes, ...there's
a 1-1 map between glyphs and distinct things on the map).
bkglyph is to render the background behind the glyph.
It's not used here.
*/
void
curses_print_glyph(winid wid, XCHAR_P x, XCHAR_P y, int glyph, int bkglyph)
{
int ch;
int color;
unsigned int special;
int attr = -1;
/* map glyph to character and color */
mapglyph(glyph, &ch, &color, &special, x, y);
if ((special & MG_PET) && iflags.hilite_pet) {
attr = iflags.wc2_petattr;
}
if ((special & MG_DETECT) && iflags.use_inverse) {
attr = A_REVERSE;
}
if (!symset[PRIMARY].name || !strcmpi(symset[PRIMARY].name, "curses")) {
ch = curses_convert_glyph(ch, glyph);
}
if (wid == NHW_MAP) {
/* hilite stairs not in 3.6, yet
if ((special & MG_STAIRS) && iflags.hilite_hidden_stairs) {
color = 16 + (color * 2);
} else
*/
if ((special & MG_OBJPILE) && iflags.hilite_pile) {
color = 16 + (color * 2) + 1;
}
}
curses_putch(wid, x, y, ch, color, attr);
}
/*
raw_print(str) -- Print directly to a screen, or otherwise guarantee that
the user sees str. raw_print() appends a newline to str.
It need not recognize ASCII control characters. This is
used during startup (before windowing system initialization
-- maybe this means only error startup messages are raw),
for error messages, and maybe other "msg" uses. E.g.
updating status for micros (i.e, "saving").
*/
void
curses_raw_print(const char *str)
{
puts(str);
}
/*
raw_print_bold(str)
-- Like raw_print(), but prints in bold/standout (if possible).
*/
void
curses_raw_print_bold(const char *str)
{
curses_raw_print(str);
}
/*
int nhgetch() -- Returns a single character input from the user.
-- In the tty window-port, nhgetch() assumes that tgetch()
will be the routine the OS provides to read a character.
Returned character _must_ be non-zero.
*/
int
curses_nhgetch()
{
int ch;
curses_prehousekeeping();
ch = curses_read_char();
curses_posthousekeeping();
return ch;
}
/*
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
return value is non-zero, a character was typed, else,
a position in the MAP window is returned in x, y and mod.
mod may be one of
CLICK_1 -- mouse click type 1
CLICK_2 -- mouse click type 2
The different click types can map to whatever the
hardware supports. If no mouse is supported, this
routine always returns a non-zero character.
*/
int
curses_nh_poskey(int *x, int *y, int *mod)
{
int key = curses_nhgetch();
#ifdef NCURSES_MOUSE_VERSION
/* Mouse event if mouse_support is true */
if (key == KEY_MOUSE) {
key = curses_get_mouse(x, y, mod);
}
#endif
return key;
}
/*
nhbell() -- Beep at user. [This will exist at least until sounds are
redone, since sounds aren't attributable to windows anyway.]
*/
void
curses_nhbell()
{
beep();
}
/*
doprev_message()
-- Display previous messages. Used by the ^P command.
-- On the tty-port this scrolls WIN_MESSAGE back one line.
*/
int
curses_doprev_message()
{
curses_prev_mesg();
return 0;
}
/*
char yn_function(const char *ques, const char *choices, char default)
-- Print a prompt made up of ques, choices and default.
Read a single character response that is contained in
choices or default. If choices is NULL, all possible
inputs are accepted and returned. This overrides
everything else. The choices are expected to be in
lower case. Entering ESC always maps to 'q', or 'n',
in that order, if present in choices, otherwise it maps
to default. Entering any other quit character (SPACE,
RETURN, NEWLINE) maps to default.
-- If the choices string contains ESC, then anything after
it is an acceptable response, but the ESC and whatever
follows is not included in the prompt.
-- If the choices string contains a '#' then accept a count.
Place this value in the global "yn_number" and return '#'.
-- This uses the top line in the tty window-port, other
ports might use a popup.
*/
char
curses_yn_function(const char *question, const char *choices, CHAR_P def)
{
return (char) curses_character_input_dialog(question, choices, def);
}
/*
getlin(const char *ques, char *input)
-- Prints ques as a prompt and reads a single line of text,
up to a newline. The string entered is returned without the
newline. ESC is used to cancel, in which case the string
"\033\000" is returned.
-- getlin() must call flush_screen(1) before doing anything.
-- This uses the top line in the tty window-port, other
ports might use a popup.
*/
void
curses_getlin(const char *question, char *input)
{
curses_line_input_dialog(question, input, BUFSZ);
}
/*
int get_ext_cmd(void)
-- Get an extended command in a window-port specific way.
An index into extcmdlist[] is returned on a successful
selection, -1 otherwise.
*/
int
curses_get_ext_cmd()
{
return curses_ext_cmd();
}
/*
number_pad(state)
-- Initialize the number pad to the given state.
*/
void
curses_number_pad(int state)
{
}
/*
delay_output() -- Causes a visible delay of 50ms in the output.
Conceptually, this is similar to wait_synch() followed
by a nap(50ms), but allows asynchronous operation.
*/
void
curses_delay_output()
{
/* refreshing the whole display is a waste of time,
* but that's why we're here */
refresh();
napms(50);
}
/*
start_screen() -- Only used on Unix tty ports, but must be declared for
completeness. Sets up the tty to work in full-screen
graphics mode. Look at win/tty/termcap.c for an
example. If your window-port does not need this function
just declare an empty function.
*/
void
curses_start_screen()
{
}
/*
end_screen() -- Only used on Unix tty ports, but must be declared for
completeness. The complement of start_screen().
*/
void
curses_end_screen()
{
}
/*
outrip(winid, int)
-- The tombstone code. If you want the traditional code use
genl_outrip for the value and check the #if in rip.c.
*/
void
curses_outrip(winid wid, int how)
{
}
/*
preference_update(preference)
-- The player has just changed one of the wincap preference
settings, and the NetHack core is notifying your window
port of that change. If your window-port is capable of
dynamically adjusting to the change then it should do so.
Your window-port will only be notified of a particular
change if it indicated that it wants to be by setting the
corresponding bit in the wincap mask.
*/
void
curses_preference_update(const char *pref)
{
if ((strcmp(pref, "align_status") == 0) ||
(strcmp(pref, "align_message") == 0)) {
curses_create_main_windows();
curses_last_messages();
doredraw();
}
}

630
win/curses/cursmesg.c Normal file
View File

@@ -0,0 +1,630 @@
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
#include "curses.h"
#include "hack.h"
#include "wincurs.h"
#include "cursmesg.h"
#include <ctype.h>
/* Message window routines for curses interface */
/* Private declatations */
typedef struct nhpm {
char *str; /* Message text */
long turn; /* Turn number for message */
struct nhpm *prev_mesg; /* Pointer to previous message */
struct nhpm *next_mesg; /* Pointer to next message */
} nhprev_mesg;
static void scroll_window(winid wid);
static void unscroll_window(winid wid);
static void directional_scroll(winid wid, int nlines);
static void mesg_add_line(char *mline);
static nhprev_mesg *get_msg_line(boolean reverse, int mindex);
static int turn_lines = 1;
static int mx = 0;
static int my = 0; /* message window text location */
static nhprev_mesg *first_mesg = NULL;
static nhprev_mesg *last_mesg = NULL;
static int max_messages;
static int num_messages = 0;
/* Write a string to the message window. Attributes set by calling function. */
void
curses_message_win_puts(const char *message, boolean recursed)
{
int height, width, linespace;
char *tmpstr;
WINDOW *win = curses_get_nhwin(MESSAGE_WIN);
boolean border = curses_window_has_border(MESSAGE_WIN);
int message_length = strlen(message);
int border_space = 0;
static long suppress_turn = -1;
if (strncmp("Count:", message, 6) == 0) {
curses_count_window(message);
return;
}
if (suppress_turn == moves) {
return;
}
curses_get_window_size(MESSAGE_WIN, &height, &width);
if (border) {
border_space = 1;
if (mx < 1) {
mx = 1;
}
if (my < 1) {
my = 1;
}
}
linespace = ((width + border_space) - 3) - mx;
if (strcmp(message, "#") == 0) { /* Extended command or Count: */
if ((strcmp(toplines, "#") != 0) && (my >= (height - 1 + border_space)) && (height != 1)) { /* Bottom of message window */
scroll_window(MESSAGE_WIN);
mx = width;
my--;
strcpy(toplines, message);
}
return;
}
if (!recursed) {
strcpy(toplines, message);
mesg_add_line((char *) message);
}
if (linespace < message_length) {
if (my >= (height - 1 + border_space)) { /* bottom of message win */
if ((turn_lines > height) || (height == 1)) {
/* Pause until key is hit - Esc suppresses any further
messages that turn */
if (curses_more() == '\033') {
suppress_turn = moves;
return;
}
} else {
scroll_window(MESSAGE_WIN);
turn_lines++;
}
} else {
if (mx != border_space) {
my++;
mx = border_space;
}
}
}
if (height > 1) {
curses_toggle_color_attr(win, NONE, A_BOLD, ON);
}
if ((mx == border_space) && ((message_length + 2) > width)) {
tmpstr = curses_break_str(message, (width - 2), 1);
mvwprintw(win, my, mx, "%s", tmpstr);
mx += strlen(tmpstr);
if (strlen(tmpstr) < (size_t) (width - 2)) {
mx++;
}
free(tmpstr);
if (height > 1) {
curses_toggle_color_attr(win, NONE, A_BOLD, OFF);
}
curses_message_win_puts(tmpstr = curses_str_remainder(message, (width - 2), 1),
TRUE);
free(tmpstr);
} else {
mvwprintw(win, my, mx, "%s", message);
curses_toggle_color_attr(win, NONE, A_BOLD, OFF);
mx += message_length + 1;
}
wrefresh(win);
}
int
curses_block(boolean noscroll)
/* noscroll - blocking because of msgtype = stop/alert */
/* else blocking because window is full, so need to scroll after */
{
int height, width, ret;
WINDOW *win = curses_get_nhwin(MESSAGE_WIN);
char *resp = " \n\033"; /* space, enter, esc */
curses_get_window_size(MESSAGE_WIN, &height, &width);
curses_toggle_color_attr(win, MORECOLOR, NONE, ON);
mvwprintw(win, my, mx, iflags.msg_is_alert ? "<TAB!>" : ">>");
curses_toggle_color_attr(win, MORECOLOR, NONE, OFF);
if (iflags.msg_is_alert)
curses_alert_main_borders(TRUE);
wrefresh(win);
while (iflags.msg_is_alert && (ret = wgetch(win) != '\t'));
/* msgtype=stop should require space/enter rather than
* just any key, as we want to prevent YASD from
* riding direction keys. */
while (!iflags.msg_is_alert && (ret = wgetch(win)) && !index(resp,(char)ret));
if (iflags.msg_is_alert)
curses_alert_main_borders(FALSE);
if (height == 1) {
curses_clear_unhighlight_message_window();
} else {
mvwprintw(win, my, mx, " ");
if (!noscroll) {
scroll_window(MESSAGE_WIN);
turn_lines = 1;
}
wrefresh(win);
}
return ret;
}
int
curses_more()
{
return curses_block(FALSE);
}
/* Clear the message window if one line; otherwise unhighlight old messages */
void
curses_clear_unhighlight_message_window()
{
int mh, mw, count;
boolean border = curses_window_has_border(MESSAGE_WIN);
WINDOW *win = curses_get_nhwin(MESSAGE_WIN);
turn_lines = 1;
curses_get_window_size(MESSAGE_WIN, &mh, &mw);
mx = 0;
if (border) {
mx++;
}
if (mh == 1) {
curses_clear_nhwin(MESSAGE_WIN);
} else {
mx += mw; /* Force new line on new turn */
if (border) {
for (count = 0; count < mh; count++) {
mvwchgat(win, count + 1, 1, mw, COLOR_PAIR(8), A_NORMAL, NULL);
}
} else {
for (count = 0; count < mh; count++) {
mvwchgat(win, count, 0, mw, COLOR_PAIR(8), A_NORMAL, NULL);
}
}
wnoutrefresh(win);
}
}
/* Reset message window cursor to starting position, and display most
recent messages. */
void
curses_last_messages()
{
boolean border = curses_window_has_border(MESSAGE_WIN);
if (border) {
mx = 1;
my = 1;
} else {
mx = 0;
my = 0;
}
nhprev_mesg *mesg;
int i;
for (i = (num_messages - 1); i > 0; i--) {
mesg = get_msg_line(TRUE, i);
if (mesg && mesg->str && strcmp(mesg->str, ""))
curses_message_win_puts(mesg->str, TRUE);
}
curses_message_win_puts(toplines, TRUE);
}
/* Initialize list for message history */
void
curses_init_mesg_history()
{
max_messages = iflags.msg_history;
if (max_messages < 1) {
max_messages = 1;
}
if (max_messages > MESG_HISTORY_MAX) {
max_messages = MESG_HISTORY_MAX;
}
}
/* Display previous message window messages in reverse chron order */
void
curses_prev_mesg()
{
int count;
winid wid;
long turn = 0;
anything *identifier;
nhprev_mesg *mesg;
menu_item *selected = NULL;
wid = curses_get_wid(NHW_MENU);
curses_create_nhmenu(wid);
identifier = malloc(sizeof (anything));
identifier->a_void = NULL;
for (count = 0; count < num_messages; count++) {
mesg = get_msg_line(TRUE, count);
if ((turn != mesg->turn) && (count != 0)) {
curses_add_menu(wid, NO_GLYPH, identifier, 0, 0, A_NORMAL,
"---", FALSE);
}
curses_add_menu(wid, NO_GLYPH, identifier, 0, 0, A_NORMAL,
mesg->str, FALSE);
turn = mesg->turn;
}
curses_end_menu(wid, "");
curses_select_menu(wid, PICK_NONE, &selected);
}
/* Shows Count: in a separate window, or at the bottom of the message
window, depending on the user's settings */
void
curses_count_window(const char *count_text)
{
int startx, starty, winx, winy;
int messageh, messagew;
static WINDOW *countwin = NULL;
if ((count_text == NULL) && (countwin != NULL)) {
delwin(countwin);
countwin = NULL;
counting = FALSE;
return;
}
counting = TRUE;
if (iflags.wc_popup_dialog) { /* Display count in popup window */
startx = 1;
starty = 1;
if (countwin == NULL) {
countwin = curses_create_window(25, 1, UP);
}
} else { /* Display count at bottom of message window */
curses_get_window_xy(MESSAGE_WIN, &winx, &winy);
curses_get_window_size(MESSAGE_WIN, &messageh, &messagew);
if (curses_window_has_border(MESSAGE_WIN)) {
winx++;
winy++;
}
winy += messageh - 1;
if (countwin == NULL) {
pline("#");
#ifndef PDCURSES
countwin = newwin(1, 25, winy, winx);
#endif /* !PDCURSES */
}
#ifdef PDCURSES
else {
curses_destroy_win(countwin);
}
countwin = newwin(1, 25, winy, winx);
#endif /* PDCURSES */
startx = 0;
starty = 0;
}
mvwprintw(countwin, starty, startx, "%s", count_text);
wrefresh(countwin);
}
/* Gets a "line" (buffer) of input. */
void
curses_message_win_getline(const char *prompt, char *answer, int buffer)
{
int height, width; /* of window */
char *tmpbuf, *p_answer; /* combined prompt + answer */
int nlines, maxlines, i; /* prompt + answer */
int promptline;
int promptx;
char **linestarts; /* pointers to start of each line */
char *tmpstr; /* for free() */
int maxy, maxx; /* linewrap / scroll */
int ch;
WINDOW *win = curses_get_nhwin(MESSAGE_WIN);
int border_space = 0;
int len = 0; /* of answer string */
boolean border = curses_window_has_border(MESSAGE_WIN);
int orig_cursor = curs_set(0);
curses_get_window_size(MESSAGE_WIN, &height, &width);
if (border) {
border_space = 1;
if (mx < 1) mx = 1;
if (my < 1) my = 1;
}
maxy = height - 1 + border_space;
maxx = width - 1 + border_space;
tmpbuf = (char *)malloc(strlen(prompt) + buffer + 2);
maxlines = buffer / width * 2;
strcpy(tmpbuf, prompt);
strcat(tmpbuf, " ");
nlines = curses_num_lines(tmpbuf,width);
maxlines += nlines * 2;
linestarts = (char **)malloc(sizeof(char*) * maxlines);
p_answer = tmpbuf + strlen(tmpbuf);
linestarts[0] = tmpbuf;
if (mx > border_space) { /* newline */
if (my >= maxy) scroll_window(MESSAGE_WIN);
else my++;
mx = border_space;
}
curses_toggle_color_attr(win, NONE, A_BOLD, ON);
for (i = 0; i < nlines-1; i++) {
tmpstr = curses_break_str(linestarts[i],width-1,1);
linestarts[i+1] = linestarts[i] + strlen(tmpstr);
if (*linestarts[i+1] == ' ') linestarts[i+1]++;
mvwaddstr(win,my,mx,tmpstr);
free(tmpstr);
if (++my >= maxy) {
scroll_window(MESSAGE_WIN);
my--;
}
}
mvwaddstr(win,my,mx,linestarts[nlines-1]);
mx = promptx = strlen(linestarts[nlines-1]) + border_space;
promptline = nlines - 1;
while(1) {
mx = strlen(linestarts[nlines - 1]) + border_space;
if (mx > maxx) {
if (nlines < maxlines) {
tmpstr = curses_break_str(linestarts[nlines - 1], width - 1, 1);
mx = strlen(tmpstr) + border_space;
mvwprintw(win, my, mx, "%*c", maxx - mx + 1, ' ');
if (++my > maxy) {
scroll_window(MESSAGE_WIN);
my--;
}
mx = border_space;
linestarts[nlines] = linestarts[nlines - 1] + strlen(tmpstr);
if (*linestarts[nlines] == ' ') linestarts[nlines]++;
mvwaddstr(win, my, mx, linestarts[nlines]);
mx = strlen(linestarts[nlines]) + border_space;
nlines++;
free(tmpstr);
} else {
p_answer[--len] = '\0';
mvwaddch(win, my, --mx, ' ');
}
}
wmove(win, my, mx);
curs_set(1);
wrefresh(win);
ch = getch();
curs_set(0);
switch(ch) {
case '\033': /* DOESCAPE */
/* blank the input but don't exit */
while(nlines - 1 > promptline) {
if (nlines-- > height) {
unscroll_window(MESSAGE_WIN);
tmpstr = curses_break_str(linestarts[nlines - height], width - 1, 1);
mvwaddstr(win, border_space, border_space, tmpstr);
free(tmpstr);
} else {
mx = border_space;
mvwprintw(win, my, mx, "%*c", maxx - mx, ' ');
my--;
}
}
mx = promptx;
mvwprintw(win, my, mx, "%*c", maxx - mx, ' ');
*p_answer = '\0';
len = 0;
break;
case ERR: /* should not happen */
*answer = '\0';
free(tmpbuf);
free(linestarts);
curs_set(orig_cursor);
curses_toggle_color_attr(win, NONE, A_BOLD, OFF);
return;
case '\r':
case '\n':
free(linestarts);
strncpy(answer, p_answer, buffer);
strcpy(toplines, tmpbuf);
mesg_add_line((char *) tmpbuf);
free(tmpbuf);
curs_set(orig_cursor);
curses_toggle_color_attr(win, NONE, A_BOLD, OFF);
if (++my > maxy) {
scroll_window(MESSAGE_WIN);
my--;
}
mx = border_space;
return;
case '\b':
case KEY_BACKSPACE:
if (len < 1) {
len = 1;
mx = promptx;
}
p_answer[--len] = '\0';
mvwaddch(win, my, --mx, ' ');
/* try to unwrap back to the previous line if there is one */
if (nlines > 1 && strlen(linestarts[nlines - 2]) < (size_t) width) {
mvwaddstr(win, my - 1, border_space, linestarts[nlines - 2]);
if (nlines-- > height) {
unscroll_window(MESSAGE_WIN);
tmpstr = curses_break_str(linestarts[nlines - height], width - 1, 1);
mvwaddstr(win, border_space, border_space, tmpstr);
free(tmpstr);
} else {
/* clean up the leftovers on the next line, if we didn't scroll it away */
mvwprintw(win, my--, border_space, "%*c", strlen(linestarts[nlines]), ' ');
}
}
break;
default:
p_answer[len++] = ch;
if (len >= buffer) len = buffer-1;
else mvwaddch(win, my, mx, ch);
p_answer[len] = '\0';
}
}
}
/* Scroll lines upward in given window, or clear window if only one line. */
static void
scroll_window(winid wid)
{
directional_scroll(wid,1);
}
static void
unscroll_window(winid wid)
{
directional_scroll(wid,-1);
}
static void
directional_scroll(winid wid, int nlines)
{
int wh, ww, s_top, s_bottom;
boolean border = curses_window_has_border(wid);
WINDOW *win = curses_get_nhwin(wid);
curses_get_window_size(wid, &wh, &ww);
if (wh == 1) {
curses_clear_nhwin(wid);
return;
}
if (border) {
s_top = 1;
s_bottom = wh;
} else {
s_top = 0;
s_bottom = wh - 1;
}
scrollok(win, TRUE);
wsetscrreg(win, s_top, s_bottom);
wscrl(win, nlines);
scrollok(win, FALSE);
if (wid == MESSAGE_WIN) {
if (border)
mx = 1;
else
mx = 0;
}
if (border) {
box(win, 0, 0);
}
wrefresh(win);
}
/* Add given line to message history */
static void
mesg_add_line(char *mline)
{
nhprev_mesg *tmp_mesg = NULL;
nhprev_mesg *current_mesg = malloc(sizeof (nhprev_mesg));
current_mesg->str = curses_copy_of(mline);
current_mesg->turn = moves;
current_mesg->next_mesg = NULL;
if (num_messages == 0) {
first_mesg = current_mesg;
}
if (last_mesg != NULL) {
last_mesg->next_mesg = current_mesg;
}
current_mesg->prev_mesg = last_mesg;
last_mesg = current_mesg;
if (num_messages < max_messages) {
num_messages++;
} else {
tmp_mesg = first_mesg->next_mesg;
free(first_mesg);
first_mesg = tmp_mesg;
}
}
/* Returns specified line from message history, or NULL if out of bounds */
static nhprev_mesg *
get_msg_line(boolean reverse, int mindex)
{
int count;
nhprev_mesg *current_mesg;
if (reverse) {
current_mesg = last_mesg;
for (count = 0; count < mindex; count++) {
if (current_mesg == NULL) {
return NULL;
}
current_mesg = current_mesg->prev_mesg;
}
return current_mesg;
} else {
current_mesg = first_mesg;
for (count = 0; count < mindex; count++) {
if (current_mesg == NULL) {
return NULL;
}
current_mesg = current_mesg->next_mesg;
}
return current_mesg;
}
}

19
win/curses/cursmesg.h Normal file
View File

@@ -0,0 +1,19 @@
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
#ifndef CURSMESG_H
# define CURSMESG_H
/* Global declarations */
void curses_message_win_puts(const char *message, boolean recursed);
int curses_block(boolean require_tab);
int curses_more(void);
void curses_clear_unhighlight_message_window(void);
void curses_message_win_getline(const char *prompt, char *answer, int buffer);
void curses_last_messages(void);
void curses_init_mesg_history(void);
void curses_prev_mesg(void);
void curses_count_window(const char *count_text);
#endif /* CURSMESG_H */

880
win/curses/cursmisc.c Normal file
View File

@@ -0,0 +1,880 @@
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
#include "curses.h"
#include "hack.h"
#include "wincurs.h"
#include "cursmisc.h"
#include "func_tab.h"
#include "dlb.h"
#include <ctype.h>
/* Misc. curses interface functions */
/* Private declarations */
static int curs_x = -1;
static int curs_y = -1;
static int parse_escape_sequence(void);
/* Macros for Control and Alt keys */
#ifndef M
# ifndef NHSTDC
# define M(c) (0x80 | (c))
# else
# define M(c) ((c) - 128)
# endif/* NHSTDC */
#endif
#ifndef C
# define C(c) (0x1f & (c))
#endif
/* Read a character of input from the user */
int
curses_read_char()
{
int ch, tmpch;
ch = getch();
tmpch = ch;
ch = curses_convert_keys(ch);
if (ch == 0) {
ch = '\033'; /* map NUL to ESC since nethack doesn't expect NUL */
}
#if defined(ALT_0) && defined(ALT_9) /* PDCurses, maybe others */
if ((ch >= ALT_0) && (ch <= ALT_9)) {
tmpch = (ch - ALT_0) + '0';
ch = M(tmpch);
}
#endif
#if defined(ALT_A) && defined(ALT_Z) /* PDCurses, maybe others */
if ((ch >= ALT_A) && (ch <= ALT_Z)) {
tmpch = (ch - ALT_A) + 'a';
ch = M(tmpch);
}
#endif
#ifdef KEY_RESIZE
/* Handle resize events via get_nh_event, not this code */
if (ch == KEY_RESIZE) {
ch = '\033'; /* NetHack doesn't know what to do with KEY_RESIZE */
}
#endif
if (counting && !isdigit(ch)) { /* Dismiss count window if necissary */
curses_count_window(NULL);
curses_refresh_nethack_windows();
}
return ch;
}
/* Turn on or off the specified color and / or attribute */
void
curses_toggle_color_attr(WINDOW * win, int color, int attr, int onoff)
{
#ifdef TEXTCOLOR
int curses_color;
/* Map color disabled */
if ((!iflags.wc_color) && (win == mapwin)) {
return;
}
/* GUI color disabled */
// if ((!iflags.wc2_guicolor) && (win != mapwin)) {
// return;
// }
if (color == 0) { /* make black fg visible */
# ifdef USE_DARKGRAY
if (iflags.wc2_darkgray) {
if (can_change_color() && (COLORS > 16)) {
/* colorpair for black is already darkgray */
} else { /* Use bold for a bright black */
wattron(win, A_BOLD);
}
} else
# endif/* USE_DARKGRAY */
color = CLR_BLUE;
}
curses_color = color + 1;
if (COLORS < 16) {
if (curses_color > 8 && curses_color < 17)
curses_color -= 8;
else if (curses_color > (17 + 16))
curses_color -= 16;
}
if (onoff == ON) { /* Turn on color/attributes */
if (color != NONE) {
if ((((color > 7) && (color < 17)) ||
(color > 17 + 17)) && (COLORS < 16)) {
wattron(win, A_BOLD);
}
wattron(win, COLOR_PAIR(curses_color));
}
if (attr != NONE) {
wattron(win, attr);
}
} else { /* Turn off color/attributes */
if (color != NONE) {
if ((color > 7) && (COLORS < 16)) {
wattroff(win, A_BOLD);
}
# ifdef USE_DARKGRAY
if ((color == 0) && (!can_change_color() || (COLORS <= 16))) {
wattroff(win, A_BOLD);
}
# else
if (iflags.use_inverse) {
wattroff(win, A_REVERSE);
}
# endif/* DARKGRAY */
wattroff(win, COLOR_PAIR(curses_color));
}
if (attr != NONE) {
wattroff(win, attr);
}
}
#endif /* TEXTCOLOR */
}
/* clean up and quit - taken from tty port */
void
curses_bail(const char *mesg)
{
clearlocks();
curses_exit_nhwindows(mesg);
nh_terminate(EXIT_SUCCESS);
}
/* Return a winid for a new window of the given type */
winid
curses_get_wid(int type)
{
winid ret;
static winid menu_wid = 20; /* Always even */
static winid text_wid = 21; /* Always odd */
switch (type) {
case NHW_MESSAGE:
return MESSAGE_WIN;
case NHW_MAP:
return MAP_WIN;
case NHW_STATUS:
return STATUS_WIN;
case NHW_MENU:
ret = menu_wid;
break;
case NHW_TEXT:
ret = text_wid;
break;
default:
panic("curses_get_wid: unsupported window type");
ret = -1; /* Not reached */
}
while (curses_window_exists(ret)) {
ret += 2;
if ((ret + 2) > 10000) { /* Avoid "wid2k" problem */
ret -= 9900;
}
}
if (type == NHW_MENU) {
menu_wid += 2;
} else {
text_wid += 2;
}
return ret;
}
/*
* Allocate a copy of the given string. If null, return a string of
* zero length.
*
* This is taken from copy_of() in tty/wintty.c.
*/
char *
curses_copy_of(const char *s)
{
if (!s)
s = "";
return strcpy((char *) alloc((unsigned) (strlen(s) + 1)), s);
}
/* Determine the number of lines needed for a string for a dialog window
of the given width */
int
curses_num_lines(const char *str, int width)
{
int last_space, count;
int curline = 1;
char substr[BUFSZ];
char tmpstr[BUFSZ];
strncpy(substr, str, BUFSZ-1);
substr[BUFSZ-1] = '\0';
while (strlen(substr) > (size_t) width) {
last_space = 0;
for (count = 0; count <= width; count++) {
if (substr[count] == ' ')
last_space = count;
}
if (last_space == 0) { /* No spaces found */
last_space = count - 1;
}
for (count = (last_space + 1); (size_t) count < strlen(substr); count++) {
tmpstr[count - (last_space + 1)] = substr[count];
}
tmpstr[count - (last_space + 1)] = '\0';
strcpy(substr, tmpstr);
curline++;
}
return curline;
}
/* Break string into smaller lines to fit into a dialog window of the
given width */
char *
curses_break_str(const char *str, int width, int line_num)
{
int last_space, count;
char *retstr;
int curline = 0;
int strsize = strlen(str) + 1;
#if __STDC_VERSION__ >= 199901L
char substr[strsize];
char curstr[strsize];
char tmpstr[strsize];
strcpy(substr, str);
#else
#ifndef BUFSZ
#define BUFSZ 256
#endif
char substr[BUFSZ * 2];
char curstr[BUFSZ * 2];
char tmpstr[BUFSZ * 2];
if (strsize > (BUFSZ * 2) - 1) {
paniclog("curses", "curses_break_str() string too long.");
strncpy(substr, str, (BUFSZ * 2) - 2);
substr[(BUFSZ * 2) - 1] = '\0';
} else
strcpy(substr, str);
#endif
while (curline < line_num) {
if (strlen(substr) == 0) {
break;
}
curline++;
last_space = 0;
for (count = 0; count <= width; count++) {
if (substr[count] == ' ') {
last_space = count;
} else if (substr[count] == '\0') {
last_space = count;
break;
}
}
if (last_space == 0) { /* No spaces found */
last_space = count - 1;
}
for (count = 0; count < last_space; count++) {
curstr[count] = substr[count];
}
curstr[count] = '\0';
if (substr[count] == '\0') {
break;
}
for (count = (last_space + 1); (size_t) count < strlen(substr); count++) {
tmpstr[count - (last_space + 1)] = substr[count];
}
tmpstr[count - (last_space + 1)] = '\0';
strcpy(substr, tmpstr);
}
if (curline < line_num) {
return NULL;
}
retstr = curses_copy_of(curstr);
return retstr;
}
/* Return the remaining portion of a string after hacking-off line_num lines */
char *
curses_str_remainder(const char *str, int width, int line_num)
{
int last_space, count;
char *retstr;
int curline = 0;
int strsize = strlen(str) + 1;
#if __STDC_VERSION__ >= 199901L
char substr[strsize];
char curstr[strsize];
char tmpstr[strsize];
strcpy(substr, str);
#else
#ifndef BUFSZ
#define BUFSZ 256
#endif
char substr[BUFSZ * 2];
char curstr[BUFSZ * 2];
char tmpstr[BUFSZ * 2];
if (strsize > (BUFSZ * 2) - 1) {
paniclog("curses", "curses_str_remainder() string too long.");
strncpy(substr, str, (BUFSZ * 2) - 2);
substr[(BUFSZ * 2) - 1] = '\0';
} else
strcpy(substr, str);
#endif
while (curline < line_num) {
if (strlen(substr) == 0) {
break;
}
curline++;
last_space = 0;
for (count = 0; count <= width; count++) {
if (substr[count] == ' ') {
last_space = count;
} else if (substr[count] == '\0') {
last_space = count;
break;
}
}
if (last_space == 0) { /* No spaces found */
last_space = count - 1;
}
for (count = 0; count < last_space; count++) {
curstr[count] = substr[count];
}
curstr[count] = '\0';
if (substr[count] == '\0') {
break;
}
for (count = (last_space + 1); (size_t) count < strlen(substr); count++) {
tmpstr[count - (last_space + 1)] = substr[count];
}
tmpstr[count - (last_space + 1)] = '\0';
strcpy(substr, tmpstr);
}
if (curline < line_num) {
return NULL;
}
retstr = curses_copy_of(substr);
return retstr;
}
/* Determine if the given NetHack winid is a menu window */
boolean
curses_is_menu(winid wid)
{
if ((wid > 19) && !(wid % 2)) { /* Even number */
return TRUE;
} else {
return FALSE;
}
}
/* Determine if the given NetHack winid is a text window */
boolean
curses_is_text(winid wid)
{
if ((wid > 19) && (wid % 2)) { /* Odd number */
return TRUE;
} else {
return FALSE;
}
}
/* Replace certain characters with portable drawing characters if
cursesgraphics option is enabled */
int
curses_convert_glyph(int ch, int glyph)
{
int symbol;
if (Is_rogue_level(&u.uz)) {
return ch;
}
/* Save some processing time by returning if the glyph represents
an object that we don't have custom characters for */
if (!glyph_is_cmap(glyph)) {
return ch;
}
symbol = glyph_to_cmap(glyph);
/* If user selected a custom character for this object, don't
override this. */
if (((glyph_is_cmap(glyph)) && (ch != showsyms[symbol]))) {
return ch;
}
switch (symbol) {
case S_vwall:
return ACS_VLINE;
case S_hwall:
return ACS_HLINE;
case S_tlcorn:
return ACS_ULCORNER;
case S_trcorn:
return ACS_URCORNER;
case S_blcorn:
return ACS_LLCORNER;
case S_brcorn:
return ACS_LRCORNER;
case S_crwall:
return ACS_PLUS;
case S_tuwall:
return ACS_BTEE;
case S_tdwall:
return ACS_TTEE;
case S_tlwall:
return ACS_RTEE;
case S_trwall:
return ACS_LTEE;
case S_tree:
return ACS_PLMINUS;
case S_corr:
return ACS_CKBOARD;
case S_litcorr:
return ACS_CKBOARD;
}
return ch;
}
/* Move text cursor to specified coordinates in the given NetHack window */
void
curses_move_cursor(winid wid, int x, int y)
{
int sx, sy, ex, ey;
int xadj = 0;
int yadj = 0;
#ifndef PDCURSES
WINDOW *win = curses_get_nhwin(MAP_WIN);
#endif
if (wid != MAP_WIN) {
return;
}
#ifdef PDCURSES
/* PDCurses seems to not handle wmove correctly, so we use move and
physical screen coordinates instead */
curses_get_window_xy(wid, &xadj, &yadj);
#endif
curs_x = x + xadj;
curs_y = y + yadj;
curses_map_borders(&sx, &sy, &ex, &ey, x, y);
if (curses_window_has_border(wid)) {
curs_x++;
curs_y++;
}
if ((x >= sx) && (x <= ex) && (y >= sy) && (y <= ey)) {
curs_x -= sx;
curs_y -= sy;
#ifdef PDCURSES
move(curs_y, curs_x);
#else
wmove(win, curs_y, curs_x);
#endif
}
}
/* Perform actions that should be done every turn before nhgetch() */
void
curses_prehousekeeping()
{
#ifndef PDCURSES
WINDOW *win = curses_get_nhwin(MAP_WIN);
#endif /* PDCURSES */
if ((curs_x > -1) && (curs_y > -1)) {
curs_set(1);
#ifdef PDCURSES
/* PDCurses seems to not handle wmove correctly, so we use move
and physical screen coordinates instead */
move(curs_y, curs_x);
#else
wmove(win, curs_y, curs_x);
#endif /* PDCURSES */
curses_refresh_nhwin(MAP_WIN);
}
}
/* Perform actions that should be done every turn after nhgetch() */
void
curses_posthousekeeping()
{
curs_set(0);
//curses_decrement_highlights(FALSE);
curses_clear_unhighlight_message_window();
}
void
curses_view_file(const char *filename, boolean must_exist)
{
winid wid;
anything *identifier;
char buf[BUFSZ];
menu_item *selected = NULL;
dlb *fp = dlb_fopen(filename, "r");
if ((fp == NULL) && (must_exist)) {
pline("Cannot open %s for reading!", filename);
}
if (fp == NULL) {
return;
}
wid = curses_get_wid(NHW_MENU);
curses_create_nhmenu(wid);
identifier = malloc(sizeof (anything));
identifier->a_void = NULL;
while (dlb_fgets(buf, BUFSZ, fp) != NULL) {
curses_add_menu(wid, NO_GLYPH, identifier, 0, 0, A_NORMAL, buf, FALSE);
}
dlb_fclose(fp);
curses_end_menu(wid, "");
curses_select_menu(wid, PICK_NONE, &selected);
}
void
curses_rtrim(char *str)
{
char *s;
for (s = str; *s != '\0'; ++s);
for (--s; isspace(*s) && s > str; --s);
if (s == str)
*s = '\0';
else
*(++s) = '\0';
}
/* Read numbers until non-digit is encountered, and return number
in int form. */
int
curses_get_count(int first_digit)
{
long current_count = first_digit;
int current_char;
current_char = curses_read_char();
while (isdigit(current_char)) {
current_count = (current_count * 10) + (current_char - '0');
if (current_count > LARGEST_INT) {
current_count = LARGEST_INT;
}
pline("Count: %ld", current_count);
current_char = curses_read_char();
}
ungetch(current_char);
if (current_char == '\033') { /* Cancelled with escape */
current_count = -1;
}
return current_count;
}
/* Convert the given NetHack text attributes into the format curses
understands, and return that format mask. */
int
curses_convert_attr(int attr)
{
int curses_attr;
switch (attr) {
case ATR_NONE:
curses_attr = A_NORMAL;
break;
case ATR_ULINE:
curses_attr = A_UNDERLINE;
break;
case ATR_BOLD:
curses_attr = A_BOLD;
break;
case ATR_BLINK:
curses_attr = A_BLINK;
break;
case ATR_INVERSE:
curses_attr = A_REVERSE;
break;
default:
curses_attr = A_NORMAL;
}
return curses_attr;
}
/* Map letter attributes from a string to bitmask. Return mask on
success, or 0 if not found */
int
curses_read_attrs(char *attrs)
{
int retattr = 0;
if (strchr(attrs, 'b') || strchr(attrs, 'B')) {
retattr = retattr | A_BOLD;
}
if (strchr(attrs, 'i') || strchr(attrs, 'I')) {
retattr = retattr | A_REVERSE;
}
if (strchr(attrs, 'u') || strchr(attrs, 'U')) {
retattr = retattr | A_UNDERLINE;
}
if (strchr(attrs, 'k') || strchr(attrs, 'K')) {
retattr = retattr | A_BLINK;
}
#ifdef A_ITALIC
if (strchr(attrs, 't') || strchr(attrs, 'T')) {
retattr = retattr | A_ITALIC;
}
#endif
#ifdef A_RIGHTLINE
if (strchr(attrs, 'r') || strchr(attrs, 'R')) {
retattr = retattr | A_RIGHTLINE;
}
#endif
#ifdef A_LEFTLINE
if (strchr(attrs, 'l') || strchr(attrs, 'L')) {
retattr = retattr | A_LEFTLINE;
}
#endif
return retattr;
}
/* Convert special keys into values that NetHack can understand.
Currently this is limited to arrow keys, but this may be expanded. */
int
curses_convert_keys(int key)
{
int ret = key;
if (ret == '\033') {
ret = parse_escape_sequence();
}
/* Handle arrow keys */
switch (key) {
case KEY_LEFT:
if (iflags.num_pad) {
ret = '4';
} else {
ret = 'h';
}
break;
case KEY_RIGHT:
if (iflags.num_pad) {
ret = '6';
} else {
ret = 'l';
}
break;
case KEY_UP:
if (iflags.num_pad) {
ret = '8';
} else {
ret = 'k';
}
break;
case KEY_DOWN:
if (iflags.num_pad) {
ret = '2';
} else {
ret = 'j';
}
break;
#ifdef KEY_A1
case KEY_A1:
if (iflags.num_pad) {
ret = '7';
} else {
ret = 'y';
}
break;
#endif /* KEY_A1 */
#ifdef KEY_A3
case KEY_A3:
if (iflags.num_pad) {
ret = '9';
} else {
ret = 'u';
}
break;
#endif /* KEY_A3 */
#ifdef KEY_C1
case KEY_C1:
if (iflags.num_pad) {
ret = '1';
} else {
ret = 'b';
}
break;
#endif /* KEY_C1 */
#ifdef KEY_C3
case KEY_C3:
if (iflags.num_pad) {
ret = '3';
} else {
ret = 'n';
}
break;
#endif /* KEY_C3 */
#ifdef KEY_B2
case KEY_B2:
if (iflags.num_pad) {
ret = '5';
} else {
ret = 'g';
}
break;
#endif /* KEY_B2 */
}
return ret;
}
/* Process mouse events. Mouse movement is processed until no further
mouse movement events are available. Returns 0 for a mouse click
event, or the first non-mouse key event in the case of mouse
movement. */
int
curses_get_mouse(int *mousex, int *mousey, int *mod)
{
int key = '\033';
#ifdef NCURSES_MOUSE_VERSION
MEVENT event;
if (getmouse(&event) == OK) { /* When the user clicks left mouse button */
if (event.bstate & BUTTON1_CLICKED) {
/* See if coords are in map window & convert coords */
if (wmouse_trafo(mapwin, &event.y, &event.x, TRUE)) {
key = 0; /* Flag mouse click */
*mousex = event.x;
*mousey = event.y;
if (curses_window_has_border(MAP_WIN)) {
(*mousex)--;
(*mousey)--;
}
*mod = CLICK_1;
}
}
}
#endif /* NCURSES_MOUSE_VERSION */
return key;
}
static int
parse_escape_sequence(void)
{
#ifndef PDCURSES
int ret;
timeout(10);
ret = getch();
if (ret != ERR) { /* Likely an escape sequence */
if (((ret >= 'a') && (ret <= 'z')) || ((ret >= '0') && (ret <= '9'))) {
ret |= 0x80; /* Meta key support for most terminals */
} else if (ret == 'O') { /* Numeric keypad */
ret = getch();
if ((ret != ERR) && (ret >= 112) && (ret <= 121)) {
ret = ret - 112 + '0'; /* Convert to number */
} else {
ret = '\033'; /* Escape */
}
}
} else {
ret = '\033'; /* Just an escape character */
}
timeout(-1);
return ret;
#else
return '\033';
#endif /* !PDCURSES */
}

30
win/curses/cursmisc.h Normal file
View File

@@ -0,0 +1,30 @@
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
#ifndef CURSMISC_H
# define CURSMISC_H
/* Global declarations */
int curses_read_char(void);
void curses_toggle_color_attr(WINDOW * win, int color, int attr, int onoff);
void curses_bail(const char *mesg);
winid curses_get_wid(int type);
char *curses_copy_of(const char *s);
int curses_num_lines(const char *str, int width);
char *curses_break_str(const char *str, int width, int line_num);
char *curses_str_remainder(const char *str, int width, int line_num);
boolean curses_is_menu(winid wid);
boolean curses_is_text(winid wid);
int curses_convert_glyph(int ch, int glyph);
void curses_move_cursor(winid wid, int x, int y);
void curses_prehousekeeping(void);
void curses_posthousekeeping(void);
void curses_view_file(const char *filename, boolean must_exist);
void curses_rtrim(char *str);
int curses_get_count(int first_digit);
int curses_convert_attr(int attr);
int curses_read_attrs(char *attrs);
int curses_convert_keys(int key);
int curses_get_mouse(int *mousex, int *mousey, int *mod);
#endif /* CURSMISC_H */

1583
win/curses/cursstat.c Normal file

File diff suppressed because it is too large Load Diff

21
win/curses/cursstat.h Normal file
View File

@@ -0,0 +1,21 @@
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
#ifndef CURSSTAT_H
# define CURSSTAT_H
/* Used by handle_stat_change to handle some stats differently. Not an enum
because this is how NetHack code generally handles them. */
# define STAT_OTHER 0
# define STAT_STR 1
# define STAT_GOLD 2
# define STAT_AC 4
# define STAT_TIME 5
# define STAT_TROUBLE 6
/* Global declarations */
void curses_update_stats();
void curses_decrement_highlights(boolean);
attr_t curses_color_attr(int nh_color, int bg_color);
#endif /* CURSSTAT_H */

752
win/curses/curswins.c Normal file
View File

@@ -0,0 +1,752 @@
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
#include "curses.h"
#include "hack.h"
#include "wincurs.h"
#include "curswins.h"
/* Window handling for curses interface */
/* Private declarations */
typedef struct nhw {
winid nhwin; /* NetHack window id */
WINDOW *curwin; /* Curses window pointer */
int width; /* Usable width not counting border */
int height; /* Usable height not counting border */
int x; /* start of window on terminal (left) */
int y; /* start of window on termial (top) */
int orientation; /* Placement of window relative to map */
boolean border; /* Whether window has a visible border */
} nethack_window;
typedef struct nhwd {
winid nhwid; /* NetHack window id */
struct nhwd *prev_wid; /* Pointer to previous entry */
struct nhwd *next_wid; /* Pointer to next entry */
} nethack_wid;
typedef struct nhchar {
int ch; /* character */
int color; /* color info for character */
int attr; /* attributes of character */
} nethack_char;
static boolean map_clipped; /* Map window smaller than 80x21 */
static nethack_window nhwins[NHWIN_MAX]; /* NetHack window array */
static nethack_char map[ROWNO][COLNO]; /* Map window contents */
static nethack_wid *nhwids = NULL; /* NetHack wid array */
static boolean is_main_window(winid wid);
static void write_char(WINDOW * win, int x, int y, nethack_char ch);
static void clear_map(void);
/* Create a window with the specified size and orientation */
WINDOW *
curses_create_window(int width, int height, orient orientation)
{
int mapx, mapy, maph, mapw = 0;
int startx = 0;
int starty = 0;
WINDOW *win;
boolean map_border = FALSE;
int mapb_offset = 0;
if ((orientation == UP) || (orientation == DOWN) ||
(orientation == LEFT) || (orientation == RIGHT)) {
if (invent || (moves > 1)) {
map_border = curses_window_has_border(MAP_WIN);
curses_get_window_xy(MAP_WIN, &mapx, &mapy);
curses_get_window_size(MAP_WIN, &maph, &mapw);
} else {
map_border = TRUE;
mapx = 0;
mapy = 0;
maph = term_rows;
mapw = term_cols;
}
}
if (map_border) {
mapb_offset = 1;
}
width += 2; /* leave room for bounding box */
height += 2;
if ((width > term_cols) || (height > term_rows))
panic("curses_create_window: Terminal too small for dialog window");
switch (orientation) {
case CENTER:
startx = (term_cols / 2) - (width / 2);
starty = (term_rows / 2) - (height / 2);
break;
case UP:
if (invent || (moves > 1)) {
startx = (mapw / 2) - (width / 2) + mapx + mapb_offset;
} else {
startx = 0;
}
starty = mapy + mapb_offset;
break;
case DOWN:
if (invent || (moves > 1)) {
startx = (mapw / 2) - (width / 2) + mapx + mapb_offset;
} else {
startx = 0;
}
starty = height - mapy - 1 - mapb_offset;
break;
case LEFT:
if (map_border && (width < term_cols))
startx = 1;
else
startx = 0;
starty = term_rows - height;
break;
case RIGHT:
if (invent || (moves > 1)) {
startx = (mapw + mapx + (mapb_offset * 2)) - width;
} else {
startx = term_cols - width;
}
starty = 0;
break;
default:
panic("curses_create_window: Bad orientation");
break;
}
if (startx < 0) {
startx = 0;
}
if (starty < 0) {
starty = 0;
}
win = newwin(height, width, starty, startx);
curses_toggle_color_attr(win, DIALOG_BORDER_COLOR, NONE, ON);
box(win, 0, 0);
curses_toggle_color_attr(win, DIALOG_BORDER_COLOR, NONE, OFF);
return win;
}
/* Erase and delete curses window, and refresh standard windows */
void
curses_destroy_win(WINDOW * win)
{
werase(win);
wrefresh(win);
delwin(win);
curses_refresh_nethack_windows();
}
/* Refresh nethack windows if they exist, or base window if not */
void
curses_refresh_nethack_windows()
{
WINDOW *status_window, *message_window, *map_window, *inv_window;
status_window = curses_get_nhwin(STATUS_WIN);
message_window = curses_get_nhwin(MESSAGE_WIN);
map_window = curses_get_nhwin(MAP_WIN);
inv_window = curses_get_nhwin(INV_WIN);
if ((moves <= 1) && !invent) {
/* Main windows not yet displayed; refresh base window instead */
touchwin(stdscr);
refresh();
} else {
touchwin(status_window);
wnoutrefresh(status_window);
touchwin(map_window);
wnoutrefresh(map_window);
touchwin(message_window);
wnoutrefresh(message_window);
if (inv_window) {
touchwin(inv_window);
wnoutrefresh(inv_window);
}
doupdate();
}
}
/* Return curses window pointer for given NetHack winid */
WINDOW *
curses_get_nhwin(winid wid)
{
if (!is_main_window(wid)) {
panic("curses_get_nhwin: wid out of range. Not a main window.");
}
return nhwins[wid].curwin;
}
/* Add curses window pointer and window info to list for given NetHack winid */
void
curses_add_nhwin(winid wid, int height, int width, int y, int x,
orient orientation, boolean border)
{
WINDOW *win;
int real_width = width;
int real_height = height;
if (!is_main_window(wid)) {
panic("curses_add_nhwin: wid out of range. Not a main window.");
}
nhwins[wid].nhwin = wid;
nhwins[wid].border = border;
nhwins[wid].width = width;
nhwins[wid].height = height;
nhwins[wid].x = x;
nhwins[wid].y = y;
nhwins[wid].orientation = orientation;
if (border) {
real_width += 2; /* leave room for bounding box */
real_height += 2;
}
win = newwin(real_height, real_width, y, x);
switch (wid) {
case MESSAGE_WIN:
messagewin = win;
break;
case STATUS_WIN:
statuswin = win;
break;
case MAP_WIN:
mapwin = win;
if ((width < COLNO) || (height < ROWNO)) {
map_clipped = TRUE;
} else {
map_clipped = FALSE;
}
break;
}
if (border) {
box(win, 0, 0);
}
nhwins[wid].curwin = win;
}
/* Add wid to list of known window IDs */
void
curses_add_wid(winid wid)
{
nethack_wid *new_wid;
nethack_wid *widptr = nhwids;
new_wid = malloc(sizeof (nethack_wid));
new_wid->nhwid = wid;
new_wid->next_wid = NULL;
if (widptr == NULL) {
new_wid->prev_wid = NULL;
nhwids = new_wid;
} else {
while (widptr->next_wid != NULL) {
widptr = widptr->next_wid;
}
new_wid->prev_wid = widptr;
widptr->next_wid = new_wid;
}
}
/* refresh a curses window via given nethack winid */
void
curses_refresh_nhwin(winid wid)
{
wnoutrefresh(curses_get_nhwin(wid));
doupdate();
}
/* Delete curses window via given NetHack winid and remove entry from list */
void
curses_del_nhwin(winid wid)
{
if (curses_is_menu(wid) || curses_is_text(wid)) {
curses_del_menu(wid);
return;
}
if (!is_main_window(wid)) {
panic("curses_del_nhwin: wid out of range. Not a main window.");
}
nhwins[wid].curwin = NULL;
nhwins[wid].nhwin = -1;
}
/* Delete wid from list of known window IDs */
void
curses_del_wid(winid wid)
{
nethack_wid *tmpwid;
nethack_wid *widptr = nhwids;
if (curses_is_menu(wid) || curses_is_text(wid)) {
curses_del_menu(wid);
}
while (widptr != NULL) {
if (widptr->nhwid == wid) {
if (widptr->prev_wid != NULL) {
tmpwid = widptr->prev_wid;
tmpwid->next_wid = widptr->next_wid;
} else {
nhwids = widptr->next_wid; /* New head mode, or NULL */
}
if (widptr->next_wid != NULL) {
tmpwid = widptr->next_wid;
tmpwid->prev_wid = widptr->prev_wid;
}
free(widptr);
break;
}
widptr = widptr->next_wid;
}
}
/* Print a single character in the given window at the given coordinates */
void
curses_putch(winid wid, int x, int y, int ch, int color, int attr)
{
int sx, sy, ex, ey;
boolean border = curses_window_has_border(wid);
nethack_char nch;
static boolean map_initted = FALSE;
/*
if (wid == STATUS_WIN) {
curses_update_stats();
}
*/
if (wid != MAP_WIN) {
return;
}
if (!map_initted) {
clear_map();
map_initted = TRUE;
}
map[y][x].ch = ch;
map[y][x].color = color;
map[y][x].attr = attr;
nch = map[y][x];
(void) curses_map_borders(&sx, &sy, &ex, &ey, -1, -1);
if ((x >= sx) && (x <= ex) && (y >= sy) && (y <= ey)) {
if (border) {
x++;
y++;
}
write_char(mapwin, x - sx, y - sy, nch);
}
/* refresh after every character?
* Fair go, mate! Some of us are playing from Australia! */
/* wrefresh(mapwin); */
}
/* Get x, y coordinates of curses window on the physical terminal window */
void
curses_get_window_xy(winid wid, int *x, int *y)
{
if (!is_main_window(wid)) {
panic("curses_get_window_xy: wid out of range. Not a main window.");
}
*x = nhwins[wid].x;
*y = nhwins[wid].y;
}
/* Get usable width and height curses window on the physical terminal window */
void
curses_get_window_size(winid wid, int *height, int *width)
{
*height = nhwins[wid].height;
*width = nhwins[wid].width;
}
/* Determine if given window has a visible border */
boolean
curses_window_has_border(winid wid)
{
return nhwins[wid].border;
}
/* Determine if window for given winid exists */
boolean
curses_window_exists(winid wid)
{
nethack_wid *widptr = nhwids;
while (widptr != NULL) {
if (widptr->nhwid == wid) {
return TRUE;
}
widptr = widptr->next_wid;
}
return FALSE;
}
/* Return the orientation of the specified window */
int
curses_get_window_orientation(winid wid)
{
if (!is_main_window(wid)) {
panic
("curses_get_window_orientation: wid out of range. Not a main window.");
}
return nhwins[wid].orientation;
}
/* Output a line of text to specified NetHack window with given coordinates
and text attributes */
void
curses_puts(winid wid, int attr, const char *text)
{
anything *identifier;
WINDOW *win = NULL;
if (is_main_window(wid)) {
win = curses_get_nhwin(wid);
}
if (wid == MESSAGE_WIN) {
curses_message_win_puts(text, FALSE);
return;
}
#if 0
if (wid == STATUS_WIN) {
curses_update_stats(); /* We will do the write ourselves */
/* Inventory updating isn't performed on redraws, so
also update inventory here... */
curses_update_inventory();
return;
}
#endif
if (curses_is_menu(wid) || curses_is_text(wid)) {
if (!curses_menu_exists(wid)) {
panic("curses_puts: Attempted write to nonexistant window!");
}
identifier = malloc(sizeof (anything));
identifier->a_void = NULL;
curses_add_nhmenu_item(wid, NO_GLYPH, identifier, 0, 0, attr, text,
FALSE);
} else {
waddstr(win, text);
wnoutrefresh(win);
}
}
/* Clear the contents of a window via the given NetHack winid */
void
curses_clear_nhwin(winid wid)
{
WINDOW *win = curses_get_nhwin(wid);
boolean border = curses_window_has_border(wid);
if (wid == MAP_WIN) {
clearok(win, TRUE); /* Redraw entire screen when refreshed */
clear_map();
}
werase(win);
if (border) {
box(win, 0, 0);
}
}
/* Change colour of window border to alert player to something */
void
curses_alert_win_border(winid wid, boolean onoff)
{
WINDOW *win = curses_get_nhwin(wid);
if (!win || !curses_window_has_border(wid))
return;
if (onoff)
curses_toggle_color_attr(win, ALERT_BORDER_COLOR, NONE, ON);
box(win, 0, 0);
if (onoff)
curses_toggle_color_attr(win, ALERT_BORDER_COLOR, NONE, OFF);
wnoutrefresh(win);
}
void
curses_alert_main_borders(boolean onoff)
{
curses_alert_win_border(MAP_WIN, onoff);
curses_alert_win_border(MESSAGE_WIN, onoff);
curses_alert_win_border(STATUS_WIN, onoff);
curses_alert_win_border(INV_WIN, onoff);
}
/* Return true if given wid is a main NetHack window */
static boolean
is_main_window(winid wid)
{
if ((wid == MESSAGE_WIN) || (wid == MAP_WIN) || (wid == STATUS_WIN) || wid == INV_WIN) {
return TRUE;
} else {
return FALSE;
}
}
/* Unconditionally write a single character to a window at the given
coordinates without a refresh. Currently only used for the map. */
static void
write_char(WINDOW * win, int x, int y, nethack_char nch)
{
curses_toggle_color_attr(win, nch.color, nch.attr, ON);
#ifdef PDCURSES
mvwaddrawch(win, y, x, nch.ch);
#else
mvwaddch(win, y, x, nch.ch);
#endif
curses_toggle_color_attr(win, nch.color, nch.attr, OFF);
}
/* Draw the entire visible map onto the screen given the visible map
boundaries */
void
curses_draw_map(int sx, int sy, int ex, int ey)
{
int curx, cury;
int bspace = 0;
#ifdef MAP_SCROLLBARS
int sbsx, sbsy, sbex, sbey, count;
nethack_char hsb_back, hsb_bar, vsb_back, vsb_bar;
#endif
if (curses_window_has_border(MAP_WIN)) {
bspace++;
}
#ifdef MAP_SCROLLBARS
hsb_back.ch = '-';
hsb_back.color = SCROLLBAR_BACK_COLOR;
hsb_back.attr = A_NORMAL;
hsb_bar.ch = '*';
hsb_bar.color = SCROLLBAR_COLOR;
hsb_bar.attr = A_NORMAL;
vsb_back.ch = '|';
vsb_back.color = SCROLLBAR_BACK_COLOR;
vsb_back.attr = A_NORMAL;
vsb_bar.ch = '*';
vsb_bar.color = SCROLLBAR_COLOR;
vsb_bar.attr = A_NORMAL;
/* Horizontal scrollbar */
if ((sx > 0) || (ex < (COLNO - 1))) {
sbsx = (sx * ((long) (ex - sx + 1) / COLNO));
sbex = (ex * ((long) (ex - sx + 1) / COLNO));
for (count = 0; count < sbsx; count++) {
write_char(mapwin, count + bspace, ey - sy + 1 + bspace, hsb_back);
}
for (count = sbsx; count <= sbex; count++) {
write_char(mapwin, count + bspace, ey - sy + 1 + bspace, hsb_bar);
}
for (count = sbex + 1; count <= (ex - sx); count++) {
write_char(mapwin, count + bspace, ey - sy + 1 + bspace, hsb_back);
}
}
/* Vertical scrollbar */
if ((sy > 0) || (ey < (ROWNO - 1))) {
sbsy = (sy * ((long) (ey - sy + 1) / ROWNO));
sbey = (ey * ((long) (ey - sy + 1) / ROWNO));
for (count = 0; count < sbsy; count++) {
write_char(mapwin, ex - sx + 1 + bspace, count + bspace, vsb_back);
}
for (count = sbsy; count <= sbey; count++) {
write_char(mapwin, ex - sx + 1 + bspace, count + bspace, vsb_bar);
}
for (count = sbey + 1; count <= (ey - sy); count++) {
write_char(mapwin, ex - sx + 1 + bspace, count + bspace, vsb_back);
}
}
#endif /* MAP_SCROLLBARS */
for (curx = sx; curx <= ex; curx++) {
for (cury = sy; cury <= ey; cury++) {
write_char(mapwin, curx - sx + bspace, cury - sy + bspace,
map[cury][curx]);
}
}
}
/* Init map array to blanks */
static void
clear_map()
{
int x, y;
for (x = 0; x < COLNO; x++) {
for (y = 0; y < ROWNO; y++) {
map[y][x].ch = ' ';
map[y][x].color = NO_COLOR;
map[y][x].attr = A_NORMAL;
}
}
}
/* Determine visible boundaries of map, and determine if it needs to be
based on the location of the player. */
boolean
curses_map_borders(int *sx, int *sy, int *ex, int *ey, int ux, int uy)
{
static int width = 0;
static int height = 0;
static int osx = 0;
static int osy = 0;
static int oex = 0;
static int oey = 0;
static int oux = -1;
static int ouy = -1;
if ((oux == -1) || (ouy == -1)) {
oux = u.ux;
ouy = u.uy;
}
if (ux == -1) {
ux = oux;
} else {
oux = ux;
}
if (uy == -1) {
uy = ouy;
} else {
ouy = uy;
}
curses_get_window_size(MAP_WIN, &height, &width);
#ifdef MAP_SCROLLBARS
if (width < COLNO) {
height--; /* room for horizontal scrollbar */
}
if (height < ROWNO) {
width--; /* room for vertical scrollbar */
if (width == COLNO) {
height--;
}
}
#endif /* MAP_SCROLLBARS */
if (width >= COLNO) {
*sx = 0;
*ex = COLNO - 1;
} else {
*ex = (width / 2) + ux;
*sx = *ex - (width - 1);
if (*ex >= COLNO) {
*sx = COLNO - width;
*ex = COLNO - 1;
} else if (*sx < 0) {
*sx = 0;
*ex = width - 1;
}
}
if (height >= ROWNO) {
*sy = 0;
*ey = ROWNO - 1;
} else {
*ey = (height / 2) + uy;
*sy = *ey - (height - 1);
if (*ey >= ROWNO) {
*sy = ROWNO - height;
*ey = ROWNO - 1;
} else if (*sy < 0) {
*sy = 0;
*ey = height - 1;
}
}
if ((*sx != osx) || (*sy != osy) || (*ex != oex) || (*ey != oey) ||
map_clipped) {
osx = *sx;
osy = *sy;
oex = *ex;
oey = *ey;
return TRUE;
}
return FALSE;
}

33
win/curses/curswins.h Normal file
View File

@@ -0,0 +1,33 @@
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
#ifndef CURSWIN_H
# define CURSWIN_H
/* Global declarations */
WINDOW *curses_create_window(int width, int height, orient orientation);
void curses_destroy_win(WINDOW * win);
void curses_refresh_nethack_windows(void);
WINDOW *curses_get_nhwin(winid wid);
void curses_add_nhwin(winid wid, int height, int width, int y, int x,
orient orientation, boolean border);
void curses_add_wid(winid wid);
void curses_refresh_nhwin(winid wid);
void curses_del_nhwin(winid wid);
void curses_del_wid(winid wid);
void curses_putch(winid wid, int x, int y, int ch, int color, int attrs);
void curses_get_window_xy(winid wid, int *x, int *y);
boolean curses_window_has_border(winid wid);
boolean curses_window_exists(winid wid);
int curses_get_window_orientation(winid wid);
void curses_puts(winid wid, int attr, const char *text);
void curses_clear_nhwin(winid wid);
void curses_alert_win_border(winid wid, boolean onoff);
void curses_alert_main_borders(boolean onoff);
void curses_draw_map(int sx, int sy, int ex, int ey);
boolean curses_map_borders(int *sx, int *sy, int *ex, int *ey, int ux, int uy);
#endif /* CURSWIN_H */

View File

@@ -1271,6 +1271,13 @@ int color;
if (windowprocs.name != NULL && !strcmpi(windowprocs.name, "Qt"))
return 1;
#endif
#ifdef CURSES_GRAPHICS
/* XXX has_color() should be added to windowprocs */
/* iflags.wc_color is set to false and the option disabled if the
terminal cannot display color */
if (windowprocs.name != NULL && !strcmpi(windowprocs.name, "curses"))
return iflags.wc_color;
#endif
#ifdef AMII_GRAPHICS
/* hilites[] not used */
return iflags.use_color ? 1 : 0;

View File

@@ -16,6 +16,16 @@
<PropertyGroup>
<OutDir>$(BinDir)</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="Exists('$(PDCURSES)')">
<ClCompile>
<AdditionalIncludeDirectories>$(PDCURSES);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>CURSES_GRAPHICS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<AdditionalLibraryDirectories>$(PDCURSES)\bin\$(Platform)\$(Configuration)\</AdditionalLibraryDirectories>
<AdditionalDependencies>PDCurses.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalOptions>/Gs /Oi- %(AdditionalOptions)</AdditionalOptions>
@@ -154,6 +164,14 @@
<ClCompile Include="$(WinTtyDir)getline.c" />
<ClCompile Include="$(WinTtyDir)topl.c" />
<ClCompile Include="$(WinTtyDir)wintty.c" />
<ClCompile Condition="Exists('$(PDCURSES)')" Include="$(WinCursDir)cursdial.c" />
<ClCompile Condition="Exists('$(PDCURSES)')" Include="$(WinCursDir)cursinit.c" />
<ClCompile Condition="Exists('$(PDCURSES)')" Include="$(WinCursDir)cursinvt.c" />
<ClCompile Condition="Exists('$(PDCURSES)')" Include="$(WinCursDir)cursmain.c" />
<ClCompile Condition="Exists('$(PDCURSES)')" Include="$(WinCursDir)cursmesg.c" />
<ClCompile Condition="Exists('$(PDCURSES)')" Include="$(WinCursDir)cursmisc.c" />
<ClCompile Condition="Exists('$(PDCURSES)')" Include="$(WinCursDir)cursstat.c" />
<ClCompile Condition="Exists('$(PDCURSES)')" Include="$(WinCursDir)curswins.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(IncDir)align.h" />
@@ -212,6 +230,13 @@
<ClInclude Include="$(IncDir)you.h" />
<ClInclude Include="$(IncDir)youprop.h" />
<ClInclude Include="$(WinWin32Dir)nhresource.h" />
<ClInclude Include="$(WinCursDir)cursdial.h" />
<ClInclude Include="$(WinCursDir)cursinit.h" />
<ClInclude Include="$(WinCursDir)cursinvt.h" />
<ClInclude Include="$(WinCursDir)cursmesg.h" />
<ClInclude Include="$(WinCursDir)cursmisc.h" />
<ClInclude Include="$(WinCursDir)cursstat.h" />
<ClInclude Include="$(WinCursDir)curswins.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="$(WinWin32Dir)NetHack.rc" />
@@ -229,4 +254,4 @@
<Target Name="AfterRebuild">
<MSBuild Projects="afternethack.proj" Targets="Build" Properties="Configuration=$(Configuration)" />
</Target>
</Project>
</Project>

View File

@@ -20,5 +20,9 @@
<WinWin32Dir>$(RootDir)win\win32\</WinWin32Dir>
<OutDir>$(ToolsDir)</OutDir>
<IntDir>$(ObjDir)</IntDir>
<WinCursDir>$(RootDir)win\curses\</WinCursDir>
</PropertyGroup>
<PropertyGroup Condition="'$(PDCURSES)'=='' AND Exists('$(RootDir)..\PDCurses')">
<PDCURSES>$(RootDir)..\PDCurses\</PDCURSES>
</PropertyGroup>
</Project>