From cb430610766793433d67831eaf28feff585895d0 Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 16 Nov 2018 20:51:22 -0500 Subject: [PATCH 01/15] curses changes to existing files --- doc/window.doc | 7 +++ include/config.h | 7 +++ include/flag.h | 19 ++++-- include/ntconf.h | 5 +- include/rm.h | 9 +-- include/winprocs.h | 6 +- src/cmd.c | 2 +- src/drawing.c | 15 ++++- src/options.c | 120 ++++++++++++++++++++++++++++++++++++- src/rip.c | 2 +- src/windows.c | 6 ++ sys/share/pcmain.c | 16 +++-- sys/unix/Makefile.src | 63 +++++++++++++++++-- sys/unix/hints/linux | 7 ++- sys/unix/hints/macosx10.10 | 9 +++ sys/winnt/Makefile.msc | 84 +++++++++++++++++++++----- util/makedefs.c | 3 + win/tty/termcap.c | 7 +++ 18 files changed, 345 insertions(+), 42 deletions(-) diff --git a/doc/window.doc b/doc/window.doc index a564d5b7b..0b7cbf52f 100644 --- a/doc/window.doc +++ b/doc/window.doc @@ -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: diff --git a/include/config.h b/include/config.h index ef6d0e534..4f242679a 100644 --- a/include/config.h +++ b/include/config.h @@ -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 diff --git a/include/flag.h b/include/flag.h index b8c6a5993..b0be1a0f9 100644 --- a/include/flag.h +++ b/include/flag.h @@ -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 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 */ }; /* diff --git a/include/ntconf.h b/include/ntconf.h index c0e2f77c0..73d421fd8 100644 --- a/include/ntconf.h +++ b/include/ntconf.h @@ -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 +#ifndef CURSES_GRAPHICS +#include /* conflicting definitions with curses.h */ +#endif #undef kbhit /* Use our special NT kbhit */ #define kbhit (*nt_kbhit) diff --git a/include/rm.h b/include/rm.h index 7fd41c47c..1406e5723 100644 --- a/include/rm.h +++ b/include/rm.h @@ -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]; diff --git a/include/winprocs.h b/include/winprocs.h index a0c490f11..326f44eb2 100644 --- a/include/winprocs.h +++ b/include/winprocs.h @@ -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 diff --git a/src/cmd.c b/src/cmd.c index b2debe337..8c830b9c3 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -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 */ /* diff --git a/src/drawing.c b/src/drawing.c index 68fdebdad..5a73d4c3f 100644 --- a/src/drawing.c +++ b/src/drawing.c @@ -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, }; diff --git a/src/options.c b/src/options.c index e337b4c36..21dc510a9 100644 --- a/src/options.c +++ b/src/options.c @@ -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 } }; diff --git a/src/rip.c b/src/rip.c index 6e48dde45..f6fba01bc 100644 --- a/src/rip.c +++ b/src/rip.c @@ -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) diff --git a/src/windows.c b/src/windows.c index 3a9471224..9e7ffe5b3 100644 --- a/src/windows.c +++ b/src/windows.c @@ -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 diff --git a/sys/share/pcmain.c b/sys/share/pcmain.c index 393dd0d8d..93674ab34 100644 --- a/sys/share/pcmain.c +++ b/sys/share/pcmain.c @@ -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); diff --git a/sys/unix/Makefile.src b/sys/unix/Makefile.src index 4fac84eec..bfe32f285 100644 --- a/sys/unix/Makefile.src +++ b/sys/unix/Makefile.src @@ -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 diff --git a/sys/unix/hints/linux b/sys/unix/hints/linux index 4dc6b2f53..cb0317946 100644 --- a/sys/unix/hints/linux +++ b/sys/unix/hints/linux @@ -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 diff --git a/sys/unix/hints/macosx10.10 b/sys/unix/hints/macosx10.10 index f320e25fb..a08b27acc 100644 --- a/sys/unix/hints/macosx10.10 +++ b/sys/unix/hints/macosx10.10 @@ -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) diff --git a/sys/winnt/Makefile.msc b/sys/winnt/Makefile.msc index f86a67acc..7f908c637 100644 --- a/sys/winnt/Makefile.msc +++ b/sys/winnt/Makefile.msc @@ -61,6 +61,19 @@ DEBUGINFO = Y #TARGET_CPU=x64 #TARGET_CPU=x86 +#--------------------------------------------------------------- +# OPTIONAL - Curses window port support +# +# 4. Uncomment these and set them appropriate if you want to +# include curses port support alongside TTY support in your +# console binary. You'll have to set CURSESINCL to the location +# of your curses header (.h) files and CURSESDLL to the location +# of your pdcurses.dll. +# +ADD_CURSES=Y +CURSESINCL=..\..\pdcurses +CURSESLIB=..\..\pdcurses\wincon\pdcurses.lib +# #============================================================================== # This marks the end of the BUILD DECISIONS section. #============================================================================== @@ -85,16 +98,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) +CURSES = ..\win\curses # window port files (curses) +WSHR = ..\win\share # Tile support files # # Object directory. @@ -203,6 +217,15 @@ VSVER=2999 #untested future version #---------------------------------------------------------------- +!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 + #These will be in the environment variables with one of the VS2017 #developer command prompts. #VSCMD_ARG_HOST_ARCH=x64 @@ -234,7 +257,8 @@ CL_RECENT=-sdl 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 \ + -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 @@ -300,7 +324,12 @@ 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 @@ -313,7 +342,7 @@ lflagsBuild = $(lflags) $(conlibs) $(MACHINE) # - Game build #========================================== -LIBS= user32.lib winmm.lib $(ZLIB) +LIBS= user32.lib winmm.lib $(ZLIB) $(CURSESLIB) ! IF ("$(USE_DLB)"=="Y") DLB = nhdat @@ -406,6 +435,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 +450,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 \ @@ -524,6 +561,19 @@ DATABASE = $(DAT)\data.base {$(UTIL)}.c{$(OBJ)}.o: @$(cc) $(cflagsBuild) -Fo$@ $< +#========================================== +# Rules for files in win\curses +#========================================== + +{$(CURSES)}.c{$(OBJ)}.o: + @$(cc) $(cflagsBuild) -Fo$@ $< + +{$(CURSES)}.h{$(INCL)}.h: + @copy $< $@ + +#{$(CURSES)}.txt{$(DAT)}.txt: +# @copy $< $@ + #========================================== # Rules for files in win\share #========================================== @@ -592,7 +642,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 > $@ @@ -987,6 +1037,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) diff --git a/util/makedefs.c b/util/makedefs.c index c41471b2c..39816cb12 100644 --- a/util/makedefs.c +++ b/util/makedefs.c @@ -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 diff --git a/win/tty/termcap.c b/win/tty/termcap.c index 5f344f04b..c373df04c 100644 --- a/win/tty/termcap.c +++ b/win/tty/termcap.c @@ -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; From 748280d5dc9575f26caccf03525d637acf93c588 Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 16 Nov 2018 20:53:38 -0500 Subject: [PATCH 02/15] curses new file additions --- win/curses/Bugs.txt | 13 + win/curses/Readme.txt | 116 +++ win/curses/Todo.txt | 146 ++++ win/curses/cursdial.c | 1387 ++++++++++++++++++++++++++++++++++++ win/curses/cursdial.h | 23 + win/curses/cursinit.c | 923 ++++++++++++++++++++++++ win/curses/cursinit.h | 17 + win/curses/cursinvt.c | 111 +++ win/curses/cursinvt.h | 11 + win/curses/cursmain.c | 820 +++++++++++++++++++++ win/curses/cursmesg.c | 630 ++++++++++++++++ win/curses/cursmesg.h | 19 + win/curses/cursmisc.c | 880 +++++++++++++++++++++++ win/curses/cursmisc.h | 30 + win/curses/cursstat.c | 1583 +++++++++++++++++++++++++++++++++++++++++ win/curses/cursstat.h | 21 + win/curses/curswins.c | 752 ++++++++++++++++++++ win/curses/curswins.h | 33 + 18 files changed, 7515 insertions(+) create mode 100644 win/curses/Bugs.txt create mode 100644 win/curses/Readme.txt create mode 100644 win/curses/Todo.txt create mode 100644 win/curses/cursdial.c create mode 100644 win/curses/cursdial.h create mode 100644 win/curses/cursinit.c create mode 100644 win/curses/cursinit.h create mode 100644 win/curses/cursinvt.c create mode 100644 win/curses/cursinvt.h create mode 100644 win/curses/cursmain.c create mode 100644 win/curses/cursmesg.c create mode 100644 win/curses/cursmesg.h create mode 100644 win/curses/cursmisc.c create mode 100644 win/curses/cursmisc.h create mode 100644 win/curses/cursstat.c create mode 100644 win/curses/cursstat.h create mode 100644 win/curses/curswins.c create mode 100644 win/curses/curswins.h diff --git a/win/curses/Bugs.txt b/win/curses/Bugs.txt new file mode 100644 index 000000000..86b731b43 --- /dev/null +++ b/win/curses/Bugs.txt @@ -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. diff --git a/win/curses/Readme.txt b/win/curses/Readme.txt new file mode 100644 index 000000000..f9c95fdef --- /dev/null +++ b/win/curses/Readme.txt @@ -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 + + diff --git a/win/curses/Todo.txt b/win/curses/Todo.txt new file mode 100644 index 000000000..b92327790 --- /dev/null +++ b/win/curses/Todo.txt @@ -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. diff --git a/win/curses/cursdial.c b/win/curses/cursdial.c new file mode 100644 index 000000000..274710db1 --- /dev/null +++ b/win/curses/cursdial.c @@ -0,0 +1,1387 @@ +/* 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 "cursdial.h" +#include "func_tab.h" +#include + +#if defined(FILENAME_CMP) +#define strcasecmp FILENAME_CMP +#endif +#if defined(STRNCMPI) +#define strncasecmp strncmpi +#endif + +/* Dialog windows for curses interface */ + + +/* Private declarations */ + +typedef struct nhmi { + winid wid; /* NetHack window id */ + int glyph; /* Menu glyphs */ + anything identifier; /* Value returned if item selected */ + CHAR_P accelerator; /* Character used to select item from menu */ + CHAR_P group_accel; /* Group accelerator for menu item, if any */ + int attr; /* Text attributes for item */ + const char *str; /* Text of menu item */ + BOOLEAN_P presel; /* Whether menu item should be preselected */ + boolean selected; /* Whether item is currently selected */ + int page_num; /* Display page number for entry */ + int line_num; /* Line number on page where entry begins */ + int num_lines; /* Number of lines entry uses on page */ + int count; /* Count for selected item */ + struct nhmi *prev_item; /* Pointer to previous entry */ + struct nhmi *next_item; /* Pointer to next entry */ +} nhmenu_item; + +typedef struct nhm { + winid wid; /* NetHack window id */ + const char *prompt; /* Menu prompt text */ + nhmenu_item *entries; /* Menu entries */ + int num_entries; /* Number of menu entries */ + int num_pages; /* Number of display pages for entry */ + int height; /* Window height of menu */ + int width; /* Window width of menu */ + boolean reuse_accels; /* Non-unique accelerators per page */ + struct nhm *prev_menu; /* Pointer to previous entry */ + struct nhm *next_menu; /* Pointer to next entry */ +} nhmenu; + +typedef enum menu_op_type { + SELECT, + DESELECT, + INVERT +} menu_op; + +static nhmenu *get_menu(winid wid); +static char menu_get_accel(boolean first); +static void menu_determine_pages(nhmenu *menu); +static boolean menu_is_multipage(nhmenu *menu, int width, int height); +static void menu_win_size(nhmenu *menu); +static void menu_display_page(nhmenu *menu, WINDOW * win, int page_num); +static int menu_get_selections(WINDOW * win, nhmenu *menu, int how); +static void menu_select_deselect(WINDOW * win, nhmenu_item *item, + menu_op operation); +static int menu_operation(WINDOW * win, nhmenu *menu, menu_op operation, + int page_num); +static void menu_clear_selections(nhmenu *menu); +static int menu_max_height(void); + +static nhmenu *nhmenus = NULL; /* NetHack menu array */ + + +/* Get a line of text from the player, such as asking for a character name or a wish */ + +void +curses_line_input_dialog(const char *prompt, char *answer, int buffer) +{ + int map_height, map_width, maxwidth, remaining_buf, winx, winy, count; + WINDOW *askwin, *bwin; +#if __STDC_VERSION__ >= 199901L + char input[buffer]; +#else +#ifndef BUFSZ +#define BUFSZ 256 +#endif + char input[BUFSZ]; + + buffer = BUFSZ - 1; +#endif + char *tmpstr; + int prompt_width = strlen(prompt) + buffer + 1; + int prompt_height = 1; + int height = prompt_height; + + maxwidth = term_cols - 2; + + if (iflags.window_inited) { + if (!iflags.wc_popup_dialog) { + curses_message_win_getline(prompt, answer, buffer); + return; + } + curses_get_window_size(MAP_WIN, &map_height, &map_width); + if ((prompt_width + 2) > map_width) + maxwidth = map_width - 2; + } + + if (prompt_width > maxwidth) { + prompt_height = curses_num_lines(prompt, maxwidth); + height = prompt_height; + prompt_width = maxwidth; + tmpstr = curses_break_str(prompt, maxwidth, prompt_height); + remaining_buf = buffer - (strlen(tmpstr) - 1); + if (remaining_buf > 0) { + height += (remaining_buf / prompt_width); + if ((remaining_buf % prompt_width) > 0) { + height++; + } + } + } + + if (iflags.window_inited) { + bwin = curses_create_window(prompt_width, height, UP); + wrefresh(bwin); + getbegyx(bwin, winy, winx); + askwin = newwin(height, prompt_width, winy + 1, winx + 1); + } else { + bwin = curses_create_window(prompt_width, height, CENTER); + wrefresh(bwin); + getbegyx(bwin, winy, winx); + askwin = newwin(height, prompt_width, winy + 1, winx + 1); + } + for (count = 0; count < prompt_height; count++) { + tmpstr = curses_break_str(prompt, maxwidth, count + 1); + if (count == (prompt_height - 1)) { /* Last line */ + mvwprintw(askwin, count, 0, "%s ", tmpstr); + } else { + mvwaddstr(askwin, count, 0, tmpstr); + } + free(tmpstr); + } + + echo(); + curs_set(1); + wgetnstr(askwin, input, buffer - 1); + curs_set(0); + strcpy(answer, input); + werase(bwin); + delwin(bwin); + curses_destroy_win(askwin); + noecho(); +} + + +/* Get a single character response from the player, such as a y/n prompt */ + +int +curses_character_input_dialog(const char *prompt, const char *choices, + CHAR_P def) +{ + WINDOW *askwin = NULL; + int answer, count, maxwidth, map_height, map_width; + char *linestr; + char askstr[BUFSZ + QBUFSZ]; + char choicestr[QBUFSZ]; + int prompt_width = strlen(prompt); + int prompt_height = 1; + boolean any_choice = FALSE; + boolean accept_count = FALSE; + + if (invent || (moves > 1)) { + curses_get_window_size(MAP_WIN, &map_height, &map_width); + } else { + map_height = term_rows; + map_width = term_cols; + } + + maxwidth = map_width - 2; + + if (choices != NULL) { + for (count = 0; choices[count] != '\0'; count++) { + if (choices[count] == '#') { /* Accept a count */ + accept_count = TRUE; + } + } + choicestr[0] = ' '; + choicestr[1] = '['; + for (count = 0; choices[count] != '\0'; count++) { + if (choices[count] == '\033') { /* Escape */ + break; + } + choicestr[count + 2] = choices[count]; + } + choicestr[count + 2] = ']'; + if (((def >= 'A') && (def <= 'Z')) || ((def >= 'a') && (def <= 'z'))) { + choicestr[count + 3] = ' '; + choicestr[count + 4] = '('; + choicestr[count + 5] = def; + choicestr[count + 6] = ')'; + choicestr[count + 7] = '\0'; + } else { /* No usable default choice */ + + choicestr[count + 3] = '\0'; + def = '\0'; /* Mark as no default */ + } + strcpy(askstr, prompt); + strcat(askstr, choicestr); + } else { + strcpy(askstr, prompt); + any_choice = TRUE; + } + + prompt_width = strlen(askstr); + + if ((prompt_width + 2) > maxwidth) { + prompt_height = curses_num_lines(askstr, maxwidth); + prompt_width = map_width - 2; + } + + if (iflags.wc_popup_dialog /*|| curses_stupid_hack*/) { + askwin = curses_create_window(prompt_width, prompt_height, UP); + for (count = 0; count < prompt_height; count++) { + linestr = curses_break_str(askstr, maxwidth, count + 1); + mvwaddstr(askwin, count + 1, 1, linestr); + free(linestr); + } + + wrefresh(askwin); + } else { + linestr = curses_copy_of(askstr); + pline("%s", linestr); + free(linestr); + curs_set(1); + } + + /*curses_stupid_hack = 0; */ + + while (1) { + answer = getch(); + + if (answer == ERR) { + answer = def; + break; + } + + answer = curses_convert_keys(answer); + + if (answer == KEY_ESC) { + if (choices == NULL) { + break; + } + answer = def; + for (count = 0; choices[count] != '\0'; count++) { + if (choices[count] == 'q') { /* q is preferred over n */ + answer = 'q'; + } else if ((choices[count] == 'n') && answer != 'q') { + answer = 'n'; + } + } + break; + } else if ((answer == '\n') || (answer == '\r') || (answer == ' ')) { + if ((choices != NULL) && (def != '\0')) { + answer = def; + } + break; + } + + if (digit(answer)) { + if (accept_count) { + if (answer != '0') { + yn_number = curses_get_count(answer - '0'); + touchwin(askwin); + refresh(); + } + + answer = '#'; + break; + } + } + + if (any_choice) { + break; + } + + if (choices != NULL) { + for (count = 0; (size_t) count < strlen(choices); count++) { + if (choices[count] == answer) { + break; + } + } + if (choices[count] == answer) { + break; + } + } + } + + if (iflags.wc_popup_dialog) { + /* Kludge to make prompt visible after window is dismissed + when inputting a number */ + if (digit(answer)) { + linestr = curses_copy_of(askstr); + pline("%s", linestr); + free(linestr); + curs_set(1); + } + + curses_destroy_win(askwin); + } else { + curses_clear_unhighlight_message_window(); + curs_set(0); + } + + return answer; +} + + +/* Return an extended command from the user */ + +int +curses_ext_cmd() +{ + int count, letter, prompt_width, startx, starty, winx, winy; + int messageh, messagew, maxlen = BUFSZ - 1; + int ret = -1; + char cur_choice[BUFSZ]; + int matches = 0; + WINDOW *extwin = NULL, *extwin2 = NULL; + + if (iflags.extmenu) { + return extcmd_via_menu(); + } + + startx = 0; + starty = 0; + if (iflags.wc_popup_dialog) { /* Prompt in popup window */ + int x0, y0, w, h; /* bounding coords of popup */ + extwin2 = curses_create_window(25, 1, UP); + wrefresh(extwin2); + /* create window inside window to prevent overwriting of border */ + getbegyx(extwin2,y0,x0); + getmaxyx(extwin2,h,w); + extwin = newwin(1, w-2, y0+1, x0+1); + if (w - 4 < maxlen) maxlen = w - 4; + } else { + 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; + extwin = newwin(1, messagew-2, winy, winx); + if (messagew - 4 < maxlen) maxlen = messagew - 4; + pline("#"); + } + + cur_choice[0] = '\0'; + + while (1) { + wmove(extwin, starty, startx); + waddstr(extwin, "# "); + wmove(extwin, starty, startx + 2); + waddstr(extwin, cur_choice); + wmove(extwin, starty, strlen(cur_choice) + startx + 2); + wprintw(extwin, " "); + + /* if we have an autocomplete command, AND it matches uniquely */ + if (matches == 1) { + curses_toggle_color_attr(extwin, NONE, A_UNDERLINE, ON); + wmove(extwin, starty, strlen(cur_choice) + startx + 2); + wprintw(extwin, "%s", extcmdlist[ret].ef_txt + strlen(cur_choice)); + curses_toggle_color_attr(extwin, NONE, A_UNDERLINE, OFF); + mvwprintw(extwin, starty, + strlen(extcmdlist[ret].ef_txt) + 2, " "); + } + + wrefresh(extwin); + letter = getch(); + prompt_width = strlen(cur_choice); + matches = 0; + + if (letter == '\033' || letter == ERR) { + ret = -1; + break; + } + + if ((letter == '\r') || (letter == '\n')) { + if (ret == -1) { + for (count = 0; extcmdlist[count].ef_txt; count++) { + if (!strcasecmp(cur_choice, extcmdlist[count].ef_txt)) { + ret = count; + break; + } + } + } + break; + } + + if ((letter == '\b') || (letter == KEY_BACKSPACE)) { + if (prompt_width == 0) { + ret = -1; + break; + } else { + cur_choice[prompt_width - 1] = '\0'; + letter = '*'; + prompt_width--; + } + } + if (letter != '*' && prompt_width < maxlen) { + cur_choice[prompt_width] = letter; + cur_choice[prompt_width + 1] = '\0'; + ret = -1; + } + for (count = 0; extcmdlist[count].ef_txt; count++) { + if (!(extcmdlist[count].flags & AUTOCOMPLETE)) + continue; + if (strlen(extcmdlist[count].ef_txt) > (size_t) prompt_width) { + if (strncasecmp(cur_choice, extcmdlist[count].ef_txt, + prompt_width) == 0) { + if ((extcmdlist[count].ef_txt[prompt_width] == + lowc(letter)) || letter == '*') { + if (matches == 0) { + ret = count; + } + + matches++; + } + } + } + } + } + + curses_destroy_win(extwin); + if (extwin2) curses_destroy_win(extwin2); + return ret; +} + + +/* Initialize a menu from given NetHack winid */ + +void +curses_create_nhmenu(winid wid) +{ + nhmenu *new_menu = NULL; + nhmenu *menuptr = nhmenus; + nhmenu_item *menu_item_ptr = NULL; + nhmenu_item *tmp_menu_item = NULL; + + new_menu = get_menu(wid); + + if (new_menu != NULL) { + /* Reuse existing menu, clearing out current entries */ + menu_item_ptr = new_menu->entries; + + if (menu_item_ptr != NULL) { + while (menu_item_ptr->next_item != NULL) { + tmp_menu_item = menu_item_ptr->next_item; + free(menu_item_ptr); + menu_item_ptr = tmp_menu_item; + } + free(menu_item_ptr); /* Last entry */ + new_menu->entries = NULL; + } + if (new_menu->prompt != NULL) { /* Reusing existing menu */ + free((char *) new_menu->prompt); + } + return; + } + + new_menu = malloc(sizeof (nhmenu)); + new_menu->wid = wid; + new_menu->prompt = NULL; + new_menu->entries = NULL; + new_menu->num_pages = 0; + new_menu->height = 0; + new_menu->width = 0; + new_menu->reuse_accels = FALSE; + new_menu->next_menu = NULL; + + if (nhmenus == NULL) { /* no menus in memory yet */ + new_menu->prev_menu = NULL; + nhmenus = new_menu; + } else { + while (menuptr->next_menu != NULL) { + menuptr = menuptr->next_menu; + } + new_menu->prev_menu = menuptr; + menuptr->next_menu = new_menu; + } +} + + +/* Add a menu item to the given menu window */ + +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) +{ + char *new_str; + nhmenu_item *new_item, *current_items, *menu_item_ptr; + nhmenu *current_menu = get_menu(wid); + + if (str == NULL) { + return; + } + + new_str = curses_copy_of(str); + curses_rtrim((char *) new_str); + new_item = malloc(sizeof (nhmenu_item)); + new_item->wid = wid; + new_item->glyph = glyph; + new_item->identifier = *identifier; + new_item->accelerator = accelerator; + new_item->group_accel = group_accel; + new_item->attr = attr; + new_item->str = new_str; + new_item->presel = presel; + new_item->selected = FALSE; + new_item->page_num = 0; + new_item->line_num = 0; + new_item->num_lines = 0; + new_item->count = -1; + new_item->next_item = NULL; + + if (current_menu == NULL) { + panic + ("curses_add_nhmenu_item: attempt to add item to nonexistant menu"); + } + + current_items = current_menu->entries; + menu_item_ptr = current_items; + + if (current_items == NULL) { + new_item->prev_item = NULL; + current_menu->entries = new_item; + } else { + while (menu_item_ptr->next_item != NULL) { + menu_item_ptr = menu_item_ptr->next_item; + } + new_item->prev_item = menu_item_ptr; + menu_item_ptr->next_item = new_item; + } +} + + +/* No more entries are to be added to menu, so details of the menu can be + finalized in memory */ + +void +curses_finalize_nhmenu(winid wid, const char *prompt) +{ + int count = 0; + nhmenu *current_menu = get_menu(wid); + nhmenu_item *menu_item_ptr = current_menu->entries; + + if (current_menu == NULL) { + panic("curses_finalize_nhmenu: attempt to finalize nonexistant menu"); + } + + while (menu_item_ptr != NULL) { + menu_item_ptr = menu_item_ptr->next_item; + count++; + } + + current_menu->num_entries = count; + + current_menu->prompt = curses_copy_of(prompt); +} + + +/* Display a nethack menu, and return a selection, if applicable */ + +int +curses_display_nhmenu(winid wid, int how, MENU_ITEM_P ** _selected) +{ + nhmenu *current_menu = get_menu(wid); + nhmenu_item *menu_item_ptr; + int num_chosen, count; + WINDOW *win; + MENU_ITEM_P *selected = NULL; + + *_selected = NULL; + + if (current_menu == NULL) { + panic("curses_display_nhmenu: attempt to display nonexistant menu"); + } + + menu_item_ptr = current_menu->entries; + + if (menu_item_ptr == NULL) { + panic("curses_display_nhmenu: attempt to display empty menu"); + } + + /* Reset items to unselected to clear out selections from previous + invocations of this menu, and preselect appropriate items */ + while (menu_item_ptr != NULL) { + menu_item_ptr->selected = menu_item_ptr->presel; + menu_item_ptr = menu_item_ptr->next_item; + } + + menu_win_size(current_menu); + menu_determine_pages(current_menu); + + /* Display pre and post-game menus centered */ + if (((moves <= 1) && !invent) || program_state.gameover) { + win = curses_create_window(current_menu->width, + current_menu->height, CENTER); + } else { /* Display during-game menus on the right out of the way */ + + win = curses_create_window(current_menu->width, + current_menu->height, RIGHT); + } + + num_chosen = menu_get_selections(win, current_menu, how); + curses_destroy_win(win); + + if (num_chosen > 0) { + selected = (MENU_ITEM_P *) malloc(num_chosen * sizeof (MENU_ITEM_P)); + count = 0; + + menu_item_ptr = current_menu->entries; + + while (menu_item_ptr != NULL) { + if (menu_item_ptr->selected) { + if (count == num_chosen) { + panic("curses_display_nhmenu: Selected items " + "exceeds expected number"); + } + selected[count].item = menu_item_ptr->identifier; + selected[count].count = menu_item_ptr->count; + count++; + } + menu_item_ptr = menu_item_ptr->next_item; + } + + if (count != num_chosen) { + panic("curses_display_nhmenu: Selected items less than " + "expected number"); + } + } + + *_selected = selected; + + return num_chosen; +} + + +boolean +curses_menu_exists(winid wid) +{ + if (get_menu(wid) != NULL) { + return TRUE; + } else { + return FALSE; + } +} + +/* Delete the menu associated with the given NetHack winid from memory */ + +void +curses_del_menu(winid wid) +{ + nhmenu_item *tmp_menu_item; + nhmenu_item *menu_item_ptr; + nhmenu *tmpmenu; + nhmenu *current_menu = get_menu(wid); + + if (current_menu == NULL) { + return; + } + + menu_item_ptr = current_menu->entries; + + /* First free entries associated with this menu from memory */ + if (menu_item_ptr != NULL) { + while (menu_item_ptr->next_item != NULL) { + tmp_menu_item = menu_item_ptr->next_item; + free(menu_item_ptr); + menu_item_ptr = tmp_menu_item; + } + free(menu_item_ptr); /* Last entry */ + current_menu->entries = NULL; + } + + /* Now unlink the menu from the list and free it as well */ + if (current_menu->prev_menu != NULL) { + tmpmenu = current_menu->prev_menu; + tmpmenu->next_menu = current_menu->next_menu; + } else { + nhmenus = current_menu->next_menu; /* New head mode or NULL */ + } + if (current_menu->next_menu != NULL) { + tmpmenu = current_menu->next_menu; + tmpmenu->prev_menu = current_menu->prev_menu; + } + + free(current_menu); + + curses_del_wid(wid); +} + + +/* return a pointer to the menu associated with the given NetHack winid */ + +static nhmenu * +get_menu(winid wid) +{ + nhmenu *menuptr = nhmenus; + + while (menuptr != NULL) { + if (menuptr->wid == wid) { + return menuptr; + } + menuptr = menuptr->next_menu; + } + + return NULL; /* Not found */ +} + + +static char +menu_get_accel(boolean first) +{ + char ret; + static char next_letter = 'a'; + + if (first) { + next_letter = 'a'; + } + + ret = next_letter; + + if (((next_letter < 'z') && (next_letter >= 'a')) || ((next_letter < 'Z') + && (next_letter >= + 'A')) || + ((next_letter < '9') && (next_letter >= '0'))) { + next_letter++; + } else if (next_letter == 'z') { + next_letter = 'A'; + } else if (next_letter == 'Z') { + next_letter = '0'; + } + + return ret; +} + + +/* Determine if menu will require multiple pages to display */ + +static boolean +menu_is_multipage(nhmenu *menu, int width, int height) +{ + int num_lines; + int curline = 0; + nhmenu_item *menu_item_ptr = menu->entries; + + if (strlen(menu->prompt) > 0) { + curline += curses_num_lines(menu->prompt, width) + 1; + } + + if (menu->num_entries <= (height - curline)) { + while (menu_item_ptr != NULL) { + menu_item_ptr->line_num = curline; + if (menu_item_ptr->identifier.a_void == NULL) { + num_lines = curses_num_lines(menu_item_ptr->str, width); + } else { + /* Add space for accelerator */ + num_lines = curses_num_lines(menu_item_ptr->str, width - 4); + } + menu_item_ptr->num_lines = num_lines; + curline += num_lines; + menu_item_ptr = menu_item_ptr->next_item; + if ((curline > height) || ((curline > height - 2) && + (height == menu_max_height()))) { + break; + } + } + if (menu_item_ptr == NULL) { + return FALSE; + } + } + return TRUE; +} + + +/* Determine which entries go on which page, and total number of pages */ + +static void +menu_determine_pages(nhmenu *menu) +{ + int tmpline, num_lines; + int curline = 0; + int page_num = 1; + nhmenu_item *menu_item_ptr = menu->entries; + int width = menu->width; + int height = menu->height; + int page_end = height; + + + if (strlen(menu->prompt) > 0) { + curline += curses_num_lines(menu->prompt, width) + 1; + } + + tmpline = curline; + + if (menu_is_multipage(menu, width, height)) { + page_end -= 2; /* Room to display current page number */ + } + + /* Determine what entries belong on which page */ + menu_item_ptr = menu->entries; + + while (menu_item_ptr != NULL) { + menu_item_ptr->page_num = page_num; + menu_item_ptr->line_num = curline; + if (menu_item_ptr->identifier.a_void == NULL) { + num_lines = curses_num_lines(menu_item_ptr->str, width); + } else { + /* Add space for accelerator */ + num_lines = curses_num_lines(menu_item_ptr->str, width - 4); + } + menu_item_ptr->num_lines = num_lines; + curline += num_lines; + if (curline > page_end) { + page_num++; + curline = tmpline; + /* Move ptr back so entry will be reprocessed on new page */ + menu_item_ptr = menu_item_ptr->prev_item; + } + menu_item_ptr = menu_item_ptr->next_item; + } + + menu->num_pages = page_num; +} + + +/* Determine dimensions of menu window based on term size and entries */ + +static void +menu_win_size(nhmenu *menu) +{ + int width, height, maxwidth, maxheight, curentrywidth, lastline; + int maxentrywidth = strlen(menu->prompt); + int maxheaderwidth = 0; + nhmenu_item *menu_item_ptr = menu->entries; + + maxwidth = 38; /* Reasonable minimum usable width */ + + if ((term_cols / 2) > maxwidth) { + maxwidth = (term_cols / 2); /* Half the screen */ + } + + maxheight = menu_max_height(); + + /* First, determine the width of the longest menu entry */ + while (menu_item_ptr != NULL) + { + if (menu_item_ptr->identifier.a_void == NULL) { + curentrywidth = strlen(menu_item_ptr->str); + + if (curentrywidth > maxheaderwidth) { + maxheaderwidth = curentrywidth; + } + } else { + /* Add space for accelerator */ + curentrywidth = strlen(menu_item_ptr->str) + 4; +#if 0 // FIXME: menu glyphs + if (menu_item_ptr->glyph != NO_GLYPH + && iflags.use_menu_glyphs) + curentrywidth += 2; +#endif + } + if (curentrywidth > maxentrywidth) { + maxentrywidth = curentrywidth; + } + menu_item_ptr = menu_item_ptr->next_item; + } + + /* If the widest entry is smaller than maxwidth, reduce maxwidth accordingly */ + if (maxentrywidth < maxwidth) { + maxwidth = maxentrywidth; + } + + /* Try not to wrap headers/normal text lines if possible. We can + go wider than half the screen for this purpose if need be */ + + if ((maxheaderwidth > maxwidth) && (maxheaderwidth < (term_cols - 2))) { + maxwidth = maxheaderwidth; + } + + width = maxwidth; + + /* Possibly reduce height if only 1 page */ + if (!menu_is_multipage(menu, maxwidth, maxheight)) { + menu_item_ptr = menu->entries; + + while (menu_item_ptr->next_item != NULL) { + menu_item_ptr = menu_item_ptr->next_item; + } + + lastline = (menu_item_ptr->line_num) + menu_item_ptr->num_lines; + + if (lastline < maxheight) { + maxheight = lastline; + } + } else { /* If multipage, make sure we have enough width for page footer */ + + if (width < 20) { + width = 20; + } + } + + height = maxheight; + menu->width = width; + menu->height = height; +} + + +/* Displays menu selections in the given window */ + +static void +menu_display_page(nhmenu *menu, WINDOW * win, int page_num) +{ + nhmenu_item *menu_item_ptr; + int count, curletter, entry_cols, start_col, num_lines, footer_x; + boolean first_accel = TRUE; + + int color = NO_COLOR; + int attr = A_NORMAL; + boolean menu_color = FALSE; + + /* Cycle through entries until we are on the correct page */ + + menu_item_ptr = menu->entries; + + while (menu_item_ptr != NULL) { + if (menu_item_ptr->page_num == page_num) { + break; + } + menu_item_ptr = menu_item_ptr->next_item; + } + + if (menu_item_ptr == NULL) { /* Page not found */ + panic("menu_display_page: attempt to display nonexistant page"); + } + + werase(win); + + if (strlen(menu->prompt) > 0) { + num_lines = curses_num_lines(menu->prompt, menu->width); + + for (count = 0; count < num_lines; count++) { + mvwprintw(win, count + 1, 1, "%s", + curses_break_str(menu->prompt, menu->width, count + 1)); + } + } + + /* Display items for current page */ + + while (menu_item_ptr != NULL) { + if (menu_item_ptr->page_num != page_num) { + break; + } + if (menu_item_ptr->identifier.a_void != NULL) { + if (menu_item_ptr->accelerator != 0) { + curletter = menu_item_ptr->accelerator; + } else { + if (first_accel) { + curletter = menu_get_accel(TRUE); + first_accel = FALSE; + if (!menu->reuse_accels && (menu->num_pages > 1)) { + menu->reuse_accels = TRUE; + } + } else { + curletter = menu_get_accel(FALSE); + } + menu_item_ptr->accelerator = curletter; + } + + if (menu_item_ptr->selected) { + curses_toggle_color_attr(win, HIGHLIGHT_COLOR, A_REVERSE, ON); + mvwaddch(win, menu_item_ptr->line_num + 1, 1, '<'); + mvwaddch(win, menu_item_ptr->line_num + 1, 2, curletter); + mvwaddch(win, menu_item_ptr->line_num + 1, 3, '>'); + curses_toggle_color_attr(win, HIGHLIGHT_COLOR, A_REVERSE, OFF); + } else { + curses_toggle_color_attr(win, HIGHLIGHT_COLOR, NONE, ON); + mvwaddch(win, menu_item_ptr->line_num + 1, 2, curletter); + curses_toggle_color_attr(win, HIGHLIGHT_COLOR, NONE, OFF); + mvwprintw(win, menu_item_ptr->line_num + 1, 3, ") "); + } + } + entry_cols = menu->width; + start_col = 1; + + if (menu_item_ptr->identifier.a_void != NULL) { + entry_cols -= 4; + start_col += 4; + } +#if 0 + //FIXME: menuglyphs not implemented yet + if (menu_item_ptr->glyph != NO_GLYPH && iflags.use_menu_glyphs) { + unsigned special; /*notused */ + + mapglyph(menu_item_ptr->glyph, &curletter, &color, &special, 0, 0); + curses_toggle_color_attr(win, color, NONE, ON); + mvwaddch(win, menu_item_ptr->line_num + 1, start_col, curletter); + curses_toggle_color_attr(win, color, NONE, OFF); + mvwaddch(win, menu_item_ptr->line_num + 1, start_col + 1, ' '); + entry_cols -= 2; + start_col += 2; + } +#endif + if (iflags.use_menu_color && (menu_color = get_menu_coloring + ((char *) menu_item_ptr->str, &color, + &attr))) { + if (color != NO_COLOR) { + curses_toggle_color_attr(win, color, NONE, ON); + } + attr = curses_convert_attr(attr); + if (attr != A_NORMAL) { + menu_item_ptr->attr = menu_item_ptr->attr | attr; + } + } + curses_toggle_color_attr(win, NONE, menu_item_ptr->attr, ON); + + num_lines = curses_num_lines(menu_item_ptr->str, entry_cols); + + for (count = 0; count < num_lines; count++) { + if (strlen(menu_item_ptr->str) > 0) { + mvwprintw(win, menu_item_ptr->line_num + count + 1, + start_col, "%s", curses_break_str(menu_item_ptr->str, + entry_cols, + count + 1)); + } + } + if (menu_color && (color != NO_COLOR)) { + curses_toggle_color_attr(win, color, NONE, OFF); + } + curses_toggle_color_attr(win, NONE, menu_item_ptr->attr, OFF); + menu_item_ptr = menu_item_ptr->next_item; + } + + if (menu->num_pages > 1) { + footer_x = menu->width - strlen("<- (Page X of Y) ->"); + if (menu->num_pages > 9) { /* Unlikely */ + footer_x -= 2; + } + mvwprintw(win, menu->height, footer_x + 3, "(Page %d of %d)", + page_num, menu->num_pages); + if (page_num != 1) { + curses_toggle_color_attr(win, HIGHLIGHT_COLOR, NONE, ON); + mvwaddstr(win, menu->height, footer_x, "<="); + curses_toggle_color_attr(win, HIGHLIGHT_COLOR, NONE, OFF); + } + if (page_num != menu->num_pages) { + curses_toggle_color_attr(win, HIGHLIGHT_COLOR, NONE, ON); + mvwaddstr(win, menu->height, menu->width - 2, "=>"); + curses_toggle_color_attr(win, HIGHLIGHT_COLOR, NONE, OFF); + } + } + curses_toggle_color_attr(win, DIALOG_BORDER_COLOR, NONE, ON); + box(win, 0, 0); + curses_toggle_color_attr(win, DIALOG_BORDER_COLOR, NONE, OFF); + wrefresh(win); +} + + +static int +menu_get_selections(WINDOW * win, nhmenu *menu, int how) +{ + int curletter; + int count = -1; + int count_letter = '\0'; + int curpage = 1; + int num_selected = 0; + boolean dismiss = FALSE; + char search_key[BUFSZ]; + nhmenu_item *menu_item_ptr = menu->entries; + + menu_display_page(menu, win, 1); + + while (!dismiss) { + curletter = getch(); + + if (curletter == ERR) { + num_selected = -1; + dismiss = TRUE; + } + + if (curletter == '\033') { + curletter = curses_convert_keys(curletter); + } + + switch (how) { + case PICK_NONE: + if (menu->num_pages == 1) { + if (curletter == KEY_ESC) { + num_selected = -1; + } else { + num_selected = 0; + + } + dismiss = TRUE; + break; + } + break; + case PICK_ANY: + switch (curletter) { + case MENU_SELECT_PAGE: + (void) menu_operation(win, menu, SELECT, curpage); + break; + case MENU_SELECT_ALL: + curpage = menu_operation(win, menu, SELECT, 0); + break; + case MENU_UNSELECT_PAGE: + (void) menu_operation(win, menu, DESELECT, curpage); + break; + case MENU_UNSELECT_ALL: + curpage = menu_operation(win, menu, DESELECT, 0); + break; + case MENU_INVERT_PAGE: + (void) menu_operation(win, menu, INVERT, curpage); + break; + case MENU_INVERT_ALL: + curpage = menu_operation(win, menu, INVERT, 0); + break; + } + default: + if (isdigit(curletter)) { + count = curses_get_count(curletter - '0'); + touchwin(win); + refresh(); + curletter = getch(); + if (count > 0) { + count_letter = curletter; + } + } + } + + switch (curletter) { + case KEY_ESC: + num_selected = -1; + dismiss = TRUE; + break; + case '\n': + case '\r': + dismiss = TRUE; + break; + case KEY_RIGHT: + case KEY_NPAGE: + case MENU_NEXT_PAGE: + case ' ': + if (curpage < menu->num_pages) { + curpage++; + menu_display_page(menu, win, curpage); + } else if (curletter == ' ') { + dismiss = TRUE; + break; + } + break; + case KEY_LEFT: + case KEY_PPAGE: + case MENU_PREVIOUS_PAGE: + if (curpage > 1) { + curpage--; + menu_display_page(menu, win, curpage); + } + break; + case KEY_END: + case MENU_LAST_PAGE: + if (curpage != menu->num_pages) { + curpage = menu->num_pages; + menu_display_page(menu, win, curpage); + } + break; + case KEY_HOME: + case MENU_FIRST_PAGE: + if (curpage != 1) { + curpage = 1; + menu_display_page(menu, win, curpage); + } + break; + case MENU_SEARCH: + curses_line_input_dialog("Search for:", search_key, BUFSZ); + + refresh(); + touchwin(win); + wrefresh(win); + + if (strlen(search_key) == 0) { + break; + } + + menu_item_ptr = menu->entries; + + while (menu_item_ptr != NULL) { + if ((menu_item_ptr->identifier.a_void != NULL) && + (strstri(menu_item_ptr->str, search_key))) { + if (how == PICK_ONE) { + menu_clear_selections(menu); + menu_select_deselect(win, menu_item_ptr, SELECT); + num_selected = 1; + dismiss = TRUE; + break; + } else { + menu_select_deselect(win, menu_item_ptr, INVERT); + } + } + + menu_item_ptr = menu_item_ptr->next_item; + } + + menu_item_ptr = menu->entries; + break; + default: + if (how == PICK_NONE) { + num_selected = 0; + dismiss = TRUE; + break; + } + } + + menu_item_ptr = menu->entries; + while (menu_item_ptr != NULL) { + if (menu_item_ptr->identifier.a_void != NULL) { + if (((curletter == menu_item_ptr->accelerator) && + ((curpage == menu_item_ptr->page_num) || + (!menu->reuse_accels))) || ((menu_item_ptr->group_accel) + && (curletter == + menu_item_ptr-> + group_accel))) { + if (curpage != menu_item_ptr->page_num) { + curpage = menu_item_ptr->page_num; + menu_display_page(menu, win, curpage); + } + + if (how == PICK_ONE) { + menu_clear_selections(menu); + menu_select_deselect(win, menu_item_ptr, SELECT); + num_selected = 1; + dismiss = TRUE; + break; + } else if ((how == PICK_ANY) && (curletter == count_letter)) { + menu_select_deselect(win, menu_item_ptr, SELECT); + menu_item_ptr->count = count; + count = 0; + count_letter = '\0'; + } else { + menu_select_deselect(win, menu_item_ptr, INVERT); + } + } + } + menu_item_ptr = menu_item_ptr->next_item; + } + } + + if ((how == PICK_ANY) && (num_selected != -1)) { + num_selected = 0; + menu_item_ptr = menu->entries; + + while (menu_item_ptr != NULL) { + if (menu_item_ptr->identifier.a_void != NULL) { + if (menu_item_ptr->selected) { + num_selected++; + } + } + menu_item_ptr = menu_item_ptr->next_item; + } + } + + return num_selected; +} + + +/* Select, deselect, or toggle selected for the given menu entry */ + +static void +menu_select_deselect(WINDOW * win, nhmenu_item *item, menu_op operation) +{ + int curletter = item->accelerator; + + if ((operation == DESELECT) || (item->selected && (operation == INVERT))) { + item->selected = FALSE; + mvwaddch(win, item->line_num + 1, 1, ' '); + curses_toggle_color_attr(win, HIGHLIGHT_COLOR, NONE, ON); + mvwaddch(win, item->line_num + 1, 2, curletter); + curses_toggle_color_attr(win, HIGHLIGHT_COLOR, NONE, OFF); + mvwaddch(win, item->line_num + 1, 3, ')'); + } else { + item->selected = TRUE; + curses_toggle_color_attr(win, HIGHLIGHT_COLOR, A_REVERSE, ON); + mvwaddch(win, item->line_num + 1, 1, '<'); + mvwaddch(win, item->line_num + 1, 2, curletter); + mvwaddch(win, item->line_num + 1, 3, '>'); + curses_toggle_color_attr(win, HIGHLIGHT_COLOR, A_REVERSE, OFF); + } + + wrefresh(win); +} + + +/* Perform the selected operation (select, unselect, invert selection) +on the given menu page. If menu_page is 0, then perform opetation on +all pages in menu. Returns last page displayed. */ + +static int +menu_operation(WINDOW * win, nhmenu *menu, menu_op + operation, int page_num) +{ + int first_page, last_page, current_page; + nhmenu_item *menu_item_ptr = menu->entries; + + if (page_num == 0) { /* Operation to occur on all pages */ + first_page = 1; + last_page = menu->num_pages; + } else { + first_page = page_num; + last_page = page_num; + } + + /* Cycle through entries until we are on the correct page */ + + while (menu_item_ptr != NULL) { + if (menu_item_ptr->page_num == first_page) { + break; + } + menu_item_ptr = menu_item_ptr->next_item; + } + + current_page = first_page; + + if (page_num == 0) { + menu_display_page(menu, win, current_page); + } + + if (menu_item_ptr == NULL) { /* Page not found */ + panic("menu_display_page: attempt to display nonexistant page"); + } + + while (menu_item_ptr != NULL) { + if (menu_item_ptr->page_num != current_page) { + if (menu_item_ptr->page_num > last_page) { + break; + } + + current_page = menu_item_ptr->page_num; + menu_display_page(menu, win, current_page); + } + + if (menu_item_ptr->identifier.a_void != NULL) { + menu_select_deselect(win, menu_item_ptr, operation); + } + + menu_item_ptr = menu_item_ptr->next_item; + } + + return current_page; +} + + +/* Set all menu items to unselected in menu */ + +static void +menu_clear_selections(nhmenu *menu) +{ + nhmenu_item *menu_item_ptr = menu->entries; + + while (menu_item_ptr != NULL) { + menu_item_ptr->selected = FALSE; + menu_item_ptr = menu_item_ptr->next_item; + } +} + + +/* Get the maximum height for a menu */ + +static int +menu_max_height(void) +{ + return term_rows - 2; +} diff --git a/win/curses/cursdial.h b/win/curses/cursdial.h new file mode 100644 index 000000000..bf3f68d9e --- /dev/null +++ b/win/curses/cursdial.h @@ -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 */ diff --git a/win/curses/cursinit.c b/win/curses/cursinit.c new file mode 100644 index 000000000..3e87693a7 --- /dev/null +++ b/win/curses/cursinit.c @@ -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 + +/* 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 +} diff --git a/win/curses/cursinit.h b/win/curses/cursinit.h new file mode 100644 index 000000000..c7f390047 --- /dev/null +++ b/win/curses/cursinit.h @@ -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 */ diff --git a/win/curses/cursinvt.c b/win/curses/cursinvt.c new file mode 100644 index 000000000..f5a362d7d --- /dev/null +++ b/win/curses/cursinvt.c @@ -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); +} diff --git a/win/curses/cursinvt.h b/win/curses/cursinvt.h new file mode 100644 index 000000000..99c90d277 --- /dev/null +++ b/win/curses/cursinvt.h @@ -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 */ diff --git a/win/curses/cursmain.c b/win/curses/cursmain.c new file mode 100644 index 000000000..a7addedea --- /dev/null +++ b/win/curses/cursmain.c @@ -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(); + } +} + diff --git a/win/curses/cursmesg.c b/win/curses/cursmesg.c new file mode 100644 index 000000000..9fc1d6442 --- /dev/null +++ b/win/curses/cursmesg.c @@ -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 + + +/* 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 ? "" : ">>"); + 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; + } +} diff --git a/win/curses/cursmesg.h b/win/curses/cursmesg.h new file mode 100644 index 000000000..54af5f9b3 --- /dev/null +++ b/win/curses/cursmesg.h @@ -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 */ diff --git a/win/curses/cursmisc.c b/win/curses/cursmisc.c new file mode 100644 index 000000000..4b424884d --- /dev/null +++ b/win/curses/cursmisc.c @@ -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 + +/* 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 */ +} + diff --git a/win/curses/cursmisc.h b/win/curses/cursmisc.h new file mode 100644 index 000000000..50821c81a --- /dev/null +++ b/win/curses/cursmisc.h @@ -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 */ diff --git a/win/curses/cursstat.c b/win/curses/cursstat.c new file mode 100644 index 000000000..ab3ca0ca5 --- /dev/null +++ b/win/curses/cursstat.c @@ -0,0 +1,1583 @@ +/* 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 /* toupper() */ +#include "curses.h" +#include "hack.h" +#include "wincurs.h" +#include "cursstat.h" + +/* Status window functions for curses interface */ + +/* + * The following data structures come from the genl_ routines in + * src/windows.c and as such are considered to be on the window-port + * "side" of things, rather than the NetHack-core "side" of things. + */ + +extern const char *status_fieldfmt[MAXBLSTATS]; +extern const char *status_fieldnm[MAXBLSTATS]; +extern char *status_vals[MAXBLSTATS]; +extern boolean status_activefields[MAXBLSTATS]; + +/* Long format fields for vertical window */ +static char *status_vals_long[MAXBLSTATS]; + +#ifdef STATUS_HILITES +static long curses_condition_bits; +static int curses_status_colors[MAXBLSTATS]; +int hpbar_percent, hpbar_color; + +static int FDECL(condcolor, (long, unsigned long *)); +static int FDECL(condattr, (long, unsigned long *)); +#endif /* STATUS_HILITES */ +static void FDECL(draw_status, (unsigned long *)); +static void FDECL(draw_classic, (boolean, unsigned long *)); +static void FDECL(draw_vertical, (boolean, unsigned long *)); +static void FDECL(draw_horizontal, (boolean, unsigned long *)); + +void +curses_status_init() +{ +#ifdef STATUS_HILITES + int i; + + for (i = 0; i < MAXBLSTATS; ++i) { + curses_status_colors[i] = NO_COLOR; /* no color */ + status_vals_long[i] = (char *) alloc(MAXCO); + *status_vals_long[i] = '\0'; + } + curses_condition_bits = 0L; + hpbar_percent = 0, hpbar_color = NO_COLOR; +#endif /* STATUS_HILITES */ + + /* let genl_status_init do most of the initialization */ + genl_status_init(); +} + +/* + * *_status_update() + * -- update the value of a status field. + * -- the fldindex identifies which field is changing and + * is an integer index value from botl.h + * -- fldindex could be any one of the following from botl.h: + * BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, + * BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX, + * BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX, + * BL_LEVELDESC, BL_EXP, BL_CONDITION + * -- fldindex could also be BL_FLUSH (-1), which is not really + * a field index, but is a special trigger to tell the + * windowport that it should redisplay all its status fields, + * even if no changes have been presented to it. + * -- ptr is usually a "char *", unless fldindex is BL_CONDITION. + * If fldindex is BL_CONDITION, then ptr is a long value with + * any or none of the following bits set (from botl.h): + * BL_MASK_STONE 0x00000001L + * BL_MASK_SLIME 0x00000002L + * BL_MASK_STRNGL 0x00000004L + * BL_MASK_FOODPOIS 0x00000008L + * BL_MASK_TERMILL 0x00000010L + * BL_MASK_BLIND 0x00000020L + * BL_MASK_DEAF 0x00000040L + * BL_MASK_STUN 0x00000080L + * BL_MASK_CONF 0x00000100L + * BL_MASK_HALLU 0x00000200L + * BL_MASK_LEV 0x00000400L + * BL_MASK_FLY 0x00000800L + * BL_MASK_RIDE 0x00001000L + * -- The value passed for BL_GOLD includes an encoded leading + * symbol for GOLD "\GXXXXNNNN:nnn". If the window port needs to use + * the textual gold amount without the leading "$:" the port will + * have to skip past ':' in the passed "ptr" for the BL_GOLD case. + * -- color is an unsigned int. + * color_index = color & 0x00FF; CLR_* value + * attribute = color & 0xFF00 >> 8; BL_* values + * This holds the color and attribute that the field should + * be displayed in. + * This is relevant for everything except BL_CONDITION fldindex. + * If fldindex is BL_CONDITION, this parameter should be ignored, + * as condition hilighting is done via the next colormasks + * parameter instead. + * + * -- colormasks - pointer to cond_hilites[] array of colormasks. + * Only relevant for BL_CONDITION fldindex. The window port + * should ignore this parameter for other fldindex values. + * Each condition bit must only ever appear in one of the + * CLR_ array members, but can appear in multiple HL_ATTCLR_ + * offsets (because more than one attribute can co-exist). + * See doc/window.doc for more details. + */ + +/* new approach through status_update() only */ +#define Begin_Attr(m) \ + if (m) { \ + if ((m) & HL_BOLD) \ + wattron(win, A_BOLD); \ + if ((m) & HL_INVERSE) \ + wattron(win,A_REVERSE); \ + if ((m) & HL_ULINE) \ + wattron(win,A_UNDERLINE); \ + if ((m) & HL_BLINK) \ + wattron(win,A_BLINK); \ + if ((m) & HL_DIM) \ + wattron(win,A_DIM); \ + } + +#define End_Attr(m) \ + if (m) { \ + if ((m) & HL_DIM) \ + wattroff(win,A_DIM); \ + if ((m) & HL_BLINK) \ + wattroff(win,A_BLINK); \ + if ((m) & HL_ULINE) \ + wattroff(win,A_UNDERLINE); \ + if ((m) & HL_INVERSE) \ + wattroff(win,A_REVERSE); \ + if ((m) & HL_BOLD) \ + wattroff(win,A_BOLD); \ + } + +#ifdef STATUS_HILITES + +#ifdef TEXTCOLOR +#define MaybeDisplayCond(bm,txt) \ + if (curses_condition_bits & (bm)) { \ + putstr(STATUS_WIN, 0, " "); \ + if (iflags.hilite_delta) { \ + attrmask = condattr((bm), colormasks); \ + Begin_Attr(attrmask); \ + if ((coloridx = condcolor((bm), colormasks)) != NO_COLOR) \ + curses_toggle_color_attr(win, coloridx, NONE, ON); \ + } \ + putstr(STATUS_WIN, 0, (txt)); \ + if (iflags.hilite_delta) { \ + if (coloridx != NO_COLOR) \ + curses_toggle_color_attr(win, coloridx, NONE, OFF); \ + End_Attr(attrmask); \ + } \ + } +#else +#define MaybeDisplayCond(bm,txt) \ + if (curses_condition_bits & (bm)) { \ + putstr(STATUS_WIN, 0, " "); \ + if (iflags.hilite_delta) { \ + attrmask = condattr((bm), colormasks); \ + Begin_Attr(attrmask); \ + } \ + putstr(STATUS_WIN, 0, (txt)); \ + if (iflags.hilite_delta) { \ + End_Attr(attrmask); \ + } \ + } +#endif + +void +curses_status_update(fldidx, ptr, chg, percent, color, colormasks) +int fldidx, chg UNUSED, percent, color; +genericptr_t ptr; +unsigned long *colormasks; +{ + long *condptr = (long *) ptr; + char *text = (char *) ptr; + char *goldnum = NULL; + static boolean oncearound = FALSE; /* prevent premature partial display */ + boolean use_name = TRUE; + + if (fldidx != BL_FLUSH) { + if (!status_activefields[fldidx]) + return; + if (fldidx == BL_GOLD) + goldnum = index(text,':') + 1; + switch (fldidx) { + case BL_CONDITION: + curses_condition_bits = *condptr; + oncearound = TRUE; + break; + case BL_TITLE: + case BL_HPMAX: + case BL_ENEMAX: + case BL_HUNGER: + case BL_CAP: + case BL_EXP: + use_name = FALSE; + /* FALLTHROUGH */ + default: + Sprintf(status_vals[fldidx], + (fldidx == BL_TITLE && iflags.wc2_hitpointbar) ? "%-30s" : + status_fieldfmt[fldidx] ? status_fieldfmt[fldidx] : "%s", + text); + if (use_name) { + Sprintf(status_vals_long[fldidx], "%-16s: %s", + status_fieldnm[fldidx], goldnum ? goldnum : text); + *(status_vals_long[fldidx]) = toupper((*status_vals_long[fldidx])); + } else + Strcpy(status_vals_long[fldidx], status_vals[fldidx]); +#ifdef TEXTCOLOR + curses_status_colors[fldidx] = color; +#else + curses_status_colors[fldidx] = NO_COLOR; +#endif + if (iflags.wc2_hitpointbar && fldidx == BL_HP) { + hpbar_percent = percent; +#ifdef TEXTCOLOR + hpbar_color = color; +#else + hpbar_color = NO_COLOR; +#endif + } + break; + } + } + + if (!oncearound) return; + draw_status(colormasks); +} + +void +draw_status(colormasks) +unsigned long *colormasks; +{ + boolean horiz = FALSE; + WINDOW *win = curses_get_nhwin(STATUS_WIN); + int orient = curses_get_window_orientation(STATUS_WIN); + boolean border = curses_window_has_border(STATUS_WIN); + + if ((orient != ALIGN_RIGHT) && (orient != ALIGN_LEFT)) + horiz = TRUE; + + /* Figure out if we have proper window dimensions for horizontal statusbar. */ + if (horiz) { + /* correct y */ + int cy = 3; + if (iflags.statuslines < 3) + cy = 2; + + /* actual y (and x) */ + int ax = 0; + int ay = 0; + getmaxyx(win, ay, ax); + if (border) + ay -= 2; + + if (cy != ay) { /* something changed. Redo everything */ + curses_create_main_windows(); + curses_last_messages(); + doredraw(); + win = curses_get_nhwin(STATUS_WIN); + } + } + + werase(win); + if (horiz) { + if (iflags.statuslines < 3) + draw_classic(border, colormasks); + else + draw_horizontal(border, colormasks); + } else + draw_vertical(border, colormasks); + + if (border) + box(win, 0, 0); + wnoutrefresh(win); +} + +/* The 'classic' NetHack 3.x status layout */ +void +draw_classic(border, colormasks) +boolean border; +unsigned long *colormasks; +{ + enum statusfields fieldorder[2][15] = { + { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, BL_ALIGN, + BL_SCORE, BL_FLUSH, BL_FLUSH, BL_FLUSH, BL_FLUSH, BL_FLUSH, + BL_FLUSH }, + { BL_LEVELDESC, BL_GOLD, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX, + BL_AC, BL_XP, BL_EXP, BL_HD, BL_TIME, BL_HUNGER, + BL_CAP, BL_CONDITION, BL_FLUSH } + }; +#ifdef TEXTCOLOR + int coloridx = NO_COLOR; +#endif + int i, attrmask = 0; + char *text; + int attridx = 0; + + WINDOW *win = curses_get_nhwin(STATUS_WIN); + if (border) wmove(win, 1, 1); + else wmove(win, 0, 0); + for (i = 0; fieldorder[0][i] != BL_FLUSH; ++i) { + int fldidx1 = fieldorder[0][i]; + if (status_activefields[fldidx1]) { + if (fldidx1 != BL_TITLE || !iflags.wc2_hitpointbar) { +#ifdef TEXTCOLOR + coloridx = curses_status_colors[fldidx1] & 0x00FF; +#endif + attridx = (curses_status_colors[fldidx1] & 0xFF00) >> 8; + text = status_vals[fldidx1]; + if (iflags.hilite_delta) { + if (*text == ' ') { + putstr(STATUS_WIN, 0, " "); + text++; + } + /* multiple attributes can be in effect concurrently */ + Begin_Attr(attridx); +#ifdef TEXTCOLOR + if (coloridx != NO_COLOR && coloridx != CLR_MAX) + curses_toggle_color_attr(win, coloridx, NONE, ON); +#endif + } + + putstr(STATUS_WIN, 0, text); + + if (iflags.hilite_delta) { +#ifdef TEXTCOLOR + if (coloridx != NO_COLOR) + curses_toggle_color_attr(win, coloridx,NONE,OFF); +#endif + End_Attr(attridx); + } + } else { + /* hitpointbar using hp percent calculation */ + int bar_pos, bar_len; + char *bar2 = (char *)0; + char bar[MAXCO], savedch; + boolean twoparts = FALSE; + + text = status_vals[fldidx1]; + bar_len = strlen(text); + if (bar_len < MAXCO-1) { + Strcpy(bar, text); + bar_pos = (bar_len * hpbar_percent) / 100; + if (bar_pos < 1 && hpbar_percent > 0) + bar_pos = 1; + if (bar_pos >= bar_len && hpbar_percent < 100) + bar_pos = bar_len - 1; + if (bar_pos > 0 && bar_pos < bar_len) { + twoparts = TRUE; + bar2 = &bar[bar_pos]; + savedch = *bar2; + *bar2 = '\0'; + } + } + if (iflags.hilite_delta && iflags.wc2_hitpointbar) { + putstr(STATUS_WIN, 0, "["); +#ifdef TEXTCOLOR + coloridx = hpbar_color & 0x00FF; + /* attridx = (hpbar_color & 0xFF00) >> 8; */ + if (coloridx != NO_COLOR) + curses_toggle_color_attr(win,coloridx,NONE,ON); +#endif + wattron(win,A_REVERSE); + putstr(STATUS_WIN, 0, bar); + wattroff(win,A_REVERSE); +#ifdef TEXTCOLOR + if (coloridx != NO_COLOR) + curses_toggle_color_attr(win,coloridx,NONE,OFF); +#endif + if (twoparts) { + *bar2 = savedch; + putstr(STATUS_WIN, 0, bar2); + } + putstr(STATUS_WIN, 0, "]"); + } else + putstr(STATUS_WIN, 0, text); + } + } + } + wclrtoeol(win); + if (border) wmove(win, 2, 1); + else wmove (win, 1, 0); + for (i = 0; fieldorder[1][i] != BL_FLUSH; ++i) { + int fldidx2 = fieldorder[1][i]; + + if (status_activefields[fldidx2]) { + if (fldidx2 != BL_CONDITION) { +#ifdef TEXTCOLOR + coloridx = curses_status_colors[fldidx2] & 0x00FF; +#endif + attridx = (curses_status_colors[fldidx2] & 0xFF00) >> 8; + text = status_vals[fldidx2]; + if (iflags.hilite_delta) { + if (*text == ' ') { + putstr(STATUS_WIN, 0, " "); + text++; + } + /* multiple attributes can be in effect concurrently */ + Begin_Attr(attridx); +#ifdef TEXTCOLOR + if (coloridx != NO_COLOR && coloridx != CLR_MAX) + curses_toggle_color_attr(win,coloridx,NONE,ON); +#endif + } + + if (fldidx2 == BL_GOLD) { + /* putmixed() due to GOLD glyph */ + putmixed(STATUS_WIN, 0, text); + } else { + putstr(STATUS_WIN, 0, text); + } + + if (iflags.hilite_delta) { +#ifdef TEXTCOLOR + if (coloridx != NO_COLOR) + curses_toggle_color_attr(win,coloridx,NONE,OFF); +#endif + End_Attr(attridx); + } + } else { + MaybeDisplayCond(BL_MASK_STONE, "Stone"); + MaybeDisplayCond(BL_MASK_SLIME, "Slime"); + MaybeDisplayCond(BL_MASK_STRNGL, "Strngl"); + MaybeDisplayCond(BL_MASK_FOODPOIS, "FoodPois"); + MaybeDisplayCond(BL_MASK_TERMILL, "TermIll"); + MaybeDisplayCond(BL_MASK_BLIND, "Blind"); + MaybeDisplayCond(BL_MASK_DEAF, "Deaf"); + MaybeDisplayCond(BL_MASK_STUN, "Stun"); + MaybeDisplayCond(BL_MASK_CONF, "Conf"); + MaybeDisplayCond(BL_MASK_HALLU, "Hallu"); + MaybeDisplayCond(BL_MASK_LEV, "Lev"); + MaybeDisplayCond(BL_MASK_FLY, "Fly"); + MaybeDisplayCond(BL_MASK_RIDE, "Ride"); + } + } + } + wclrtoeol(win); + return; +} + +/* The new NH4-style horizontal layout on 3 lines */ +void +draw_horizontal(border, colormasks) +boolean border; +unsigned long *colormasks; +{ + /* TODO: implement this */ + /* for now, just draw classic */ + draw_classic(border, colormasks); +} + +/* The vertical layout from the original curses implementation */ +void +draw_vertical(border, colormasks) +boolean border; +unsigned long *colormasks; +{ + enum statusfields fieldorder[24] = { + BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, BL_ALIGN, + BL_SCORE, BL_LEVELDESC, BL_GOLD, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX, + BL_AC, BL_XP, BL_EXP, BL_HD, BL_TIME, BL_HUNGER, + BL_CAP, BL_CONDITION, BL_FLUSH + }; +#ifdef TEXTCOLOR + int coloridx = NO_COLOR; +#endif + int i, attrmask = 0; + char *text; + int attridx = 0; + int x = 0, y = 0; + + WINDOW *win = curses_get_nhwin(STATUS_WIN); + if (border) x++,y++; + for (i = 0; fieldorder[i] != BL_FLUSH; ++i) { + int fldidx1 = fieldorder[i]; + if (status_activefields[fldidx1]) { + if (fldidx1 != BL_TITLE || !iflags.wc2_hitpointbar) { + + if (fldidx1 != BL_CONDITION) { +#ifdef TEXTCOLOR + coloridx = curses_status_colors[fldidx1] & 0x00FF; +#endif + attridx = (curses_status_colors[fldidx1] & 0xFF00) >> 8; + text = status_vals_long[fldidx1]; + if (iflags.hilite_delta) { + if (*text == ' ') { + putstr(STATUS_WIN, 0, " "); + text++; + } + /* multiple attributes can be in effect concurrently */ + Begin_Attr(attridx); +#ifdef TEXTCOLOR + if (coloridx != NO_COLOR && coloridx != CLR_MAX) + curses_toggle_color_attr(win, coloridx, NONE, ON); +#endif + } + + if (fldidx1 != BL_HPMAX + && fldidx1 != BL_ENEMAX + && fldidx1 != BL_EXP) + wmove(win, y++, x); /* everything on a new line except the above */ + + putstr(STATUS_WIN, 0, text); + + if (fldidx1 == BL_TITLE) y++; + + if (iflags.hilite_delta) { +#ifdef TEXTCOLOR + if (coloridx != NO_COLOR) + curses_toggle_color_attr(win, coloridx,NONE,OFF); +#endif + End_Attr(attridx); + } + } else { /* condition */ + MaybeDisplayCond(BL_MASK_STONE, "Stone"); + MaybeDisplayCond(BL_MASK_SLIME, "Slime"); + MaybeDisplayCond(BL_MASK_STRNGL, "Strngl"); + MaybeDisplayCond(BL_MASK_FOODPOIS, "FoodPois"); + MaybeDisplayCond(BL_MASK_TERMILL, "TermIll"); + MaybeDisplayCond(BL_MASK_BLIND, "Blind"); + MaybeDisplayCond(BL_MASK_DEAF, "Deaf"); + MaybeDisplayCond(BL_MASK_STUN, "Stun"); + MaybeDisplayCond(BL_MASK_CONF, "Conf"); + MaybeDisplayCond(BL_MASK_HALLU, "Hallu"); + MaybeDisplayCond(BL_MASK_LEV, "Lev"); + MaybeDisplayCond(BL_MASK_FLY, "Fly"); + MaybeDisplayCond(BL_MASK_RIDE, "Ride"); + } + } else { + /* hitpointbar using hp percent calculation */ + int bar_pos, bar_len; + char *bar2 = (char *)0; + char bar[MAXCO], savedch; + boolean twoparts = FALSE; + int height,width; + + text = status_vals[fldidx1]; + getmaxyx(win, height, width); + bar_len = min(strlen(text), (size_t)width - (border ? 4 : 2)); + text[bar_len] = '\0'; + if (bar_len < MAXCO-1) { + Strcpy(bar, text); + bar_pos = (bar_len * hpbar_percent) / 100; + if (bar_pos < 1 && hpbar_percent > 0) + bar_pos = 1; + if (bar_pos >= bar_len && hpbar_percent < 100) + bar_pos = bar_len - 1; + if (bar_pos > 0 && bar_pos < bar_len) { + twoparts = TRUE; + bar2 = &bar[bar_pos]; + savedch = *bar2; + *bar2 = '\0'; + } + } + wmove(win, y++, x); + if (iflags.hilite_delta && iflags.wc2_hitpointbar) { + putstr(STATUS_WIN, 0, "["); +#ifdef TEXTCOLOR + coloridx = hpbar_color & 0x00FF; + /* attridx = (hpbar_color & 0xFF00) >> 8; */ + if (coloridx != NO_COLOR) + curses_toggle_color_attr(win,coloridx,NONE,ON); +#endif + wattron(win,A_REVERSE); + putstr(STATUS_WIN, 0, bar); + wattroff(win,A_REVERSE); +#ifdef TEXTCOLOR + if (coloridx != NO_COLOR) + curses_toggle_color_attr(win,coloridx,NONE,OFF); +#endif + if (twoparts) { + *bar2 = savedch; + putstr(STATUS_WIN, 0, bar2); + } + putstr(STATUS_WIN, 0, "]"); + } else + putstr(STATUS_WIN, 0, text); + y++; /* blank line after title */ + } + } + } + return; +} + +#ifdef TEXTCOLOR +/* + * Return what color this condition should + * be displayed in based on user settings. + */ +int condcolor(bm, bmarray) +long bm; +unsigned long *bmarray; +{ + int i; + + if (bm && bmarray) + for (i = 0; i < CLR_MAX; ++i) { + if (bmarray[i] && (bm & bmarray[i])) + return i; + } + return NO_COLOR; +} +#endif /* TEXTCOLOR */ + +int condattr(bm, bmarray) +long bm; +unsigned long *bmarray; +{ + int attr = 0; + int i; + + if (bm && bmarray) { + for (i = HL_ATTCLR_DIM; i < BL_ATTCLR_MAX; ++i) { + if (bmarray[i] && (bm & bmarray[i])) { + switch(i) { + case HL_ATTCLR_DIM: + attr |= HL_DIM; + break; + case HL_ATTCLR_BLINK: + attr |= HL_BLINK; + break; + case HL_ATTCLR_ULINE: + attr |= HL_ULINE; + break; + case HL_ATTCLR_INVERSE: + attr |= HL_INVERSE; + break; + case HL_ATTCLR_BOLD: + attr |= HL_BOLD; + break; + } + } + } + } + return attr; +} +#endif /* STATUS_HILITES */ + +#if 0 //old stuff to be re-incorporated +/* Private declarations */ + +/* Used to track previous value of things, to highlight changes. */ +typedef struct nhs { + long value; + int highlight_turns; + int highlight_color; +} nhstat; + +static attr_t get_trouble_color(const char *); +static void draw_trouble_str(const char *); +static void print_statdiff(const char *append, nhstat *, int, int); +static void get_playerrank(char *); +static int hpen_color(boolean, int, int); +static void draw_bar(boolean, int, int, const char *); +static void draw_horizontal(int, int, int, int); +static void draw_horizontal_new(int, int, int, int); +static void draw_vertical(int, int, int, int); +static void curses_add_statuses(WINDOW *, boolean, boolean, int *, int *); +static void curses_add_status(WINDOW *, boolean, boolean, int *, int *, + const char *, int); +static int decrement_highlight(nhstat *, boolean); + +#ifdef STATUS_COLORS +static attr_t hpen_color_attr(boolean, int, int); +extern struct color_option text_color_of(const char *text, + const struct text_color_option *color_options); +struct color_option percentage_color_of(int value, int max, + const struct percent_color_option *color_options); + +extern const struct text_color_option *text_colors; +extern const struct percent_color_option *hp_colors; +extern const struct percent_color_option *pw_colors; +#endif + +/* Whether or not we have printed status window content at least once. + Used to ensure that prev* doesn't end up highlighted on game start. */ +static boolean first = TRUE; +static nhstat prevdepth; +static nhstat prevstr; +static nhstat prevint; +static nhstat prevwis; +static nhstat prevdex; +static nhstat prevcon; +static nhstat prevcha; +static nhstat prevau; +static nhstat prevlevel; +static nhstat prevac; +static nhstat prevexp; +static nhstat prevtime; + +#ifdef SCORE_ON_BOTL +static nhstat prevscore; +#endif + +extern const char *hu_stat[]; /* from eat.c */ +extern const char *enc_stat[]; /* from botl.c */ + +/* If the statuscolors patch isn't enabled, have some default colors for status problems + anyway */ + +struct statcolor { + const char *txt; /* For status problems */ + int color; /* Default color assuming STATUS_COLORS isn't enabled */ +}; + +static const struct statcolor default_colors[] = { + {"Satiated", CLR_YELLOW}, + {"Hungry", CLR_YELLOW}, + {"Weak", CLR_ORANGE}, + {"Fainted", CLR_BRIGHT_MAGENTA}, + {"Fainting", CLR_BRIGHT_MAGENTA}, + {"Burdened", CLR_RED}, + {"Stressed", CLR_RED}, + {"Strained", CLR_ORANGE}, + {"Overtaxed", CLR_ORANGE}, + {"Overloaded", CLR_BRIGHT_MAGENTA}, + {"Conf", CLR_BRIGHT_BLUE}, + {"Blind", CLR_BRIGHT_BLUE}, + {"Stun", CLR_BRIGHT_BLUE}, + {"Hallu", CLR_BRIGHT_BLUE}, + {"Ill", CLR_BRIGHT_MAGENTA}, + {"FoodPois", CLR_BRIGHT_MAGENTA}, + {"Slime", CLR_BRIGHT_MAGENTA}, + {NULL, NULL, NO_COLOR}, +}; + +static attr_t +get_trouble_color(const char *stat) +{ + attr_t res = curses_color_attr(CLR_GRAY, 0); + const struct statcolor *clr; + for (clr = default_colors; clr->txt; clr++) { + if (stat && !strcmp(clr->txt, stat)) { +#ifdef STATUS_COLORS + /* Check if we have a color enabled with statuscolors */ + if (!iflags.use_status_colors) + return curses_color_attr(CLR_GRAY, 0); /* no color configured */ + + struct color_option stat_color; + + stat_color = text_color_of(clr->txt, text_colors); + if (stat_color.color == NO_COLOR && !stat_color.attr_bits) + return curses_color_attr(CLR_GRAY, 0); + + if (stat_color.color != NO_COLOR) + res = curses_color_attr(stat_color.color, 0); + + res = curses_color_attr(stat_color.color, 0); + int count; + for (count = 0; (1 << count) <= stat_color.attr_bits; count++) { + if (count != ATR_NONE && + (stat_color.attr_bits & (1 << count))) + res |= curses_convert_attr(count); + } + + return res; +#else + return curses_color_attr(clr->color, 0); +#endif + } + } + + return res; +} + +/* TODO: This is in the wrong place. */ +void +get_playerrank(char *rank) +{ + char buf[BUFSZ]; + if (Upolyd) { + int k = 0; + + Strcpy(buf, mons[u.umonnum].mname); + while(buf[k] != 0) { + if ((k == 0 || (k > 0 && buf[k-1] == ' ')) && + 'a' <= buf[k] && buf[k] <= 'z') + buf[k] += 'A' - 'a'; + k++; + } + Strcpy(rank, buf); + } else + Strcpy(rank, rank_of(u.ulevel, Role_switch, flags.female)); +} + +/* Handles numerical stat changes of various kinds. + type is generally STAT_OTHER (generic "do nothing special"), + but is used if the stat needs to be handled in a special way. */ +static void +print_statdiff(const char *append, nhstat *stat, int new, int type) +{ + char buf[BUFSZ]; + WINDOW *win = curses_get_nhwin(STATUS_WIN); + + int color = CLR_GRAY; + + /* Turncount isn't highlighted, or it would be highlighted constantly. */ + if (type != STAT_TIME && new != stat->value) { + /* Less AC is better */ + if ((type == STAT_AC && new < stat->value) || + (type != STAT_AC && new > stat->value)) { + color = STAT_UP_COLOR; + if (type == STAT_GOLD) + color = HI_GOLD; + } else + color = STAT_DOWN_COLOR; + + stat->value = new; + stat->highlight_color = color; + stat->highlight_turns = 5; + } else if (stat->highlight_turns) + color = stat->highlight_color; + + attr_t attr = curses_color_attr(color, 0); + wattron(win, attr); + wprintw(win, "%s", append); + if (type == STAT_STR && new > 18) { + if (new > 118) + wprintw(win, "%d", new - 100); + else if (new == 118) + wprintw(win, "18/**"); + else + wprintw(win, "18/%02d", new - 18); + } else + wprintw(win, "%d", new); + + wattroff(win, attr); +} + +static void +draw_trouble_str(const char *str) +{ + WINDOW *win = curses_get_nhwin(STATUS_WIN); + + attr_t attr = get_trouble_color(str); + wattron(win, attr); + wprintw(win, "%s", str); + wattroff(win, attr); +} + +/* Returns a ncurses attribute for foreground and background. + This should probably be in cursinit.c or something. */ +attr_t +curses_color_attr(int nh_color, int bg_color) +{ + int color = nh_color + 1; + attr_t cattr = A_NORMAL; + + if (!nh_color) { +#ifdef USE_DARKGRAY + if (iflags.wc2_darkgray) { + if (!can_change_color() || COLORS <= 16) + cattr |= A_BOLD; + } else +#endif + color = COLOR_BLUE; + } + + if (COLORS < 16 && color > 8) { + color -= 8; + cattr = A_BOLD; + } + + /* Can we do background colors? We can if we have more than + 16*7 colors (more than 8*7 for terminals with bold) */ + if (COLOR_PAIRS > (COLORS >= 16 ? 16 : 8) * 7) { + /* NH3 has a rather overcomplicated way of defining + its colors past the first 16: + Pair Foreground Background + 17 Black Red + 18 Black Blue + 19 Red Red + 20 Red Blue + 21 Green Red + ... + (Foreground order: Black, Red, Green, Yellow, Blue, + Magenta, Cyan, Gray/White) + + To work around these oddities, we define backgrounds + by the following pairs: + + 16 COLORS + 49-64: Green + 65-80: Yellow + 81-96: Magenta + 97-112: Cyan + 113-128: Gray/White + + 8 COLORS + 9-16: Green + 33-40: Yellow + 41-48: Magenta + 49-56: Cyan + 57-64: Gray/White */ + + if (bg_color == nh_color) + color = 1; /* Make foreground black if fg==bg */ + + if (bg_color == CLR_RED || bg_color == CLR_BLUE) { + /* already defined before extension */ + color *= 2; + color += 16; + if (bg_color == CLR_RED) + color--; + } else { + boolean hicolor = FALSE; + if (COLORS >= 16) + hicolor = TRUE; + + switch (bg_color) { + case CLR_GREEN: + color = (hicolor ? 48 : 8) + color; + break; + case CLR_BROWN: + color = (hicolor ? 64 : 32) + color; + break; + case CLR_MAGENTA: + color = (hicolor ? 80 : 40) + color; + break; + case CLR_CYAN: + color = (hicolor ? 96 : 48) + color; + break; + case CLR_GRAY: + color = (hicolor ? 112 : 56) + color; + break; + default: + break; + } + } + } + cattr |= COLOR_PAIR(color); + + return cattr; +} + +/* Returns a complete curses attribute. Used to possibly bold/underline/etc HP/Pw. */ +#ifdef STATUS_COLORS +static attr_t +hpen_color_attr(boolean is_hp, int cur, int max) +{ + struct color_option stat_color; + int count; + attr_t attr = 0; + if (!iflags.use_status_colors) + return curses_color_attr(CLR_GRAY, 0); + + stat_color = percentage_color_of(cur, max, is_hp ? hp_colors : pw_colors); + + if (stat_color.color != NO_COLOR) + attr |= curses_color_attr(stat_color.color, 0); + + for (count = 0; (1 << count) <= stat_color.attr_bits; count++) { + if (count != ATR_NONE && (stat_color.attr_bits & (1 << count))) + attr |= curses_convert_attr(count); + } + + return attr; +} +#endif + +/* Return color for the HP bar. + With status colors ON, this respect its configuration (defaulting to gray), but + only obeys the color (no weird attributes for the HP bar). + With status colors OFF, this returns reasonable defaults which are also used + for the HP/Pw text itself. */ +static int +hpen_color(boolean is_hp, int cur, int max) +{ +#ifdef STATUS_COLORS + if (iflags.use_status_colors) { + struct color_option stat_color; + stat_color = percentage_color_of(cur, max, is_hp ? hp_colors : pw_colors); + + if (stat_color.color == NO_COLOR) + return CLR_GRAY; + else + return stat_color.color; + } else + return CLR_GRAY; +#endif + + int color = CLR_GRAY; + if (cur == max) + color = CLR_GRAY; + else if (cur * 3 > max * 2) /* >2/3 */ + color = is_hp ? CLR_GREEN : CLR_CYAN; + else if (cur * 3 > max) /* >1/3 */ + color = is_hp ? CLR_YELLOW : CLR_BLUE; + else if (cur * 7 > max) /* >1/7 */ + color = is_hp ? CLR_RED : CLR_MAGENTA; + else + color = is_hp ? CLR_ORANGE : CLR_BRIGHT_MAGENTA; + + return color; +} + +/* Draws a bar + is_hp: TRUE if we're drawing HP, Pw otherwise (determines colors) + cur/max: Current/max HP/Pw + title: Not NULL if we are drawing as part of an existing title. + Otherwise, the format is as follows: [ 11 / 11 ] */ +static void +draw_bar(boolean is_hp, int cur, int max, const char *title) +{ + WINDOW *win = curses_get_nhwin(STATUS_WIN); + +#ifdef STATUS_COLORS + if (!iflags.hitpointbar) { + wprintw(win, "%s", !title ? "---" : title); + return; + } +#endif + + char buf[BUFSZ]; + if (title) + Strcpy(buf, title); + else { + int len = 5; + sprintf(buf, "%*d / %-*d", len, cur, len, max); + } + + /* Colors */ + attr_t fillattr, attr; + int color = hpen_color(is_hp, cur, max); + int invcolor = color & 7; + + fillattr = curses_color_attr(color, invcolor); + attr = curses_color_attr(color, 0); + + /* Figure out how much of the bar to fill */ + int fill = 0; + int len = strlen(buf); + if (cur > 0 && max > 0) + fill = len * cur / max; + if (fill > len) + fill = len; + + waddch(win, '['); + wattron(win, fillattr); + wprintw(win, "%.*s", fill, buf); + wattroff(win, fillattr); + wattron(win, attr); + wprintw(win, "%.*s", len - fill, &buf[fill]); + wattroff(win, attr); + waddch(win, ']'); +} + +/* Update the status win - this is called when NetHack would normally + write to the status window, so we know somwthing has changed. We + override the write and update what needs to be updated ourselves. */ +void +curses_update_stats(void) +{ + WINDOW *win = curses_get_nhwin(STATUS_WIN); + + /* Clear the window */ + werase(win); + + int orient = curses_get_window_orientation(STATUS_WIN); + + boolean horiz = FALSE; + if ((orient != ALIGN_RIGHT) && (orient != ALIGN_LEFT)) + horiz = TRUE; + boolean border = curses_window_has_border(STATUS_WIN); + + /* Figure out if we have proper window dimensions for horizontal statusbar. */ + if (horiz) { + /* correct y */ + int cy = 3; + if (iflags.statuslines < 3) + cy = 2; + + /* actual y (and x) */ + int ax = 0; + int ay = 0; + getmaxyx(win, ay, ax); + if (border) + ay -= 2; + + if (cy != ay) { + curses_create_main_windows(); + curses_last_messages(); + doredraw(); + + /* Reset XP highlight (since classic_status and new show different numbers) */ + prevexp.highlight_turns = 0; + curses_update_stats(); + return; + } + } + + /* Starting x/y. Passed to draw_horizontal/draw_vertical to keep track of + window positioning. */ + int x = 0; + int y = 0; + + /* Don't start at border position if applicable */ + if (border) { + x++; + y++; + } + + /* Get HP values. */ + int hp = u.uhp; + int hpmax = u.uhpmax; + if (Upolyd) { + hp = u.mh; + hpmax = u.mhmax; + } + + if (orient != ALIGN_RIGHT && orient != ALIGN_LEFT) + draw_horizontal(x, y, hp, hpmax); + else + draw_vertical(x, y, hp, hpmax); + + if (border) + box(win, 0, 0); + + wnoutrefresh(win); + + if (first) { + first = FALSE; + + /* Zero highlight timers. This will call curses_update_status again if needed */ + curses_decrement_highlights(TRUE); + } +} + +static void +draw_horizontal(int x, int y, int hp, int hpmax) +{ + if (iflags.statuslines >= 3) { + /* Draw new-style statusbar */ + draw_horizontal_new(x, y, hp, hpmax); + return; + } + char buf[BUFSZ]; + char rank[BUFSZ]; + WINDOW *win = curses_get_nhwin(STATUS_WIN); + + /* Line 1 */ + wmove(win, y, x); + + get_playerrank(rank); + sprintf(buf, "%s the %s", plname, rank); + + /* Use the title as HP bar (similar to hitpointbar) */ + draw_bar(TRUE, hp, hpmax, buf); + + /* Attributes */ + print_statdiff(" St:", &prevstr, ACURR(A_STR), STAT_STR); + print_statdiff(" Dx:", &prevdex, ACURR(A_DEX), STAT_OTHER); + print_statdiff(" Co:", &prevcon, ACURR(A_CON), STAT_OTHER); + print_statdiff(" In:", &prevint, ACURR(A_INT), STAT_OTHER); + print_statdiff(" Wi:", &prevwis, ACURR(A_WIS), STAT_OTHER); + print_statdiff(" Ch:", &prevcha, ACURR(A_CHA), STAT_OTHER); + + wprintw(win, (u.ualign.type == A_CHAOTIC ? " Chaotic" : + u.ualign.type == A_NEUTRAL ? " Neutral" : " Lawful")); + +#ifdef SCORE_ON_BOTL + if (flags.showscore) + print_statdiff(" S:", &prevscore, botl_score(), STAT_OTHER); +#endif /* SCORE_ON_BOTL */ + + + /* Line 2 */ + y++; + wmove(win, y, x); + + describe_level(buf); + + wprintw(win, "%s", buf); + + print_statdiff("$", &prevau, money_cnt(invent), STAT_GOLD); + + /* HP/Pw use special coloring rules */ + attr_t hpattr, pwattr; +#ifdef STATUS_COLORS + hpattr = hpen_color_attr(TRUE, hp, hpmax); + pwattr = hpen_color_attr(FALSE, u.uen, u.uenmax); +#else + int hpcolor, pwcolor; + hpcolor = hpen_color(TRUE, hp, hpmax); + pwcolor = hpen_color(FALSE, u.uen, u.uenmax); + hpattr = curses_color_attr(hpcolor, 0); + pwattr = curses_color_attr(pwcolor, 0); +#endif + wprintw(win, " HP:"); + wattron(win, hpattr); + wprintw(win, "%d(%d)", (hp < 0) ? 0 : hp, hpmax); + wattroff(win, hpattr); + + wprintw(win, " Pw:"); + wattron(win, pwattr); + wprintw(win, "%d(%d)", u.uen, u.uenmax); + wattroff(win, pwattr); + + print_statdiff(" AC:", &prevac, u.uac, STAT_AC); + + if (Upolyd) + print_statdiff(" HD:", &prevlevel, mons[u.umonnum].mlevel, STAT_OTHER); + else if (flags.showexp) { + print_statdiff(" Xp:", &prevlevel, u.ulevel, STAT_OTHER); + /* use waddch, we don't want to highlight the '/' */ + waddch(win, '/'); + print_statdiff("", &prevexp, u.uexp, STAT_OTHER); + } + else + print_statdiff(" Exp:", &prevlevel, u.ulevel, STAT_OTHER); + + if (flags.time) + print_statdiff(" T:", &prevtime, moves, STAT_TIME); + + curses_add_statuses(win, FALSE, FALSE, NULL, NULL); +} + +static void +draw_horizontal_new(int x, int y, int hp, int hpmax) +{ + char buf[BUFSZ]; + char rank[BUFSZ]; + WINDOW *win = curses_get_nhwin(STATUS_WIN); + + /* Line 1 */ + wmove(win, y, x); + + get_playerrank(rank); + char race[BUFSZ]; + Strcpy(race, urace.adj); + race[0] = highc(race[0]); + wprintw(win, "%s the %s %s%s%s", plname, + (u.ualign.type == A_CHAOTIC ? "Chaotic" : + u.ualign.type == A_NEUTRAL ? "Neutral" : "Lawful"), + Upolyd ? "" : race, Upolyd ? "" : " ", + rank); + + /* Line 2 */ + y++; + wmove(win, y, x); + wprintw(win, "HP:"); + draw_bar(TRUE, hp, hpmax, NULL); + print_statdiff(" AC:", &prevac, u.uac, STAT_AC); + if (Upolyd) + print_statdiff(" HD:", &prevlevel, mons[u.umonnum].mlevel, STAT_OTHER); + else if (flags.showexp) { + /* Ensure that Xp have proper highlight on level change. */ + int levelchange = 0; + if (prevlevel.value != u.ulevel) { + if (prevlevel.value < u.ulevel) + levelchange = 1; + else + levelchange = 2; + } + print_statdiff(" Xp:", &prevlevel, u.ulevel, STAT_OTHER); + /* use waddch, we don't want to highlight the '/' */ + waddch(win, '('); + + /* Figure out amount of Xp needed to next level */ + int xp_left = 0; + if (u.ulevel < 30) + xp_left = (newuexp(u.ulevel) - u.uexp); + + if (levelchange) { + prevexp.value = (xp_left + 1); + if (levelchange == 2) + prevexp.value = (xp_left - 1); + } + print_statdiff("", &prevexp, xp_left, STAT_AC); + waddch(win, ')'); + } + else + print_statdiff(" Exp:", &prevlevel, u.ulevel, STAT_OTHER); + + waddch(win, ' '); + describe_level(buf); + + wprintw(win, "%s", buf); + + /* Line 3 */ + y++; + wmove(win, y, x); + wprintw(win, "Pw:"); + draw_bar(FALSE, u.uen, u.uenmax, NULL); + + print_statdiff(" $", &prevau, money_cnt(invent), STAT_GOLD); + +#ifdef SCORE_ON_BOTL + if (flags.showscore) + print_statdiff(" S:", &prevscore, botl_score(), STAT_OTHER); +#endif /* SCORE_ON_BOTL */ + + if (flags.time) + print_statdiff(" T:", &prevtime, moves, STAT_TIME); + + curses_add_statuses(win, TRUE, FALSE, &x, &y); + + /* Right-aligned attributes */ + int stat_length = 6; /* " Dx:xx" */ + int str_length = 6; + if (ACURR(A_STR) > 18 && ACURR(A_STR) < 119) + str_length = 9; + + getmaxyx(win, y, x); + + /* We want to deal with top line of y. getmaxx would do what we want, but it only + exist for compatibility reasons and might not exist at all in some versions. */ + y = 0; + if (curses_window_has_border(STATUS_WIN)) { + x--; + y++; + } + + x -= stat_length; + int orig_x = x; + wmove(win, y, x); + print_statdiff(" Co:", &prevcon, ACURR(A_CON), STAT_OTHER); + x -= stat_length; + wmove(win, y, x); + print_statdiff(" Dx:", &prevdex, ACURR(A_DEX), STAT_OTHER); + x -= str_length; + wmove(win, y, x); + print_statdiff(" St:", &prevstr, ACURR(A_STR), STAT_STR); + + x = orig_x; + y++; + wmove(win, y, x); + print_statdiff(" Ch:", &prevcha, ACURR(A_CHA), STAT_OTHER); + x -= stat_length; + wmove(win, y, x); + print_statdiff(" Wi:", &prevwis, ACURR(A_WIS), STAT_OTHER); + x -= str_length; + wmove(win, y, x); + print_statdiff(" In:", &prevint, ACURR(A_INT), STAT_OTHER); +} + +/* Personally I never understood the point of a vertical status bar. But removing the + option would be silly, so keep the functionality. */ +static void +draw_vertical(int x, int y, int hp, int hpmax) +{ + char buf[BUFSZ]; + char rank[BUFSZ]; + WINDOW *win = curses_get_nhwin(STATUS_WIN); + + /* Print title and dungeon branch */ + wmove(win, y++, x); + + get_playerrank(rank); + int ranklen = strlen(rank); + int namelen = strlen(plname); + int maxlen = 19; +#ifdef STATUS_COLORS + if (!iflags.hitpointbar) + maxlen += 2; /* With no hitpointbar, we can fit more since there's no "[]" */ +#endif + + if ((ranklen + namelen) > maxlen) { + /* The result doesn't fit. Strip name if >10 characters, then strip title */ + if (namelen > 10) { + while (namelen > 10 && (ranklen + namelen) > maxlen) + namelen--; + } + + while ((ranklen + namelen) > maxlen) + ranklen--; /* Still doesn't fit, strip rank */ + } + sprintf(buf, "%-*s the %-*s", namelen, plname, ranklen, rank); + draw_bar(TRUE, hp, hpmax, buf); + wmove(win, y++, x); + wprintw(win, "%s", dungeons[u.uz.dnum].dname); + + y++; /* Blank line inbetween */ + wmove(win, y++, x); + + /* Attributes. Old vertical order is preserved */ + print_statdiff("Strength: ", &prevstr, ACURR(A_STR), STAT_STR); + wmove(win, y++, x); + print_statdiff("Intelligence: ", &prevint, ACURR(A_INT), STAT_OTHER); + wmove(win, y++, x); + print_statdiff("Wisdom: ", &prevwis, ACURR(A_WIS), STAT_OTHER); + wmove(win, y++, x); + print_statdiff("Dexterity: ", &prevdex, ACURR(A_DEX), STAT_OTHER); + wmove(win, y++, x); + print_statdiff("Constitution: ", &prevcon, ACURR(A_CON), STAT_OTHER); + wmove(win, y++, x); + print_statdiff("Charisma: ", &prevcha, ACURR(A_CHA), STAT_OTHER); + wmove(win, y++, x); + wprintw(win, "Alignment: "); + wprintw(win, (u.ualign.type == A_CHAOTIC ? "Chaotic" : + u.ualign.type == A_NEUTRAL ? "Neutral" : "Lawful")); + wmove(win, y++, x); + wprintw(win, "Dungeon Level: "); + + /* Astral Plane doesn't fit */ + if (In_endgame(&u.uz)) + wprintw(win, "%s", Is_astralevel(&u.uz) ? "Astral" : "End Game"); + else + wprintw(win, "%d", depth(&u.uz)); + wmove(win, y++, x); + + print_statdiff("Gold: ", &prevau, money_cnt(invent), STAT_GOLD); + wmove(win, y++, x); + + /* HP/Pw use special coloring rules */ + attr_t hpattr, pwattr; +#ifdef STATUS_COLORS + hpattr = hpen_color_attr(TRUE, hp, hpmax); + pwattr = hpen_color_attr(FALSE, u.uen, u.uenmax); +#else + int hpcolor, pwcolor; + hpcolor = hpen_color(TRUE, hp, hpmax); + pwcolor = hpen_color(FALSE, u.uen, u.uenmax); + hpattr = curses_color_attr(hpcolor, 0); + pwattr = curses_color_attr(pwcolor, 0); +#endif + + wprintw(win, "Hit Points: "); + wattron(win, hpattr); + wprintw(win, "%d/%d", (hp < 0) ? 0 : hp, hpmax); + wattroff(win, hpattr); + wmove(win, y++, x); + + wprintw(win, "Magic Power: "); + wattron(win, pwattr); + wprintw(win, "%d/%d", u.uen, u.uenmax); + wattroff(win, pwattr); + wmove(win, y++, x); + + print_statdiff("Armor Class: ", &prevac, u.uac, STAT_AC); + wmove(win, y++, x); + + if (Upolyd) + print_statdiff("Hit Dice: ", &prevlevel, mons[u.umonnum].mlevel, STAT_OTHER); + else if (flags.showexp) { + print_statdiff("Experience: ", &prevlevel, u.ulevel, STAT_OTHER); + /* use waddch, we don't want to highlight the '/' */ + waddch(win, '/'); + print_statdiff("", &prevexp, u.uexp, STAT_OTHER); + } + else + print_statdiff("Level: ", &prevlevel, u.ulevel, STAT_OTHER); + wmove(win, y++, x); + + if (flags.time) { + print_statdiff("Time: ", &prevtime, moves, STAT_TIME); + wmove(win, y++, x); + } + +#ifdef SCORE_ON_BOTL + if (flags.showscore) { + print_statdiff("Score: ", &prevscore, botl_score(), STAT_OTHER); + wmove(win, y++, x); + } +#endif /* SCORE_ON_BOTL */ + + curses_add_statuses(win, FALSE, TRUE, &x, &y); +} + +static void +curses_add_statuses(WINDOW *win, boolean align_right, + boolean vertical, int *x, int *y) +{ + if (align_right) { + /* Right-aligned statuses. Since add_status decrease one x more + (to separate them with spaces), add 1 to x unless we have borders + (which would offset what add_status does) */ + int mx = *x; + int my = *y; + getmaxyx(win, my, mx); + if (!curses_window_has_border(STATUS_WIN)) + mx++; + + *x = mx; + } + +#define statprob(str, trouble) \ + curses_add_status(win, align_right, vertical, x, y, str, trouble) + + /* Hunger */ + statprob(hu_stat[u.uhs], u.uhs != 1); /* 1 is NOT_HUNGRY (not defined here) */ + + /* General troubles */ + statprob("Conf", Confusion); + statprob("Blind", Blind); + statprob("Stun", Stunned); + statprob("Hallu", Hallucination); + statprob("Ill", (u.usick_type & SICK_NONVOMITABLE)); + statprob("FoodPois", (u.usick_type & SICK_VOMITABLE)); + statprob("Slime", Slimed); + + /* Encumbrance */ + int enc = near_capacity(); + statprob(enc_stat[enc], enc > UNENCUMBERED); +#undef statprob +} + +static void +curses_add_status(WINDOW *win, boolean align_right, boolean vertical, + int *x, int *y, const char *str, int trouble) +{ + /* If vertical is TRUE here with no x/y, that's an error. But handle + it gracefully since NH3 doesn't recover well in crashes. */ + if (!x || !y) + vertical = FALSE; + + if (!trouble) + return; + + if (!vertical && !align_right) + waddch(win, ' '); + + /* For whatever reason, hunger states have trailing spaces. Get rid of them. */ + char buf[BUFSZ]; + Strcpy(buf, str); + int i; + for (i = 0; (buf[i] != ' ' && buf[i] != '\0'); i++) ; + + buf[i] = '\0'; + if (align_right) { + *x -= (strlen(buf) + 1); /* add spacing */ + wmove(win, *y, *x); + } + + draw_trouble_str(buf); + + if (vertical) { + wmove(win, *y, *x); + *y += 1; /* ++ advances the pointer addr */ + } +} + +/* Decrement a single highlight, return 1 if decremented to zero. zero is TRUE if we're + zeroing the highlight. */ +static int +decrement_highlight(nhstat *stat, boolean zero) +{ + if (stat->highlight_turns > 0) { + if (zero) { + stat->highlight_turns = 0; + return 1; + } + + stat->highlight_turns--; + if (stat->highlight_turns == 0) + return 1; + } + return 0; +} + +/* Decrement the highlight_turns for all stats. Call curses_update_stats + if needed to unhighlight a stat */ +void +curses_decrement_highlights(boolean zero) +{ + int unhighlight = 0; + + unhighlight |= decrement_highlight(&prevdepth, zero); + unhighlight |= decrement_highlight(&prevstr, zero); + unhighlight |= decrement_highlight(&prevdex, zero); + unhighlight |= decrement_highlight(&prevcon, zero); + unhighlight |= decrement_highlight(&prevint, zero); + unhighlight |= decrement_highlight(&prevwis, zero); + unhighlight |= decrement_highlight(&prevcha, zero); + unhighlight |= decrement_highlight(&prevau, zero); + unhighlight |= decrement_highlight(&prevlevel, zero); + unhighlight |= decrement_highlight(&prevac, zero); + unhighlight |= decrement_highlight(&prevexp, zero); + unhighlight |= decrement_highlight(&prevtime, zero); +#ifdef SCORE_ON_BOTL + unhighlight |= decrement_highlight(&prevscore, zero); +#endif + + if (unhighlight) + curses_update_stats(); +} +#endif diff --git a/win/curses/cursstat.h b/win/curses/cursstat.h new file mode 100644 index 000000000..7773d422b --- /dev/null +++ b/win/curses/cursstat.h @@ -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 */ diff --git a/win/curses/curswins.c b/win/curses/curswins.c new file mode 100644 index 000000000..b04fd94c7 --- /dev/null +++ b/win/curses/curswins.c @@ -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; +} diff --git a/win/curses/curswins.h b/win/curses/curswins.h new file mode 100644 index 000000000..ce1935e07 --- /dev/null +++ b/win/curses/curswins.h @@ -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 */ From 113d5cdb26d76626b08434bce4bb4ea6842379f2 Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 16 Nov 2018 21:00:11 -0500 Subject: [PATCH 03/15] missed include/wincurs.h --- include/wincurs.h | 310 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 310 insertions(+) create mode 100644 include/wincurs.h diff --git a/include/wincurs.h b/include/wincurs.h new file mode 100644 index 000000000..0db58fcbc --- /dev/null +++ b/include/wincurs.h @@ -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 */ + From afe828507a545a6942b10b7e3d2118e5ccbd61a3 Mon Sep 17 00:00:00 2001 From: Bart House Date: Sat, 17 Nov 2018 14:48:38 -0800 Subject: [PATCH 04/15] Getting build working. --- win/win32/vs2017/NetHack.vcxproj | 37 +++++++++++++++++++++++++++++--- win/win32/vs2017/dirs.props | 1 + 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/win/win32/vs2017/NetHack.vcxproj b/win/win32/vs2017/NetHack.vcxproj index 66ad09e34..981bc1e45 100644 --- a/win/win32/vs2017/NetHack.vcxproj +++ b/win/win32/vs2017/NetHack.vcxproj @@ -16,6 +16,18 @@ $(BinDir) + + $(PDCURSES);$(IncludePath) + + + $(PDCURSES);$(IncludePath) + + + $(PDCURSES);$(IncludePath) + + + $(PDCURSES);$(IncludePath) + /Gs /Oi- %(AdditionalOptions) @@ -24,10 +36,14 @@ Speed true $(WinWin32Dir);$(IncDir);$(SysWinntDir);$(SysShareDir);$(WinShareDir);%(AdditionalIncludeDirectories) - TILES;WIN32CON;DLB;MSWIN_GRAPHICS;_LIB;%(PreprocessorDefinitions) + TILES;WIN32CON;DLB;MSWIN_GRAPHICS;CURSES_GRAPHICS;_LIB;%(PreprocessorDefinitions) - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;winmm.lib;Winmm.lib;%(AdditionalDependencies) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;winmm.lib;Winmm.lib;PDCurses.lib;%(AdditionalDependencies) + $(PDCURSES)\bin\$(Platform)\$(Configuration)\ + $(PDCURSES)\bin\$(Platform)\$(Configuration)\ + $(PDCURSES)\bin\$(Platform)\$(Configuration)\ + $(PDCURSES)\bin\$(Platform)\$(Configuration)\ @@ -154,6 +170,14 @@ + + + + + + + + @@ -212,6 +236,13 @@ + + + + + + + @@ -229,4 +260,4 @@ - + \ No newline at end of file diff --git a/win/win32/vs2017/dirs.props b/win/win32/vs2017/dirs.props index 9d4ed49e4..e1c36b8a1 100644 --- a/win/win32/vs2017/dirs.props +++ b/win/win32/vs2017/dirs.props @@ -20,5 +20,6 @@ $(RootDir)win\win32\ $(ToolsDir) $(ObjDir) + $(RootDir)win\curses\ From ac367ef4cc2cbad036964402ba541f869e6c8dd9 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 17 Nov 2018 20:27:21 -0500 Subject: [PATCH 05/15] curses port: accept return on Windows platform --- win/curses/cursmesg.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/win/curses/cursmesg.c b/win/curses/cursmesg.c index 9fc1d6442..67986bbbb 100644 --- a/win/curses/cursmesg.c +++ b/win/curses/cursmesg.c @@ -132,6 +132,11 @@ curses_message_win_puts(const char *message, boolean recursed) wrefresh(win); } +#ifdef WIN32 +#define XTRA_RESP "\r" +#else +#define XTRA_RESP "" +#endif int curses_block(boolean noscroll) @@ -140,8 +145,7 @@ curses_block(boolean noscroll) { int height, width, ret; WINDOW *win = curses_get_nhwin(MESSAGE_WIN); - char *resp = " \n\033"; /* space, enter, esc */ - + char *resp = " \n\033" XTRA_RESP; /* space, enter, esc */ curses_get_window_size(MESSAGE_WIN, &height, &width); curses_toggle_color_attr(win, MORECOLOR, NONE, ON); From 83fb79b77597cc44658458872a81d406ae177487 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 17 Nov 2018 21:17:43 -0500 Subject: [PATCH 06/15] more CR on windows --- win/curses/cursmesg.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/win/curses/cursmesg.c b/win/curses/cursmesg.c index 67986bbbb..d54249dcd 100644 --- a/win/curses/cursmesg.c +++ b/win/curses/cursmesg.c @@ -133,9 +133,11 @@ curses_message_win_puts(const char *message, boolean recursed) } #ifdef WIN32 -#define XTRA_RESP "\r" +#define RESP " \n\033\r" /* space, enter, esc, cr */ +#define TRANS " \n\033\n" /* translated return value */ #else -#define XTRA_RESP "" +#define RESP " \n\033" /* space, enter, esc */ +#define TRANS " \n\033" #endif int @@ -145,8 +147,9 @@ curses_block(boolean noscroll) { int height, width, ret; WINDOW *win = curses_get_nhwin(MESSAGE_WIN); - char *resp = " \n\033" XTRA_RESP; /* space, enter, esc */ - + char *resp = RESP; + char *trans = TRANS; + char *rp = (char *) 0; curses_get_window_size(MESSAGE_WIN, &height, &width); curses_toggle_color_attr(win, MORECOLOR, NONE, ON); mvwprintw(win, my, mx, iflags.msg_is_alert ? "" : ">>"); @@ -158,7 +161,9 @@ curses_block(boolean noscroll) /* 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)); + while (!iflags.msg_is_alert && + (ret = wgetch(win)) && ((rp = index(resp,(char)ret)) == 0)) + ; if (iflags.msg_is_alert) curses_alert_main_borders(FALSE); if (height == 1) { @@ -171,6 +176,8 @@ curses_block(boolean noscroll) } wrefresh(win); } + if (rp) + ret = trans[(rp - resp)]; return ret; } From 2829e3f78003bc554d435dd473693b1326eee003 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 17 Nov 2018 21:29:41 -0500 Subject: [PATCH 07/15] Revert "more CR on windows" This reverts commit 83fb79b77597cc44658458872a81d406ae177487. --- win/curses/cursmesg.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/win/curses/cursmesg.c b/win/curses/cursmesg.c index d54249dcd..67986bbbb 100644 --- a/win/curses/cursmesg.c +++ b/win/curses/cursmesg.c @@ -133,11 +133,9 @@ curses_message_win_puts(const char *message, boolean recursed) } #ifdef WIN32 -#define RESP " \n\033\r" /* space, enter, esc, cr */ -#define TRANS " \n\033\n" /* translated return value */ +#define XTRA_RESP "\r" #else -#define RESP " \n\033" /* space, enter, esc */ -#define TRANS " \n\033" +#define XTRA_RESP "" #endif int @@ -147,9 +145,8 @@ curses_block(boolean noscroll) { int height, width, ret; WINDOW *win = curses_get_nhwin(MESSAGE_WIN); - char *resp = RESP; - char *trans = TRANS; - char *rp = (char *) 0; + char *resp = " \n\033" XTRA_RESP; /* 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 ? "" : ">>"); @@ -161,9 +158,7 @@ curses_block(boolean noscroll) /* 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)) && ((rp = index(resp,(char)ret)) == 0)) - ; + 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) { @@ -176,8 +171,6 @@ curses_block(boolean noscroll) } wrefresh(win); } - if (rp) - ret = trans[(rp - resp)]; return ret; } From 816a7dd87151525e0e6b71b6fd024fe25de24e6d Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 17 Nov 2018 21:30:01 -0500 Subject: [PATCH 08/15] Revert "curses port: accept return on Windows platform" This reverts commit ac367ef4cc2cbad036964402ba541f869e6c8dd9. --- win/curses/cursmesg.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/win/curses/cursmesg.c b/win/curses/cursmesg.c index 67986bbbb..9fc1d6442 100644 --- a/win/curses/cursmesg.c +++ b/win/curses/cursmesg.c @@ -132,11 +132,6 @@ curses_message_win_puts(const char *message, boolean recursed) wrefresh(win); } -#ifdef WIN32 -#define XTRA_RESP "\r" -#else -#define XTRA_RESP "" -#endif int curses_block(boolean noscroll) @@ -145,7 +140,8 @@ curses_block(boolean noscroll) { int height, width, ret; WINDOW *win = curses_get_nhwin(MESSAGE_WIN); - char *resp = " \n\033" XTRA_RESP; /* space, enter, esc */ + char *resp = " \n\033"; /* space, enter, esc */ + curses_get_window_size(MESSAGE_WIN, &height, &width); curses_toggle_color_attr(win, MORECOLOR, NONE, ON); From 6ba270563c70bd4273e19db50be976c7a2a73388 Mon Sep 17 00:00:00 2001 From: Bart House Date: Sat, 17 Nov 2018 23:56:31 -0800 Subject: [PATCH 09/15] Build curses if PDCurses is available. There are two ways to enable curses in the build. Either set the environment variable PDCURSES to a folder containing a PDCurses repository OR place the PDCurses folder alongside the NetHack source repository. --- win/win32/vs2017/NetHack.vcxproj | 46 ++++++++++++++------------------ win/win32/vs2017/dirs.props | 3 +++ 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/win/win32/vs2017/NetHack.vcxproj b/win/win32/vs2017/NetHack.vcxproj index 981bc1e45..e51474a65 100644 --- a/win/win32/vs2017/NetHack.vcxproj +++ b/win/win32/vs2017/NetHack.vcxproj @@ -16,18 +16,16 @@ $(BinDir) - - $(PDCURSES);$(IncludePath) - - - $(PDCURSES);$(IncludePath) - - - $(PDCURSES);$(IncludePath) - - - $(PDCURSES);$(IncludePath) - + + + $(PDCURSES);%(AdditionalIncludeDirectories) + CURSES_GRAPHICS;%(PreprocessorDefinitions) + + + $(PDCURSES)\bin\$(Platform)\$(Configuration)\ + PDCurses.lib;%(AdditionalDependencies) + + /Gs /Oi- %(AdditionalOptions) @@ -36,14 +34,10 @@ Speed true $(WinWin32Dir);$(IncDir);$(SysWinntDir);$(SysShareDir);$(WinShareDir);%(AdditionalIncludeDirectories) - TILES;WIN32CON;DLB;MSWIN_GRAPHICS;CURSES_GRAPHICS;_LIB;%(PreprocessorDefinitions) + TILES;WIN32CON;DLB;MSWIN_GRAPHICS;_LIB;%(PreprocessorDefinitions) - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;winmm.lib;Winmm.lib;PDCurses.lib;%(AdditionalDependencies) - $(PDCURSES)\bin\$(Platform)\$(Configuration)\ - $(PDCURSES)\bin\$(Platform)\$(Configuration)\ - $(PDCURSES)\bin\$(Platform)\$(Configuration)\ - $(PDCURSES)\bin\$(Platform)\$(Configuration)\ + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;winmm.lib;Winmm.lib;%(AdditionalDependencies) @@ -170,14 +164,14 @@ - - - - - - - - + + + + + + + + diff --git a/win/win32/vs2017/dirs.props b/win/win32/vs2017/dirs.props index e1c36b8a1..0ebb31a25 100644 --- a/win/win32/vs2017/dirs.props +++ b/win/win32/vs2017/dirs.props @@ -22,4 +22,7 @@ $(ObjDir) $(RootDir)win\curses\ + + $(RootDir)..\PDCurses\ + From 9c4de50c9a67d005f89193ee008397c7bf0dcbc9 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 18 Nov 2018 06:24:32 -0500 Subject: [PATCH 10/15] windows VS Makefile with curses and PDCurses --- sys/winnt/Makefile.msc | 103 +++++++++++++++++++++++++++++++++-------- 1 file changed, 83 insertions(+), 20 deletions(-) diff --git a/sys/winnt/Makefile.msc b/sys/winnt/Makefile.msc index 7f908c637..c0773c035 100644 --- a/sys/winnt/Makefile.msc +++ b/sys/winnt/Makefile.msc @@ -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? # #----------------------------------------------------------------------------------------- #========================================================================================= @@ -64,15 +65,16 @@ DEBUGINFO = Y #--------------------------------------------------------------- # OPTIONAL - Curses window port support # -# 4. Uncomment these and set them appropriate if you want to +# 4. Uncomment these and set them appropriately if you want to # include curses port support alongside TTY support in your -# console binary. You'll have to set CURSESINCL to the location -# of your curses header (.h) files and CURSESDLL to the location -# of your pdcurses.dll. +# 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 -CURSESINCL=..\..\pdcurses -CURSESLIB=..\..\pdcurses\wincon\pdcurses.lib +PDCURSES_TOP=..\..\pdcurses # #============================================================================== # This marks the end of the BUILD DECISIONS section. @@ -98,18 +100,20 @@ CURSESLIB=..\..\pdcurses\wincon\pdcurses.lib # 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) -CURSES = ..\win\curses # window port files (curses) -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 +! IF ("$(ADD_CURSES)"=="Y") +PDCURSES = # # Object directory. # @@ -481,6 +485,38 @@ 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) +PANEL_HEADER = $(PDCURSES_TOP)\panel.h +TERM_HEADER = $(PDCURSES_TOP)\term.h +PDCSRC = $(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 +PDCOSDIR = $(PDCURSES_SRCDIR)/wincon + +PDCURSES_WIN_H = $(PDCOSDIR)/pdcwin.h + +PDCLIB = $(O)\pdcurses.lib + +!ELSE +PDCLIB = +!ENDIF + #========================================== # Header file macros #========================================== @@ -602,6 +638,16 @@ DATABASE = $(DAT)\data.base {$(MSWIN)}.c{$(OBJ)}.o: @$(cc) $(cflagsBuild) -Fo$@ $< +#========================================== +# Rules for files in PDCurses +#========================================== + +{$(PDCURSES_TOP)}.c{$(OBJ)}.o: + @$(cc) $(cflagsBuild) -Fo$@ $< + +{$(PDCSRC)}.c{$(OBJ)}.o: + @$(cc) $(cflagsBuild) -Fo$@ $< + #========================================== #=============== TARGETS ================== #========================================== @@ -757,7 +803,7 @@ $(GAMEDIR)\NetHack.exe : $(O)gamedir.tag $(O)tile.o $(O)nttty.o $(O)guistub.o \ @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 + $(PDCLIB) $(LIBS) $(conlibs) -out:$@ @<<$(@B).lnk $(GAMEOBJ) $(TTYOBJ) $(O)nttty.o @@ -1213,6 +1259,12 @@ $(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 +#========================================== + +$(PDCLIB) : $(PDCLIBOBJS) + #========================================== # Housekeeping #========================================== @@ -1428,6 +1480,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: From bbe7029e773d5637dd2facaddb2d3ff9095497e8 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 18 Nov 2018 06:48:47 -0500 Subject: [PATCH 11/15] more curses Makfile mods --- sys/winnt/Makefile.msc | 273 ++++++++++++++++++++--------------------- 1 file changed, 136 insertions(+), 137 deletions(-) diff --git a/sys/winnt/Makefile.msc b/sys/winnt/Makefile.msc index c0773c035..49b1aa590 100644 --- a/sys/winnt/Makefile.msc +++ b/sys/winnt/Makefile.msc @@ -112,8 +112,6 @@ MSWIN = ..\win\win32 # window port files (win32) WCURSES = ..\win\curses # window port files (curses) WSHR = ..\win\share # Tile support files -! IF ("$(ADD_CURSES)"=="Y") -PDCURSES = # # Object directory. # @@ -219,17 +217,6 @@ VSVER=2999 #untested future version !include ! ENDIF -#---------------------------------------------------------------- - -!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 - #These will be in the environment variables with one of the VS2017 #developer command prompts. #VSCMD_ARG_HOST_ARCH=x64 @@ -259,100 +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" $(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 #========================================== #================ MACROS ================== @@ -492,10 +385,8 @@ OPTIONS_FILE = $(DAT)\options PDCURSES_CURSES_H = $(PDCURSES_TOP)\curses.h PDCURSES_CURSPRIV_H = $(PDCURSES_TOP)\curspriv.h PDCURSES_HEADERS = $(PDCURSES_CURSES_H) $(PDCURSES_CURSPRIV_H) -PANEL_HEADER = $(PDCURSES_TOP)\panel.h -TERM_HEADER = $(PDCURSES_TOP)\term.h -PDCSRC = $(PDCURSES_TOP)\wincon - +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 \ @@ -507,12 +398,11 @@ PDCLIBOBJS = $(O)addch.o $(O)addchstr.o $(O)addstr.o $(O)attr.o $(O)beep.o PDCOBJS = $(O)pdcclip.o $(O)pdcdisp.o $(O)pdcgetsc.o $(O)pdckbd.o $(O)pdcscrn.o \ $(O)pdcsetsc.o $(O)pdcutil.o -PDCOSDIR = $(PDCURSES_SRCDIR)/wincon - -PDCURSES_WIN_H = $(PDCOSDIR)/pdcwin.h PDCLIB = $(O)\pdcurses.lib +PDCINCL = /I$(PDCURSES_TOP) /I$(PDCSRC) /I$(PDCURSES_TOP)\pdcurses + !ELSE PDCLIB = !ENDIF @@ -554,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 ================== #========================================== @@ -597,19 +595,6 @@ DATABASE = $(DAT)\data.base {$(UTIL)}.c{$(OBJ)}.o: @$(cc) $(cflagsBuild) -Fo$@ $< -#========================================== -# Rules for files in win\curses -#========================================== - -{$(CURSES)}.c{$(OBJ)}.o: - @$(cc) $(cflagsBuild) -Fo$@ $< - -{$(CURSES)}.h{$(INCL)}.h: - @copy $< $@ - -#{$(CURSES)}.txt{$(DAT)}.txt: -# @copy $< $@ - #========================================== # Rules for files in win\share #========================================== @@ -638,15 +623,28 @@ 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) $(cflagsBuild) -Fo$@ $< + @$(cc) $(PDCINCL) $(cflagsBuild) -Fo$@ $< {$(PDCSRC)}.c{$(OBJ)}.o: - @$(cc) $(cflagsBuild) -Fo$@ $< + @$(cc) $(PDCINCL) $(cflagsBuild) -Fo$@ $< + +{$(PDCWINCON)}.c{$(OBJ)}.o: + @$(cc) $(PDCINCL) $(cflagsBuild) -Fo$@ $< #========================================== #=============== TARGETS ================== @@ -798,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 \ - $(PDCLIB) $(LIBS) $(conlibs) -out:$@ @<<$(@B).lnk + $(LIBS) $(PDCLIB) $(conlibs) -out:$@ @<<$(@B).lnk $(GAMEOBJ) $(TTYOBJ) $(O)nttty.o @@ -826,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 @@ -1263,7 +1261,8 @@ $(O)til2bm32.o: $(WSHR)\tile2bmp.c $(HACK_H) $(TILE_H) $(MSWSYS)\win32api.h # PDCurses #========================================== -$(PDCLIB) : $(PDCLIBOBJS) +$(PDCLIB) : $(PDCLIBOBJS) $(PDCOBJS) + lib -nologo /out:$@ $(PDCLIBOBJS) $(PDCOBJS) #========================================== # Housekeeping @@ -1539,9 +1538,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 \ @@ -1739,7 +1738,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) From 935fe5bca831f304e60b525ec03c3e46cb6c44e0 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 18 Nov 2018 06:57:17 -0500 Subject: [PATCH 12/15] Makefile macro correction --- sys/winnt/Makefile.msc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sys/winnt/Makefile.msc b/sys/winnt/Makefile.msc index 49b1aa590..1f7f5fed6 100644 --- a/sys/winnt/Makefile.msc +++ b/sys/winnt/Makefile.msc @@ -401,7 +401,7 @@ PDCOBJS = $(O)pdcclip.o $(O)pdcdisp.o $(O)pdcgetsc.o $(O)pdckbd.o $(O)pdcscrn.o PDCLIB = $(O)\pdcurses.lib -PDCINCL = /I$(PDCURSES_TOP) /I$(PDCSRC) /I$(PDCURSES_TOP)\pdcurses +PDCINCL = /I$(PDCURSES_TOP) /I$(PDCSRC) /I$(PDCWINCON) !ELSE PDCLIB = @@ -1348,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 $(U)pdcurses.lib del $(O)pdcurses.lib +!ENDIF clean: if exist $(O)*.o del $(O)*.o if exist $(O)utility.tag del $(O)utility.tag From bf0e0b9f1281152ee5e551f8da0bad37043430fb Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 18 Nov 2018 07:10:58 -0500 Subject: [PATCH 13/15] Makefile cut-and-paste error --- sys/winnt/Makefile.msc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/winnt/Makefile.msc b/sys/winnt/Makefile.msc index 1f7f5fed6..28a7bae48 100644 --- a/sys/winnt/Makefile.msc +++ b/sys/winnt/Makefile.msc @@ -1349,7 +1349,7 @@ spotless: clean 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 $(U)pdcurses.lib del $(O)pdcurses.lib + if exist $(O)pdcurses.lib del $(O)pdcurses.lib !ENDIF clean: if exist $(O)*.o del $(O)*.o From 1b36af5d8aee7e501678ab64a6b725d825614cf1 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 18 Nov 2018 07:59:59 -0500 Subject: [PATCH 14/15] update Install.nt --- sys/winnt/Install.nt | 75 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 16 deletions(-) diff --git a/sys/winnt/Install.nt b/sys/winnt/Install.nt index 9d8fd01e0..91446103e 100644 --- a/sys/winnt/Install.nt +++ b/sys/winnt/Install.nt @@ -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". + + +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: From 60c3c2f1130bdaa27483524e0d22ec38faa1fc43 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 18 Nov 2018 08:34:24 -0500 Subject: [PATCH 15/15] fix a build error when curses is NOT enabled Fix an error when curses build macros are not defined makefile(1262) : fatal error U1083: target macro '$(PDCLIB)' expands to nothing --- sys/winnt/Makefile.msc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sys/winnt/Makefile.msc b/sys/winnt/Makefile.msc index 28a7bae48..926eed8ad 100644 --- a/sys/winnt/Makefile.msc +++ b/sys/winnt/Makefile.msc @@ -73,8 +73,8 @@ DEBUGINFO = Y # PDCurses header (.h) files and PDCURSES_C to the location # of your PDCurses C files. # -ADD_CURSES=Y -PDCURSES_TOP=..\..\pdcurses +#ADD_CURSES=Y +#PDCURSES_TOP=..\..\pdcurses # #============================================================================== # This marks the end of the BUILD DECISIONS section. @@ -1261,7 +1261,7 @@ $(O)til2bm32.o: $(WSHR)\tile2bmp.c $(HACK_H) $(TILE_H) $(MSWSYS)\win32api.h # PDCurses #========================================== -$(PDCLIB) : $(PDCLIBOBJS) $(PDCOBJS) +$(O)\pdcurses.lib : $(PDCLIBOBJS) $(PDCOBJS) lib -nologo /out:$@ $(PDCLIBOBJS) $(PDCOBJS) #==========================================