curses new file additions
This commit is contained in:
13
win/curses/Bugs.txt
Normal file
13
win/curses/Bugs.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
Here is a list of known issues with the curses interface at the time of
|
||||
this writing. Send any others you discover to me (Karl Garrison) at
|
||||
kgarrison@obox.com, along with how to reproduce the problem, if
|
||||
possible. Missing features are listed in the file Todo.txt.
|
||||
|
||||
* Resizing a window to 80 columns or less causes a crash (PDCurses for
|
||||
SDL and X11 only). Windows starting at this size and below do not
|
||||
cause a crash, however.
|
||||
|
||||
* Cursor position is wrong on map for smaller terminal windows in all
|
||||
versions of PDCurses (smaller than 80 width or 24 height). This is
|
||||
due to an incomplete workaround for an issue with the wmove() function
|
||||
in PDCurses.
|
||||
116
win/curses/Readme.txt
Normal file
116
win/curses/Readme.txt
Normal file
@@ -0,0 +1,116 @@
|
||||
INTRO
|
||||
=====
|
||||
|
||||
The "curses" windowport is a new text-based interface for NetHack,
|
||||
using high-level curses routines to control the display. Currently, it
|
||||
has been compiled and tested on Linux and Windows, but it should also
|
||||
be portable to a number of other systems, such as other forms of UNIX,
|
||||
Mac OS X, MSDOS, and OS/2.
|
||||
|
||||
Some features of this interface compared to the traditional tty
|
||||
interface include:
|
||||
|
||||
* Dynamic window resizing (e.g. maximizing a terminal window)
|
||||
* Dynamic configurable placement of status and message windows,
|
||||
relative to the map
|
||||
* Makes better use of larger terminal windows
|
||||
* Fancier display (e.g. window borders, optional popup dialogs)
|
||||
* "cursesgraphics" option for fancier line-drawing characters for
|
||||
drawing the dungeon - this should work on most terminals/platforms
|
||||
|
||||
|
||||
BUILDING
|
||||
========
|
||||
|
||||
As of this writing code has been compiled on Linux and Windows.
|
||||
|
||||
UNIX/Linux build instructions: Follow the instructions in
|
||||
sys/unix/Install.unx. By default, the Makefile is setup to compile
|
||||
against ncurses. Edit Makefile.src if you wish to compile against a
|
||||
different curses library, such as PDCurses for SDL.
|
||||
|
||||
Windows build instructions: If you are using Mingw32 as your compiler,
|
||||
then follow the instructions in sys/winnt/Install.nt with the following
|
||||
changes:
|
||||
|
||||
* After running nhsetup, manually copy the file cursmake.gcc to the
|
||||
src/ subdirectory
|
||||
* Instead of typing "mingw32-make -f Makefile.gcc install" you will
|
||||
type "mingw32-make -f cursmake.gcc install"
|
||||
|
||||
If you are using a different compiler, you will have to manually modify
|
||||
the appropriate Makefile to include the curses windowport files.
|
||||
|
||||
|
||||
GAMEPLAY
|
||||
========
|
||||
|
||||
Gameplay should be similar to the tty interface for NetHack; the
|
||||
differences are primarily visual. This windowport supports dymanic
|
||||
resizing of the terminal window, so you can play with it to see how it
|
||||
looks best to you during a game. Also, the align_status and
|
||||
align_message options may be set during the game, so you can experiment
|
||||
to see what arraingement looks best to you.
|
||||
|
||||
For menus, in addition to the normal configurable keybindings for menu
|
||||
navigation descrived in the Guidebook, you can use the right and left
|
||||
arrows to to forward or backward one page, respectively, and the home
|
||||
and end keys to go to the first and last pages, respectively.
|
||||
|
||||
Some configuration options that are specific to or relevant to the
|
||||
curses windowport are shown below. Copy any of these that you like to
|
||||
your nethack configuration file (e.g. .nethackrc for UNIX or
|
||||
NetHack.cnf for Windows):
|
||||
#
|
||||
# Use this if the binary was compiled with multiple window interfaces,
|
||||
# and curses is not the default
|
||||
OPTIONS=windowtype:curses
|
||||
#
|
||||
# Set this for Windows systems, or for PDCurses for SDL on any system.
|
||||
# The latter uses a cp437 font, which works with this option
|
||||
#OPTIONS=IBMgraphics
|
||||
#
|
||||
# Set this if IBMgraphics above won't work for your system. Mutually
|
||||
# exclusive with the above option, and should work on nearly any
|
||||
# system.
|
||||
OPTIONS=cursesgraphics
|
||||
#
|
||||
# Optionally specify the alignment of the message and status windows
|
||||
# relative to the map window. If not specified, the code will default
|
||||
# to the locations used in the tty interface: message window on top,
|
||||
# and status window on bottom. Placing either of these on the right or
|
||||
# left really only works well for winder terminal windows.
|
||||
OPTIONS=align_message:bottom,align_status:right
|
||||
#
|
||||
# Use a small popup "window" for short prompts, e.g. "Really save?".
|
||||
# If this is not set, the message window will be used for these as is
|
||||
# done for the tty interface.
|
||||
OPTIONS=popup_dialog
|
||||
#
|
||||
# Specify the initial window size for NetHack in units of characters.
|
||||
# This is supported on PDCurses for SDL as well as PDCurses for
|
||||
# Windows.
|
||||
OPTIONS=term_cols:110,term_rows:32
|
||||
#
|
||||
# Controls the usage of window borders for the main NetHack windows
|
||||
# (message, map, and status windows). A value of 1 forces the borders
|
||||
# to be drawn, a value of 2 forces them to be off, and a value of 3
|
||||
# allows the code to decide if they should be drawn based on the size
|
||||
# of the terminal window.
|
||||
OPTIONS=windowborders:3
|
||||
|
||||
|
||||
CONTACT
|
||||
=======
|
||||
|
||||
Please send any bug reports, suggestions, patches, or miscellaneous
|
||||
feedback to me (Karl Garrison) at: kgarrison@pobox.com. Note that as
|
||||
of this writing, I only have sporatic Internet access, so I may not get
|
||||
back to you right away.
|
||||
|
||||
Happy Hacking!
|
||||
|
||||
Karl Garrison
|
||||
March, 2009
|
||||
|
||||
|
||||
146
win/curses/Todo.txt
Normal file
146
win/curses/Todo.txt
Normal file
@@ -0,0 +1,146 @@
|
||||
Below are some things I would like to see
|
||||
|
||||
NETHACK INTERFACE
|
||||
=================
|
||||
|
||||
(These are the functions in cursmain.c called by the core NetHack code)
|
||||
|
||||
* Implement curses_rip for optional fancier color tombstone, as well
|
||||
as one that will display correctly on smaller terminals.
|
||||
|
||||
* I am confused as to how mark_synch, wait_synch, and delay_output
|
||||
should work. Help, please?
|
||||
|
||||
* Both PDCurses and Ncurses have mouse support, so the poskey function
|
||||
could probably be implemented easily enough.
|
||||
|
||||
* raw_print is supposed to be able to work before the windowing system
|
||||
has been initialized, as well as after, so I am unsure if curses
|
||||
functions should be used here. Maybe check to see if initscr() has
|
||||
been called, and use curses functions if so, and call initscr() from
|
||||
there is not? Right now it is just a call to puts() with no support
|
||||
for bold text.
|
||||
|
||||
|
||||
DISPLAY
|
||||
=======
|
||||
|
||||
* Consolidate refreshes of the display for smoother output.
|
||||
|
||||
* Horizontal scrollbar to show position for displays < 80 columns.
|
||||
|
||||
* Calls to getch() should probably be turned into wgetch() for the
|
||||
appropriate window. This causes quirty cursor behavior under
|
||||
PDCurses, however.
|
||||
|
||||
* Animation effects do not display properly - this could probably be
|
||||
fixed with a correct implementation of the delay_output function.
|
||||
|
||||
* Support option to set forground and background colors for individual
|
||||
windows
|
||||
|
||||
|
||||
MENUS
|
||||
=====
|
||||
|
||||
(cursdial.c)
|
||||
|
||||
* Menus need to be able to accept a count as input, e.g. to specifiy
|
||||
how many items to drop.
|
||||
|
||||
* Currently the "preselected" flag for an individual menu item is
|
||||
ignored. This should eventually be implemented.
|
||||
|
||||
* Menus probably should never overlap with the message window, since
|
||||
the user sometmes needs to be able to see the messages while the menu
|
||||
is active, e.g. when identifying multiple items one at a time.
|
||||
|
||||
* Perhaps allow for keyboard navigation of individual items, e.g.
|
||||
using the up and down arrows to move among the selectable items, and
|
||||
selecting individual items with the spacebar. Perhaps the tab key
|
||||
could jump to the first selectable item after the next heading, and
|
||||
shift-tab could jump to the first item of the previous heading.
|
||||
|
||||
|
||||
MESSAGE WINDOW
|
||||
==============
|
||||
|
||||
(cursmesg.c)
|
||||
|
||||
* Hitting Esc at the more prompt (which is '>>' for the curses
|
||||
interface) should suppress the display of any further messages for
|
||||
that turn like the tty interface does.
|
||||
|
||||
|
||||
MAP WINDOW
|
||||
==========
|
||||
|
||||
(curswins.c)
|
||||
|
||||
* The map window would probably benefit from a total redesign. Right
|
||||
now, it uses a pad instead of a regular curses window, which causes a
|
||||
number of special cases in the code to account for it, and a seperate
|
||||
window behind it just to draw the border. It feels kludgy and
|
||||
annoying!
|
||||
|
||||
|
||||
STATUS WINDOW
|
||||
=============
|
||||
|
||||
(cursstat.c)
|
||||
|
||||
* If the status window is on the right or left, then we have much more
|
||||
room to work with for each item horizontally. Expand out some of the
|
||||
labels for clarity. We can also list the current dungeon (e.g.
|
||||
Gnomish Mines) and perhaps show thermometer bars for hit points and
|
||||
magical power.
|
||||
|
||||
* Conversely, if we have a narrower dislay, compress some of the
|
||||
labels to save space, and do not display some items that never or
|
||||
rarely change (e.g. name, level and title, and alignment). Perhaps
|
||||
display changes to these fields in the message window if they do
|
||||
happen to change (e.g. converting to a new alignment).
|
||||
|
||||
* Maybe add some configuration options for what colors are used and
|
||||
the like.
|
||||
|
||||
OTHER DIALOGS
|
||||
=============
|
||||
|
||||
(cursdial.c)
|
||||
|
||||
* curses_yn_function needs to accept a count if a '#' is present in
|
||||
choices string.
|
||||
|
||||
* Extended commands should be enterable letter-by-letter via a '#'
|
||||
prompt if user does not have the extmenu command set to TRUE.
|
||||
|
||||
* Character selection should allow for a random selection of any or
|
||||
all choices.
|
||||
|
||||
|
||||
OTHER PLATFORMS
|
||||
===============
|
||||
|
||||
* PDCurses also work on DOS and OS/2. PDCurses for SDL and ncurses
|
||||
exist for Mac OS X. Porting the curses interface to these platforms
|
||||
should not be too difficult.
|
||||
|
||||
|
||||
MISC
|
||||
====
|
||||
|
||||
* Update documentation and in-game help to describe the newly-added
|
||||
options: cursesgraphics, term_rows, term_cols, and windowborders.
|
||||
|
||||
* Recognize "Alt" key in a platform-independant way to allow its use
|
||||
to select extended commands. Currently this works for PDCurses. For
|
||||
Ncurses, the Alt key works in an xterm or rxvt if the -meta8 flag is
|
||||
passed, but I'd like to see a general way of detecting it.
|
||||
|
||||
* PDCurses has a function named "addrawch" to output the visual
|
||||
representation of a control character to the screen without having the
|
||||
control character affect the display otherwise. I would like to find
|
||||
a way to accomplish the same thing via Ncurses to e.g. be able to use
|
||||
a font like nh10 with the correct symbol mappings in an xterm or the
|
||||
like.
|
||||
1387
win/curses/cursdial.c
Normal file
1387
win/curses/cursdial.c
Normal file
File diff suppressed because it is too large
Load Diff
23
win/curses/cursdial.h
Normal file
23
win/curses/cursdial.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
|
||||
|
||||
#ifndef CURSDIAL_H
|
||||
# define CURSDIAL_H
|
||||
|
||||
/* Global declarations */
|
||||
|
||||
void curses_line_input_dialog(const char *prompt, char *answer, int buffer);
|
||||
int curses_character_input_dialog(const char *prompt, const char *choices,
|
||||
CHAR_P def);
|
||||
int curses_ext_cmd(void);
|
||||
void curses_create_nhmenu(winid wid);
|
||||
void curses_add_nhmenu_item(winid wid, int glyph, const ANY_P * identifier,
|
||||
CHAR_P accelerator, CHAR_P group_accel, int attr,
|
||||
const char *str, BOOLEAN_P presel);
|
||||
void curses_finalize_nhmenu(winid wid, const char *prompt);
|
||||
int curses_display_nhmenu(winid wid, int how, MENU_ITEM_P ** _selected);
|
||||
boolean curses_menu_exists(winid wid);
|
||||
void curses_del_menu(winid wid);
|
||||
|
||||
|
||||
|
||||
#endif /* CURSDIAL_H */
|
||||
923
win/curses/cursinit.c
Normal file
923
win/curses/cursinit.c
Normal file
@@ -0,0 +1,923 @@
|
||||
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
|
||||
|
||||
#include "curses.h"
|
||||
#include "hack.h"
|
||||
#include "wincurs.h"
|
||||
#include "cursinit.h"
|
||||
#include "patchlevel.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
/* Initialization and startup functions for curses interface */
|
||||
|
||||
/* Private declarations */
|
||||
|
||||
static void set_window_position(int *, int *, int *, int *, int,
|
||||
int *, int *, int *, int *, int,
|
||||
int, int);
|
||||
|
||||
/* array to save initial terminal colors for later restoration */
|
||||
|
||||
typedef struct nhrgb_type {
|
||||
short r;
|
||||
short g;
|
||||
short b;
|
||||
} nhrgb;
|
||||
|
||||
nhrgb orig_yellow;
|
||||
nhrgb orig_white;
|
||||
nhrgb orig_darkgray;
|
||||
nhrgb orig_hired;
|
||||
nhrgb orig_higreen;
|
||||
nhrgb orig_hiyellow;
|
||||
nhrgb orig_hiblue;
|
||||
nhrgb orig_himagenta;
|
||||
nhrgb orig_hicyan;
|
||||
nhrgb orig_hiwhite;
|
||||
|
||||
/* Banners used for an optional ASCII splash screen */
|
||||
|
||||
#define NETHACK_SPLASH_A \
|
||||
" _ _ _ _ _ _ "
|
||||
|
||||
#define NETHACK_SPLASH_B \
|
||||
"| \\ | | | | | | | | | | "
|
||||
|
||||
#define NETHACK_SPLASH_C \
|
||||
"| \\| | ___ | |_ | |__| | __ _ ___ | | __"
|
||||
|
||||
#define NETHACK_SPLASH_D \
|
||||
"| . ` | / _ \\| __|| __ | / _` | / __|| |/ /"
|
||||
|
||||
#define NETHACK_SPLASH_E \
|
||||
"| |\\ || __/| |_ | | | || (_| || (__ | < "
|
||||
|
||||
#define NETHACK_SPLASH_F \
|
||||
"|_| \\_| \\___| \\__||_| |_| \\__,_| \\___||_|\\_\\"
|
||||
|
||||
|
||||
/* win* is size and placement of window to change, x/y/w/h is baseline which can
|
||||
decrease depending on alignment of win* in orientation.
|
||||
Negative minh/minw: as much as possible, but at least as much as specified. */
|
||||
static void
|
||||
set_window_position(int *winx, int *winy, int *winw, int *winh, int orientation,
|
||||
int *x, int *y, int *w, int *h, int border_space,
|
||||
int minh, int minw)
|
||||
{
|
||||
*winw = *w;
|
||||
*winh = *h;
|
||||
|
||||
/* Set window height/width */
|
||||
if (orientation == ALIGN_TOP || orientation == ALIGN_BOTTOM) {
|
||||
if (minh < 0) {
|
||||
*winh = (*h - ROWNO - border_space);
|
||||
if (-minh > *winh)
|
||||
*winh = -minh;
|
||||
} else
|
||||
*winh = minh;
|
||||
*h -= (*winh + border_space);
|
||||
} else {
|
||||
if (minw < 0) {
|
||||
*winw = (*w - COLNO - border_space);
|
||||
if (-minw > *winw)
|
||||
*winw = -minw;
|
||||
} else
|
||||
*winw = minw;
|
||||
*w -= (*winw + border_space);
|
||||
}
|
||||
|
||||
*winx = *w + border_space + *x;
|
||||
*winy = *h + border_space + *y;
|
||||
|
||||
/* Set window position */
|
||||
if (orientation != ALIGN_RIGHT) {
|
||||
*winx = *x;
|
||||
if (orientation == ALIGN_LEFT)
|
||||
*x += *winw + border_space;
|
||||
}
|
||||
if (orientation != ALIGN_BOTTOM) {
|
||||
*winy = *y;
|
||||
if (orientation == ALIGN_TOP)
|
||||
*y += *winh + border_space;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create the "main" nonvolitile windows used by nethack */
|
||||
|
||||
void
|
||||
curses_create_main_windows()
|
||||
{
|
||||
int min_message_height = 1;
|
||||
int message_orientation = 0;
|
||||
int status_orientation = 0;
|
||||
int border_space = 0;
|
||||
int hspace = term_cols - 80;
|
||||
boolean borders = FALSE;
|
||||
|
||||
switch (iflags.wc2_windowborders) {
|
||||
case 1: /* On */
|
||||
borders = TRUE;
|
||||
break;
|
||||
case 2: /* Off */
|
||||
borders = FALSE;
|
||||
break;
|
||||
case 3: /* Auto */
|
||||
if ((term_cols > 81) && (term_rows > 25)) {
|
||||
borders = TRUE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
borders = FALSE;
|
||||
}
|
||||
|
||||
|
||||
if (borders) {
|
||||
border_space = 2;
|
||||
hspace -= border_space;
|
||||
}
|
||||
|
||||
if ((term_cols - border_space) < COLNO) {
|
||||
min_message_height++;
|
||||
}
|
||||
|
||||
/* Determine status window orientation */
|
||||
if (!iflags.wc_align_status || (iflags.wc_align_status == ALIGN_TOP)
|
||||
|| (iflags.wc_align_status == ALIGN_BOTTOM)) {
|
||||
if (!iflags.wc_align_status) {
|
||||
iflags.wc_align_status = ALIGN_BOTTOM;
|
||||
}
|
||||
status_orientation = iflags.wc_align_status;
|
||||
} else { /* left or right alignment */
|
||||
|
||||
/* Max space for player name and title horizontally */
|
||||
if ((hspace >= 26) && (term_rows >= 24)) {
|
||||
status_orientation = iflags.wc_align_status;
|
||||
hspace -= (26 + border_space);
|
||||
} else {
|
||||
status_orientation = ALIGN_BOTTOM;
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine message window orientation */
|
||||
if (!iflags.wc_align_message || (iflags.wc_align_message == ALIGN_TOP)
|
||||
|| (iflags.wc_align_message == ALIGN_BOTTOM)) {
|
||||
if (!iflags.wc_align_message) {
|
||||
iflags.wc_align_message = ALIGN_TOP;
|
||||
}
|
||||
message_orientation = iflags.wc_align_message;
|
||||
} else { /* left or right alignment */
|
||||
|
||||
if ((hspace - border_space) >= 25) { /* Arbitrary */
|
||||
message_orientation = iflags.wc_align_message;
|
||||
} else {
|
||||
message_orientation = ALIGN_TOP;
|
||||
}
|
||||
}
|
||||
|
||||
/* Figure out window positions and placements. Status and message area can be aligned
|
||||
based on configuration. The priority alignment-wise is: status > msgarea > game.
|
||||
Define everything as taking as much space as possible and shrink/move based on
|
||||
alignment positions. */
|
||||
int message_x = 0;
|
||||
int message_y = 0;
|
||||
int status_x = 0;
|
||||
int status_y = 0;
|
||||
int inv_x = 0;
|
||||
int inv_y = 0;
|
||||
int map_x = 0;
|
||||
int map_y = 0;
|
||||
|
||||
int message_height = 0;
|
||||
int message_width = 0;
|
||||
int status_height = 0;
|
||||
int status_width = 0;
|
||||
int inv_height = 0;
|
||||
int inv_width = 0;
|
||||
int map_height = (term_rows - border_space);
|
||||
int map_width = (term_cols - border_space);
|
||||
|
||||
boolean status_vertical = FALSE;
|
||||
boolean msg_vertical = FALSE;
|
||||
if (status_orientation == ALIGN_LEFT ||
|
||||
status_orientation == ALIGN_RIGHT)
|
||||
status_vertical = TRUE;
|
||||
if (message_orientation == ALIGN_LEFT ||
|
||||
message_orientation == ALIGN_RIGHT)
|
||||
msg_vertical = TRUE;
|
||||
|
||||
int statusheight = 3;
|
||||
if (iflags.statuslines < 3)
|
||||
statusheight = 2;
|
||||
|
||||
/* Vertical windows have priority. Otherwise, priotity is:
|
||||
status > inv > msg */
|
||||
if (status_vertical)
|
||||
set_window_position(&status_x, &status_y, &status_width, &status_height,
|
||||
status_orientation, &map_x, &map_y, &map_width, &map_height,
|
||||
border_space, statusheight, 26);
|
||||
|
||||
if (iflags.perm_invent) {
|
||||
/* Take up all width unless msgbar is also vertical. */
|
||||
int width = -25;
|
||||
if (msg_vertical)
|
||||
width = 25;
|
||||
|
||||
set_window_position(&inv_x, &inv_y, &inv_width, &inv_height,
|
||||
ALIGN_RIGHT, &map_x, &map_y, &map_width, &map_height,
|
||||
border_space, -1, width);
|
||||
}
|
||||
|
||||
if (msg_vertical)
|
||||
set_window_position(&message_x, &message_y, &message_width, &message_height,
|
||||
message_orientation, &map_x, &map_y, &map_width, &map_height,
|
||||
border_space, -1, -25);
|
||||
|
||||
/* Now draw horizontal windows */
|
||||
if (!status_vertical)
|
||||
set_window_position(&status_x, &status_y, &status_width, &status_height,
|
||||
status_orientation, &map_x, &map_y, &map_width, &map_height,
|
||||
border_space, statusheight, 26);
|
||||
|
||||
if (!msg_vertical)
|
||||
set_window_position(&message_x, &message_y, &message_width, &message_height,
|
||||
message_orientation, &map_x, &map_y, &map_width, &map_height,
|
||||
border_space, -1, -25);
|
||||
|
||||
if (map_width > COLNO)
|
||||
map_width = COLNO;
|
||||
|
||||
if (map_height > ROWNO)
|
||||
map_height = ROWNO;
|
||||
|
||||
if (curses_get_nhwin(STATUS_WIN)) {
|
||||
curses_del_nhwin(STATUS_WIN);
|
||||
curses_del_nhwin(MESSAGE_WIN);
|
||||
curses_del_nhwin(MAP_WIN);
|
||||
curses_del_nhwin(INV_WIN);
|
||||
|
||||
clear();
|
||||
}
|
||||
|
||||
curses_add_nhwin(STATUS_WIN, status_height, status_width, status_y,
|
||||
status_x, status_orientation, borders);
|
||||
|
||||
curses_add_nhwin(MESSAGE_WIN, message_height, message_width, message_y,
|
||||
message_x, message_orientation, borders);
|
||||
|
||||
if (iflags.perm_invent)
|
||||
curses_add_nhwin(INV_WIN, inv_height, inv_width, inv_y, inv_x,
|
||||
ALIGN_RIGHT, borders);
|
||||
|
||||
curses_add_nhwin(MAP_WIN, map_height, map_width, map_y, map_x, 0, borders);
|
||||
|
||||
refresh();
|
||||
|
||||
curses_refresh_nethack_windows();
|
||||
/*
|
||||
if (iflags.window_inited) {
|
||||
curses_update_stats();
|
||||
if (iflags.perm_invent)
|
||||
curses_update_inventory();
|
||||
} else {
|
||||
iflags.window_inited = TRUE;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
/* Initialize curses colors to colors used by NetHack */
|
||||
|
||||
void
|
||||
curses_init_nhcolors()
|
||||
{
|
||||
#ifdef TEXTCOLOR
|
||||
if (has_colors()) {
|
||||
use_default_colors();
|
||||
init_pair(1, COLOR_BLACK, -1);
|
||||
init_pair(2, COLOR_RED, -1);
|
||||
init_pair(3, COLOR_GREEN, -1);
|
||||
init_pair(4, COLOR_YELLOW, -1);
|
||||
init_pair(5, COLOR_BLUE, -1);
|
||||
init_pair(6, COLOR_MAGENTA, -1);
|
||||
init_pair(7, COLOR_CYAN, -1);
|
||||
init_pair(8, -1, -1);
|
||||
|
||||
{
|
||||
int i;
|
||||
|
||||
int clr_remap[16] = {
|
||||
COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW,
|
||||
COLOR_BLUE,
|
||||
COLOR_MAGENTA, COLOR_CYAN, -1, COLOR_WHITE,
|
||||
COLOR_RED + 8, COLOR_GREEN + 8, COLOR_YELLOW + 8,
|
||||
COLOR_BLUE + 8,
|
||||
COLOR_MAGENTA + 8, COLOR_CYAN + 8, COLOR_WHITE + 8
|
||||
};
|
||||
|
||||
for (i = 0; i < (COLORS >= 16 ? 16 : 8); i++) {
|
||||
init_pair(17 + (i * 2) + 0, clr_remap[i], COLOR_RED);
|
||||
init_pair(17 + (i * 2) + 1, clr_remap[i], COLOR_BLUE);
|
||||
}
|
||||
|
||||
boolean hicolor = FALSE;
|
||||
if (COLORS >= 16)
|
||||
hicolor = TRUE;
|
||||
|
||||
/* Work around the crazy definitions above for more background colors... */
|
||||
for (i = 0; i < (COLORS >= 16 ? 16 : 8); i++) {
|
||||
init_pair((hicolor ? 49 : 9) + i, clr_remap[i], COLOR_GREEN);
|
||||
init_pair((hicolor ? 65 : 33) + i, clr_remap[i], COLOR_YELLOW);
|
||||
init_pair((hicolor ? 81 : 41) + i, clr_remap[i], COLOR_MAGENTA);
|
||||
init_pair((hicolor ? 97 : 49) + i, clr_remap[i], COLOR_CYAN);
|
||||
init_pair((hicolor ? 113 : 57) + i, clr_remap[i], COLOR_WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (COLORS >= 16) {
|
||||
init_pair(9, COLOR_WHITE, -1);
|
||||
init_pair(10, COLOR_RED + 8, -1);
|
||||
init_pair(11, COLOR_GREEN + 8, -1);
|
||||
init_pair(12, COLOR_YELLOW + 8, -1);
|
||||
init_pair(13, COLOR_BLUE + 8, -1);
|
||||
init_pair(14, COLOR_MAGENTA + 8, -1);
|
||||
init_pair(15, COLOR_CYAN + 8, -1);
|
||||
init_pair(16, COLOR_WHITE + 8, -1);
|
||||
}
|
||||
|
||||
if (can_change_color()) {
|
||||
/* Preserve initial terminal colors */
|
||||
color_content(COLOR_YELLOW, &orig_yellow.r, &orig_yellow.g,
|
||||
&orig_yellow.b);
|
||||
color_content(COLOR_WHITE, &orig_white.r, &orig_white.g,
|
||||
&orig_white.b);
|
||||
|
||||
/* Set colors to appear as NetHack expects */
|
||||
init_color(COLOR_YELLOW, 500, 300, 0);
|
||||
init_color(COLOR_WHITE, 600, 600, 600);
|
||||
if (COLORS >= 16) {
|
||||
/* Preserve initial terminal colors */
|
||||
color_content(COLOR_RED + 8, &orig_hired.r,
|
||||
&orig_hired.g, &orig_hired.b);
|
||||
color_content(COLOR_GREEN + 8, &orig_higreen.r,
|
||||
&orig_higreen.g, &orig_higreen.b);
|
||||
color_content(COLOR_YELLOW + 8, &orig_hiyellow.r,
|
||||
&orig_hiyellow.g, &orig_hiyellow.b);
|
||||
color_content(COLOR_BLUE + 8, &orig_hiblue.r,
|
||||
&orig_hiblue.g, &orig_hiblue.b);
|
||||
color_content(COLOR_MAGENTA + 8, &orig_himagenta.r,
|
||||
&orig_himagenta.g, &orig_himagenta.b);
|
||||
color_content(COLOR_CYAN + 8, &orig_hicyan.r,
|
||||
&orig_hicyan.g, &orig_hicyan.b);
|
||||
color_content(COLOR_WHITE + 8, &orig_hiwhite.r,
|
||||
&orig_hiwhite.g, &orig_hiwhite.b);
|
||||
|
||||
/* Set colors to appear as NetHack expects */
|
||||
init_color(COLOR_RED + 8, 1000, 500, 0);
|
||||
init_color(COLOR_GREEN + 8, 0, 1000, 0);
|
||||
init_color(COLOR_YELLOW + 8, 1000, 1000, 0);
|
||||
init_color(COLOR_BLUE + 8, 0, 0, 1000);
|
||||
init_color(COLOR_MAGENTA + 8, 1000, 0, 1000);
|
||||
init_color(COLOR_CYAN + 8, 0, 1000, 1000);
|
||||
init_color(COLOR_WHITE + 8, 1000, 1000, 1000);
|
||||
# ifdef USE_DARKGRAY
|
||||
if (COLORS > 16) {
|
||||
color_content(CURSES_DARK_GRAY, &orig_darkgray.r,
|
||||
&orig_darkgray.g, &orig_darkgray.b);
|
||||
init_color(CURSES_DARK_GRAY, 300, 300, 300);
|
||||
/* just override black colorpair entry here */
|
||||
init_pair(1, CURSES_DARK_GRAY, -1);
|
||||
}
|
||||
# endif
|
||||
} else {
|
||||
/* Set flag to use bold for bright colors */
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* Allow player to pick character's role, race, gender, and alignment.
|
||||
Borrowed from the Gnome window port. */
|
||||
|
||||
void
|
||||
curses_choose_character()
|
||||
{
|
||||
int n, i, sel, count_off, pick4u;
|
||||
int count = 0;
|
||||
int cur_character = 0;
|
||||
const char **choices;
|
||||
int *pickmap;
|
||||
char *prompt;
|
||||
char pbuf[QBUFSZ];
|
||||
char choice[QBUFSZ];
|
||||
char tmpchoice[QBUFSZ];
|
||||
|
||||
prompt = build_plselection_prompt(pbuf, QBUFSZ, flags.initrole,
|
||||
flags.initrace, flags.initgend,
|
||||
flags.initalign);
|
||||
|
||||
/* This part is irritating: we have to strip the choices off of
|
||||
the string and put them in a separate string in order to use
|
||||
curses_character_input_dialog for this prompt. */
|
||||
|
||||
while (cur_character != '[') {
|
||||
cur_character = prompt[count];
|
||||
count++;
|
||||
}
|
||||
|
||||
count_off = count;
|
||||
|
||||
while (cur_character != ']') {
|
||||
tmpchoice[count - count_off] = prompt[count];
|
||||
count++;
|
||||
cur_character = prompt[count];
|
||||
}
|
||||
|
||||
tmpchoice[count - count_off] = '\0';
|
||||
lcase(tmpchoice);
|
||||
|
||||
while (!isspace(prompt[count_off])) {
|
||||
count_off--;
|
||||
}
|
||||
|
||||
prompt[count_off] = '\0';
|
||||
sprintf(choice, "%s%c", tmpchoice, '\033');
|
||||
if (strchr(tmpchoice, 't')) { /* Tutorial mode */
|
||||
mvaddstr(0, 1, "New? Press t to enter a tutorial.");
|
||||
}
|
||||
|
||||
/* Add capital letters as choices that aren't displayed */
|
||||
|
||||
for (count = 0; tmpchoice[count]; count++) {
|
||||
tmpchoice[count] = toupper(tmpchoice[count]);
|
||||
}
|
||||
|
||||
sprintf(choice, "%s%s", choice, tmpchoice);
|
||||
|
||||
/* prevent an unnecessary prompt */
|
||||
rigid_role_checks();
|
||||
|
||||
if (!flags.randomall &&
|
||||
(flags.initrole == ROLE_NONE || flags.initrace == ROLE_NONE ||
|
||||
flags.initgend == ROLE_NONE || flags.initalign == ROLE_NONE)) {
|
||||
pick4u = tolower(curses_character_input_dialog(prompt, choice, 'y'));
|
||||
} else {
|
||||
pick4u = 'y';
|
||||
}
|
||||
|
||||
if (pick4u == 'q') { /* Quit or cancelled */
|
||||
clearlocks();
|
||||
curses_bail(0);
|
||||
}
|
||||
|
||||
if (pick4u == 'y') {
|
||||
flags.randomall = TRUE;
|
||||
}
|
||||
|
||||
clear();
|
||||
refresh();
|
||||
|
||||
if (!flags.randomall && flags.initrole < 0) {
|
||||
/* select a role */
|
||||
for (n = 0; roles[n].name.m; n++)
|
||||
continue;
|
||||
choices = (const char **) alloc(sizeof (char *) * (n + 1));
|
||||
pickmap = (int *) alloc(sizeof (int) * (n + 1));
|
||||
for (;;) {
|
||||
for (n = 0, i = 0; roles[i].name.m; i++) {
|
||||
if (ok_role(i, flags.initrace, flags.initgend, flags.initalign)) {
|
||||
if (flags.initgend >= 0 && flags.female && roles[i].name.f)
|
||||
choices[n] = roles[i].name.f;
|
||||
else
|
||||
choices[n] = roles[i].name.m;
|
||||
pickmap[n++] = i;
|
||||
}
|
||||
}
|
||||
if (n > 0)
|
||||
break;
|
||||
else if (flags.initalign >= 0)
|
||||
flags.initalign = -1; /* reset */
|
||||
else if (flags.initgend >= 0)
|
||||
flags.initgend = -1;
|
||||
else if (flags.initrace >= 0)
|
||||
flags.initrace = -1;
|
||||
else
|
||||
panic("no available ROLE+race+gender+alignment combinations");
|
||||
}
|
||||
choices[n] = (const char *) 0;
|
||||
if (n > 1)
|
||||
sel =
|
||||
curses_character_dialog(choices,
|
||||
"Choose one of the following roles:");
|
||||
else
|
||||
sel = 0;
|
||||
if (sel >= 0)
|
||||
sel = pickmap[sel];
|
||||
else if (sel == ROLE_NONE) { /* Quit */
|
||||
clearlocks();
|
||||
curses_bail(0);
|
||||
}
|
||||
free((genericptr_t) choices);
|
||||
free((genericptr_t) pickmap);
|
||||
} else if (flags.initrole < 0)
|
||||
sel = ROLE_RANDOM;
|
||||
else
|
||||
sel = flags.initrole;
|
||||
|
||||
if (sel == ROLE_RANDOM) { /* Random role */
|
||||
sel = pick_role(flags.initrace, flags.initgend,
|
||||
flags.initalign, PICK_RANDOM);
|
||||
if (sel < 0)
|
||||
sel = randrole();
|
||||
}
|
||||
|
||||
flags.initrole = sel;
|
||||
|
||||
/* Select a race, if necessary */
|
||||
/* force compatibility with role, try for compatibility with
|
||||
* pre-selected gender/alignment */
|
||||
if (flags.initrace < 0 || !validrace(flags.initrole, flags.initrace)) {
|
||||
if (flags.initrace == ROLE_RANDOM || flags.randomall) {
|
||||
flags.initrace = pick_race(flags.initrole, flags.initgend,
|
||||
flags.initalign, PICK_RANDOM);
|
||||
if (flags.initrace < 0)
|
||||
flags.initrace = randrace(flags.initrole);
|
||||
} else {
|
||||
/* Count the number of valid races */
|
||||
n = 0; /* number valid */
|
||||
for (i = 0; races[i].noun; i++) {
|
||||
if (ok_race(flags.initrole, i, flags.initgend, flags.initalign))
|
||||
n++;
|
||||
}
|
||||
if (n == 0) {
|
||||
for (i = 0; races[i].noun; i++) {
|
||||
if (validrace(flags.initrole, i))
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
choices = (const char **) alloc(sizeof (char *) * (n + 1));
|
||||
pickmap = (int *) alloc(sizeof (int) * (n + 1));
|
||||
for (n = 0, i = 0; races[i].noun; i++) {
|
||||
if (ok_race(flags.initrole, i, flags.initgend, flags.initalign)) {
|
||||
choices[n] = races[i].noun;
|
||||
pickmap[n++] = i;
|
||||
}
|
||||
}
|
||||
choices[n] = (const char *) 0;
|
||||
/* Permit the user to pick, if there is more than one */
|
||||
if (n > 1)
|
||||
sel =
|
||||
curses_character_dialog(choices,
|
||||
"Choose one of the following races:");
|
||||
else
|
||||
sel = 0;
|
||||
if (sel >= 0)
|
||||
sel = pickmap[sel];
|
||||
else if (sel == ROLE_NONE) { /* Quit */
|
||||
clearlocks();
|
||||
curses_bail(0);
|
||||
}
|
||||
flags.initrace = sel;
|
||||
free((genericptr_t) choices);
|
||||
free((genericptr_t) pickmap);
|
||||
}
|
||||
if (flags.initrace == ROLE_RANDOM) { /* Random role */
|
||||
sel = pick_race(flags.initrole, flags.initgend,
|
||||
flags.initalign, PICK_RANDOM);
|
||||
if (sel < 0)
|
||||
sel = randrace(flags.initrole);
|
||||
flags.initrace = sel;
|
||||
}
|
||||
}
|
||||
|
||||
/* Select a gender, if necessary */
|
||||
/* force compatibility with role/race, try for compatibility with
|
||||
* pre-selected alignment */
|
||||
if (flags.initgend < 0 ||
|
||||
!validgend(flags.initrole, flags.initrace, flags.initgend)) {
|
||||
if (flags.initgend == ROLE_RANDOM || flags.randomall) {
|
||||
flags.initgend = pick_gend(flags.initrole, flags.initrace,
|
||||
flags.initalign, PICK_RANDOM);
|
||||
if (flags.initgend < 0)
|
||||
flags.initgend = randgend(flags.initrole, flags.initrace);
|
||||
} else {
|
||||
/* Count the number of valid genders */
|
||||
n = 0; /* number valid */
|
||||
for (i = 0; i < ROLE_GENDERS; i++) {
|
||||
if (ok_gend(flags.initrole, flags.initrace, i, flags.initalign))
|
||||
n++;
|
||||
}
|
||||
if (n == 0) {
|
||||
for (i = 0; i < ROLE_GENDERS; i++) {
|
||||
if (validgend(flags.initrole, flags.initrace, i))
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
choices = (const char **) alloc(sizeof (char *) * (n + 1));
|
||||
pickmap = (int *) alloc(sizeof (int) * (n + 1));
|
||||
for (n = 0, i = 0; i < ROLE_GENDERS; i++) {
|
||||
if (ok_gend(flags.initrole, flags.initrace, i, flags.initalign)) {
|
||||
choices[n] = genders[i].adj;
|
||||
pickmap[n++] = i;
|
||||
}
|
||||
}
|
||||
choices[n] = (const char *) 0;
|
||||
/* Permit the user to pick, if there is more than one */
|
||||
if (n > 1)
|
||||
sel =
|
||||
curses_character_dialog(choices,
|
||||
"Choose one of the following genders:");
|
||||
else
|
||||
sel = 0;
|
||||
if (sel >= 0)
|
||||
sel = pickmap[sel];
|
||||
else if (sel == ROLE_NONE) { /* Quit */
|
||||
clearlocks();
|
||||
curses_bail(0);
|
||||
}
|
||||
flags.initgend = sel;
|
||||
free((genericptr_t) choices);
|
||||
free((genericptr_t) pickmap);
|
||||
}
|
||||
if (flags.initgend == ROLE_RANDOM) { /* Random gender */
|
||||
sel = pick_gend(flags.initrole, flags.initrace,
|
||||
flags.initalign, PICK_RANDOM);
|
||||
if (sel < 0)
|
||||
sel = randgend(flags.initrole, flags.initrace);
|
||||
flags.initgend = sel;
|
||||
}
|
||||
}
|
||||
|
||||
/* Select an alignment, if necessary */
|
||||
/* force compatibility with role/race/gender */
|
||||
if (flags.initalign < 0 ||
|
||||
!validalign(flags.initrole, flags.initrace, flags.initalign)) {
|
||||
if (flags.initalign == ROLE_RANDOM || flags.randomall) {
|
||||
flags.initalign = pick_align(flags.initrole, flags.initrace,
|
||||
flags.initgend, PICK_RANDOM);
|
||||
if (flags.initalign < 0)
|
||||
flags.initalign = randalign(flags.initrole, flags.initrace);
|
||||
} else {
|
||||
/* Count the number of valid alignments */
|
||||
n = 0; /* number valid */
|
||||
for (i = 0; i < ROLE_ALIGNS; i++) {
|
||||
if (ok_align(flags.initrole, flags.initrace, flags.initgend, i))
|
||||
n++;
|
||||
}
|
||||
if (n == 0) {
|
||||
for (i = 0; i < ROLE_ALIGNS; i++)
|
||||
if (validalign(flags.initrole, flags.initrace, i))
|
||||
n++;
|
||||
}
|
||||
|
||||
choices = (const char **) alloc(sizeof (char *) * (n + 1));
|
||||
pickmap = (int *) alloc(sizeof (int) * (n + 1));
|
||||
for (n = 0, i = 0; i < ROLE_ALIGNS; i++) {
|
||||
if (ok_align(flags.initrole, flags.initrace, flags.initgend, i)) {
|
||||
choices[n] = aligns[i].adj;
|
||||
pickmap[n++] = i;
|
||||
}
|
||||
}
|
||||
choices[n] = (const char *) 0;
|
||||
/* Permit the user to pick, if there is more than one */
|
||||
if (n > 1)
|
||||
sel =
|
||||
curses_character_dialog(choices,
|
||||
"Choose one of the following alignments:");
|
||||
else
|
||||
sel = 0;
|
||||
if (sel >= 0)
|
||||
sel = pickmap[sel];
|
||||
else if (sel == ROLE_NONE) { /* Quit */
|
||||
clearlocks();
|
||||
curses_bail(0);
|
||||
}
|
||||
flags.initalign = sel;
|
||||
free((genericptr_t) choices);
|
||||
free((genericptr_t) pickmap);
|
||||
}
|
||||
if (flags.initalign == ROLE_RANDOM) {
|
||||
sel = pick_align(flags.initrole, flags.initrace,
|
||||
flags.initgend, PICK_RANDOM);
|
||||
if (sel < 0)
|
||||
sel = randalign(flags.initrole, flags.initrace);
|
||||
flags.initalign = sel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Prompt user for character race, role, alignment, or gender */
|
||||
|
||||
int
|
||||
curses_character_dialog(const char **choices, const char *prompt)
|
||||
{
|
||||
int count, count2, ret, curletter;
|
||||
char used_letters[52];
|
||||
anything identifier;
|
||||
menu_item *selected = NULL;
|
||||
winid wid = curses_get_wid(NHW_MENU);
|
||||
|
||||
identifier.a_void = 0;
|
||||
curses_start_menu(wid);
|
||||
|
||||
for (count = 0; choices[count]; count++) {
|
||||
curletter = tolower(choices[count][0]);
|
||||
for (count2 = 0; count2 < count; count2++) {
|
||||
if (curletter == used_letters[count2]) {
|
||||
curletter = toupper(curletter);
|
||||
}
|
||||
}
|
||||
|
||||
identifier.a_int = (count + 1); /* Must be non-zero */
|
||||
curses_add_menu(wid, NO_GLYPH, &identifier, curletter, 0,
|
||||
A_NORMAL, choices[count], FALSE);
|
||||
used_letters[count] = curletter;
|
||||
}
|
||||
|
||||
/* Random Selection */
|
||||
identifier.a_int = ROLE_RANDOM;
|
||||
curses_add_menu(wid, NO_GLYPH, &identifier, '*', 0, A_NORMAL, "Random",
|
||||
FALSE);
|
||||
|
||||
/* Quit prompt */
|
||||
identifier.a_int = ROLE_NONE;
|
||||
curses_add_menu(wid, NO_GLYPH, &identifier, 'q', 0, A_NORMAL, "Quit",
|
||||
FALSE);
|
||||
curses_end_menu(wid, prompt);
|
||||
ret = curses_select_menu(wid, PICK_ONE, &selected);
|
||||
if (ret == 1) {
|
||||
ret = (selected->item.a_int);
|
||||
} else { /* Cancelled selection */
|
||||
|
||||
ret = ROLE_NONE;
|
||||
}
|
||||
|
||||
if (ret > 0) {
|
||||
ret--;
|
||||
}
|
||||
|
||||
free((genericptr_t) selected);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Initialize and display options appropriately */
|
||||
|
||||
void
|
||||
curses_init_options()
|
||||
{
|
||||
set_wc_option_mod_status(WC_ALIGN_MESSAGE | WC_ALIGN_STATUS | WC_COLOR |
|
||||
WC_HILITE_PET | WC_POPUP_DIALOG, SET_IN_GAME);
|
||||
|
||||
set_wc2_option_mod_status(WC2_GUICOLOR, SET_IN_GAME);
|
||||
|
||||
/* Remove a few options that are irrelevant to this windowport */
|
||||
/*set_option_mod_status("DECgraphics", SET_IN_FILE); */
|
||||
set_option_mod_status("eight_bit_tty", SET_IN_FILE);
|
||||
|
||||
/* Add those that are */
|
||||
set_option_mod_status("statuslines", SET_IN_GAME);
|
||||
|
||||
/* Make sure that DECgraphics is not set to true via the config
|
||||
file, as this will cause display issues. We can't disable it in
|
||||
options.c in case the game is compiled with both tty and curses. */
|
||||
if (!symset[PRIMARY].name || !strcmpi(symset[PRIMARY].name, "DECgraphics")) {
|
||||
load_symset("curses",PRIMARY);
|
||||
load_symset("default",ROGUESET);
|
||||
}
|
||||
#ifdef PDCURSES
|
||||
/* PDCurses for SDL, win32 and OS/2 has the ability to set the
|
||||
terminal size programatically. If the user does not specify a
|
||||
size in the config file, we will set it to a nice big 110x32 to
|
||||
take advantage of some of the nice features of this windowport. */
|
||||
if (iflags.wc2_term_cols == 0) {
|
||||
iflags.wc2_term_cols = 110;
|
||||
}
|
||||
|
||||
if (iflags.wc2_term_rows == 0) {
|
||||
iflags.wc2_term_rows = 32;
|
||||
}
|
||||
|
||||
resize_term(iflags.wc2_term_rows, iflags.wc2_term_cols);
|
||||
getmaxyx(base_term, term_rows, term_cols);
|
||||
|
||||
/* This is needed for an odd bug with PDCurses-SDL */
|
||||
/* How to deal with this?
|
||||
switch_graphics(ASCII_GRAPHICS);
|
||||
if (iflags.IBMgraphics) {
|
||||
switch_graphics(IBM_GRAPHICS);
|
||||
} else if (iflags.cursesgraphics) {
|
||||
switch_graphics(CURS_GRAPHICS);
|
||||
} else {
|
||||
switch_graphics(ASCII_GRAPHICS);
|
||||
}
|
||||
*/
|
||||
#endif /* PDCURSES */
|
||||
if (!iflags.wc2_windowborders) {
|
||||
iflags.wc2_windowborders = 3; /* Set to auto if not specified */
|
||||
}
|
||||
|
||||
if (!iflags.wc2_petattr) {
|
||||
iflags.wc2_petattr = A_REVERSE;
|
||||
} else { /* Pet attribute specified, so hilite_pet should be true */
|
||||
|
||||
iflags.hilite_pet = TRUE;
|
||||
}
|
||||
|
||||
#ifdef NCURSES_MOUSE_VERSION
|
||||
if (iflags.wc_mouse_support) {
|
||||
mousemask(BUTTON1_CLICKED, NULL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* Display an ASCII splash screen if the splash_screen option is set */
|
||||
|
||||
void
|
||||
curses_display_splash_window()
|
||||
{
|
||||
int x_start;
|
||||
int y_start;
|
||||
|
||||
curses_get_window_xy(MAP_WIN, &x_start, &y_start);
|
||||
|
||||
if ((term_cols < 70) || (term_rows < 20)) {
|
||||
iflags.wc_splash_screen = FALSE; /* No room for s.s. */
|
||||
}
|
||||
|
||||
curses_toggle_color_attr(stdscr, CLR_WHITE, A_NORMAL, ON);
|
||||
if (iflags.wc_splash_screen) {
|
||||
mvaddstr(y_start, x_start, NETHACK_SPLASH_A);
|
||||
mvaddstr(y_start + 1, x_start, NETHACK_SPLASH_B);
|
||||
mvaddstr(y_start + 2, x_start, NETHACK_SPLASH_C);
|
||||
mvaddstr(y_start + 3, x_start, NETHACK_SPLASH_D);
|
||||
mvaddstr(y_start + 4, x_start, NETHACK_SPLASH_E);
|
||||
mvaddstr(y_start + 5, x_start, NETHACK_SPLASH_F);
|
||||
y_start += 7;
|
||||
}
|
||||
|
||||
curses_toggle_color_attr(stdscr, CLR_WHITE, A_NORMAL, OFF);
|
||||
|
||||
#ifdef COPYRIGHT_BANNER_A
|
||||
mvaddstr(y_start, x_start, COPYRIGHT_BANNER_A);
|
||||
y_start++;
|
||||
#endif
|
||||
|
||||
#ifdef COPYRIGHT_BANNER_B
|
||||
mvaddstr(y_start, x_start, COPYRIGHT_BANNER_B);
|
||||
y_start++;
|
||||
#endif
|
||||
|
||||
#ifdef COPYRIGHT_BANNER_C
|
||||
mvaddstr(y_start, x_start, COPYRIGHT_BANNER_C);
|
||||
y_start++;
|
||||
#endif
|
||||
|
||||
#ifdef COPYRIGHT_BANNER_D /* Just in case */
|
||||
mvaddstr(y_start, x_start, COPYRIGHT_BANNER_D);
|
||||
y_start++;
|
||||
#endif
|
||||
refresh();
|
||||
}
|
||||
|
||||
|
||||
/* Resore colors and cursor state before exiting */
|
||||
|
||||
void
|
||||
curses_cleanup()
|
||||
{
|
||||
#ifdef TEXTCOLOR
|
||||
if (has_colors() && can_change_color()) {
|
||||
init_color(COLOR_YELLOW, orig_yellow.r, orig_yellow.g, orig_yellow.b);
|
||||
init_color(COLOR_WHITE, orig_white.r, orig_white.g, orig_white.b);
|
||||
|
||||
if (COLORS >= 16) {
|
||||
init_color(COLOR_RED + 8, orig_hired.r, orig_hired.g, orig_hired.b);
|
||||
init_color(COLOR_GREEN + 8, orig_higreen.r, orig_higreen.g,
|
||||
orig_higreen.b);
|
||||
init_color(COLOR_YELLOW + 8, orig_hiyellow.r,
|
||||
orig_hiyellow.g, orig_hiyellow.b);
|
||||
init_color(COLOR_BLUE + 8, orig_hiblue.r, orig_hiblue.g,
|
||||
orig_hiblue.b);
|
||||
init_color(COLOR_MAGENTA + 8, orig_himagenta.r,
|
||||
orig_himagenta.g, orig_himagenta.b);
|
||||
init_color(COLOR_CYAN + 8, orig_hicyan.r, orig_hicyan.g,
|
||||
orig_hicyan.b);
|
||||
init_color(COLOR_WHITE + 8, orig_hiwhite.r, orig_hiwhite.g,
|
||||
orig_hiwhite.b);
|
||||
# ifdef USE_DARKGRAY
|
||||
if (COLORS > 16) {
|
||||
init_color(CURSES_DARK_GRAY, orig_darkgray.r,
|
||||
orig_darkgray.g, orig_darkgray.b);
|
||||
}
|
||||
# endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
17
win/curses/cursinit.h
Normal file
17
win/curses/cursinit.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
|
||||
|
||||
#ifndef CURSINIT_H
|
||||
# define CURSINIT_H
|
||||
|
||||
/* Global declarations */
|
||||
|
||||
void curses_create_main_windows(void);
|
||||
void curses_init_nhcolors(void);
|
||||
void curses_choose_character(void);
|
||||
int curses_character_dialog(const char **choices, const char *prompt);
|
||||
void curses_init_options(void);
|
||||
void curses_display_splash_window(void);
|
||||
void curses_cleanup(void);
|
||||
|
||||
|
||||
#endif /* CURSINIT_H */
|
||||
111
win/curses/cursinvt.c
Normal file
111
win/curses/cursinvt.c
Normal file
@@ -0,0 +1,111 @@
|
||||
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
|
||||
|
||||
#include "curses.h"
|
||||
#include "hack.h"
|
||||
#include "wincurs.h"
|
||||
#include "cursinvt.h"
|
||||
|
||||
/* Permanent inventory for curses interface */
|
||||
|
||||
/* Runs when the game indicates that the inventory has been updated */
|
||||
void
|
||||
curses_update_inv(void)
|
||||
{
|
||||
WINDOW *win = curses_get_nhwin(INV_WIN);
|
||||
|
||||
/* Check if the inventory window is enabled in first place */
|
||||
if (!win) {
|
||||
/* It's not. Re-initialize the main windows if the
|
||||
option was enabled. */
|
||||
if (iflags.perm_invent) {
|
||||
curses_create_main_windows();
|
||||
curses_last_messages();
|
||||
doredraw();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
boolean border = curses_window_has_border(INV_WIN);
|
||||
|
||||
/* Figure out drawing area */
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
if (border) {
|
||||
x++;
|
||||
y++;
|
||||
}
|
||||
|
||||
/* Clear the window as it is at the moment. */
|
||||
werase(win);
|
||||
|
||||
wmove(win, y, x);
|
||||
attr_t attr = A_UNDERLINE;
|
||||
wattron(win, attr);
|
||||
wprintw(win, "Inventory:");
|
||||
wattroff(win, attr);
|
||||
|
||||
/* The actual inventory will override this if we do carry stuff */
|
||||
wmove(win, y + 1, x);
|
||||
wprintw(win, "Not carrying anything");
|
||||
|
||||
display_inventory(NULL, FALSE);
|
||||
|
||||
if (border)
|
||||
box(win, 0, 0);
|
||||
|
||||
wnoutrefresh(win);
|
||||
}
|
||||
|
||||
/* Adds an inventory item. */
|
||||
void
|
||||
curses_add_inv(int y, int glyph, CHAR_P accelerator, attr_t attr,
|
||||
const char *str)
|
||||
{
|
||||
WINDOW *win = curses_get_nhwin(INV_WIN);
|
||||
|
||||
/* Figure out where to draw the line */
|
||||
int x = 0;
|
||||
if (curses_window_has_border(INV_WIN)) {
|
||||
x++;
|
||||
y++;
|
||||
}
|
||||
|
||||
wmove(win, y, x);
|
||||
if (accelerator) {
|
||||
attr_t bold = A_BOLD;
|
||||
wattron(win, bold);
|
||||
waddch(win, accelerator);
|
||||
wattroff(win, bold);
|
||||
wprintw(win, ") ");
|
||||
}
|
||||
#if 0 // FIXME: MENU GLYPHS
|
||||
if (accelerator && glyph != NO_GLYPH && iflags.use_menu_glyphs) {
|
||||
unsigned dummy = 0; /* Not used */
|
||||
int color = 0;
|
||||
int symbol = 0;
|
||||
mapglyph(glyph, &symbol, &color, &dummy,
|
||||
u.ux, u.uy);
|
||||
attr_t glyphclr = curses_color_attr(color, 0);
|
||||
wattron(win, glyphclr);
|
||||
wprintw(win, "%c ", symbol);
|
||||
wattroff(win, glyphclr);
|
||||
}
|
||||
#endif
|
||||
int color = NO_COLOR;
|
||||
if (accelerator && /* Don't colorize categories */
|
||||
iflags.use_menu_color) {
|
||||
boolean menu_color = FALSE;
|
||||
char str_mutable[BUFSZ];
|
||||
Strcpy(str_mutable, str);
|
||||
attr = 0;
|
||||
get_menu_coloring(str_mutable, &color, &attr);
|
||||
attr = curses_convert_attr(attr);
|
||||
}
|
||||
if (color == NO_COLOR) color = NONE;
|
||||
curses_toggle_color_attr(win, color, attr, ON);
|
||||
//wattron(win, attr);
|
||||
wprintw(win, "%s", str);
|
||||
//wattroff(win, attr);
|
||||
curses_toggle_color_attr(win, color, attr, OFF);
|
||||
wclrtoeol(win);
|
||||
}
|
||||
11
win/curses/cursinvt.h
Normal file
11
win/curses/cursinvt.h
Normal file
@@ -0,0 +1,11 @@
|
||||
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
|
||||
|
||||
#ifndef CURSINVT_H
|
||||
# define CURSINVT_H
|
||||
|
||||
|
||||
/* Global declarations */
|
||||
|
||||
void curses_update_inv(void);
|
||||
|
||||
#endif /* CURSINVT_H */
|
||||
820
win/curses/cursmain.c
Normal file
820
win/curses/cursmain.c
Normal file
@@ -0,0 +1,820 @@
|
||||
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
|
||||
|
||||
#include "curses.h"
|
||||
#include "hack.h"
|
||||
#include "patchlevel.h"
|
||||
#include "color.h"
|
||||
#include "wincurs.h"
|
||||
|
||||
/* Public functions for curses NetHack interface */
|
||||
|
||||
/* Interface definition, for windows.c */
|
||||
struct window_procs curses_procs = {
|
||||
"curses",
|
||||
WC_ALIGN_MESSAGE | WC_ALIGN_STATUS | WC_COLOR | WC_HILITE_PET |
|
||||
WC_PERM_INVENT | WC_POPUP_DIALOG | WC_SPLASH_SCREEN,
|
||||
WC2_DARKGRAY | WC2_HITPOINTBAR |
|
||||
#if defined(STATUS_HILITES)
|
||||
WC2_HILITE_STATUS |
|
||||
#endif
|
||||
WC2_HITPOINTBAR | WC2_FLUSH_STATUS |
|
||||
WC2_TERM_SIZE | WC2_WINDOWBORDERS | WC2_PETATTR | WC2_GUICOLOR,
|
||||
curses_init_nhwindows,
|
||||
curses_player_selection,
|
||||
curses_askname,
|
||||
curses_get_nh_event,
|
||||
curses_exit_nhwindows,
|
||||
curses_suspend_nhwindows,
|
||||
curses_resume_nhwindows,
|
||||
curses_create_nhwindow,
|
||||
curses_clear_nhwindow,
|
||||
curses_display_nhwindow,
|
||||
curses_destroy_nhwindow,
|
||||
curses_curs,
|
||||
curses_putstr,
|
||||
genl_putmixed,
|
||||
curses_display_file,
|
||||
curses_start_menu,
|
||||
curses_add_menu,
|
||||
curses_end_menu,
|
||||
curses_select_menu,
|
||||
genl_message_menu,
|
||||
curses_update_inventory,
|
||||
curses_mark_synch,
|
||||
curses_wait_synch,
|
||||
#ifdef CLIPPING
|
||||
curses_cliparound,
|
||||
#endif
|
||||
#ifdef POSITIONBAR
|
||||
donull,
|
||||
#endif
|
||||
curses_print_glyph,
|
||||
curses_raw_print,
|
||||
curses_raw_print_bold,
|
||||
curses_nhgetch,
|
||||
curses_nh_poskey,
|
||||
curses_nhbell,
|
||||
curses_doprev_message,
|
||||
curses_yn_function,
|
||||
curses_getlin,
|
||||
curses_get_ext_cmd,
|
||||
curses_number_pad,
|
||||
curses_delay_output,
|
||||
#ifdef CHANGE_COLOR /* only a Mac option currently */
|
||||
donull,
|
||||
donull,
|
||||
#endif
|
||||
curses_start_screen,
|
||||
curses_end_screen,
|
||||
genl_outrip,
|
||||
curses_preference_update,
|
||||
genl_getmsghistory,
|
||||
genl_putmsghistory,
|
||||
curses_status_init,
|
||||
genl_status_finish,
|
||||
genl_status_enablefield,
|
||||
curses_status_update,
|
||||
genl_can_suspend_yes,
|
||||
};
|
||||
|
||||
/* Track if we're performing an update to the permanent window.
|
||||
Needed since we aren't using the normal menu functions to handle
|
||||
the inventory window. */
|
||||
static int inv_update = 0;
|
||||
|
||||
/*
|
||||
init_nhwindows(int* argcp, char** argv)
|
||||
-- Initialize the windows used by NetHack. This can also
|
||||
create the standard windows listed at the top, but does
|
||||
not display them.
|
||||
-- Any commandline arguments relevant to the windowport
|
||||
should be interpreted, and *argcp and *argv should
|
||||
be changed to remove those arguments.
|
||||
-- When the message window is created, the variable
|
||||
iflags.window_inited needs to be set to TRUE. Otherwise
|
||||
all plines() will be done via raw_print().
|
||||
** Why not have init_nhwindows() create all of the "standard"
|
||||
** windows? Or at least all but WIN_INFO? -dean
|
||||
*/
|
||||
void
|
||||
curses_init_nhwindows(int *argcp, char **argv)
|
||||
{
|
||||
#ifdef PDCURSES
|
||||
char window_title[BUFSZ];
|
||||
#endif
|
||||
|
||||
#ifdef XCURSES
|
||||
base_term = Xinitscr(*argcp, argv);
|
||||
#else
|
||||
base_term = initscr();
|
||||
#endif
|
||||
#ifdef TEXTCOLOR
|
||||
if (has_colors()) {
|
||||
start_color();
|
||||
curses_init_nhcolors();
|
||||
} else {
|
||||
iflags.use_color = FALSE;
|
||||
set_option_mod_status("color", SET_IN_FILE);
|
||||
iflags.wc2_guicolor = FALSE;
|
||||
set_wc2_option_mod_status(WC2_GUICOLOR, SET_IN_FILE);
|
||||
}
|
||||
#else
|
||||
iflags.use_color = FALSE;
|
||||
set_option_mod_status("color", SET_IN_FILE);
|
||||
iflags.wc2_guicolor = FALSE;
|
||||
set_wc2_option_mod_status(WC2_GUICOLOR, SET_IN_FILE);
|
||||
#endif
|
||||
noecho();
|
||||
raw();
|
||||
meta(stdscr, TRUE);
|
||||
orig_cursor = curs_set(0);
|
||||
keypad(stdscr, TRUE);
|
||||
#ifdef NCURSES_VERSION
|
||||
# ifdef __APPLE__
|
||||
ESCDELAY = 25;
|
||||
# else
|
||||
set_escdelay(25);
|
||||
# endif/* __APPLE__ */
|
||||
#endif /* NCURSES_VERSION */
|
||||
#ifdef PDCURSES
|
||||
# ifdef DEF_GAME_NAME
|
||||
# ifdef VERSION_STRING
|
||||
sprintf(window_title, "%s %s", DEF_GAME_NAME, VERSION_STRING);
|
||||
# else
|
||||
sprintf(window_title, "%s", DEF_GAME_NAME);
|
||||
# endif
|
||||
/* VERSION_STRING */
|
||||
# else
|
||||
# ifdef VERSION_STRING
|
||||
sprintf(window_title, "%s %s", "NetHack", VERSION_STRING);
|
||||
# else
|
||||
sprintf(window_title, "%s", "NetHack");
|
||||
# endif
|
||||
/* VERSION_STRING */
|
||||
# endif/* DEF_GAME_NAME */
|
||||
PDC_set_title(window_title);
|
||||
PDC_set_blink(TRUE); /* Only if the user asks for it! */
|
||||
timeout(1);
|
||||
(void) getch();
|
||||
timeout(-1);
|
||||
#endif /* PDCURSES */
|
||||
getmaxyx(base_term, term_rows, term_cols);
|
||||
counting = FALSE;
|
||||
curses_init_options();
|
||||
if ((term_rows < 15) || (term_cols < 40)) {
|
||||
panic("Terminal too small. Must be minumum 40 width and 15 height");
|
||||
}
|
||||
|
||||
curses_create_main_windows();
|
||||
curses_init_mesg_history();
|
||||
curses_display_splash_window();
|
||||
}
|
||||
|
||||
/* Do a window-port specific player type selection. If player_selection()
|
||||
offers a Quit option, it is its responsibility to clean up and terminate
|
||||
the process. You need to fill in pl_character[0].
|
||||
*/
|
||||
void
|
||||
curses_player_selection()
|
||||
{
|
||||
curses_choose_character();
|
||||
}
|
||||
|
||||
|
||||
/* Ask the user for a player name. */
|
||||
void
|
||||
curses_askname()
|
||||
{
|
||||
curses_line_input_dialog("Who are you?", plname, PL_NSIZ);
|
||||
}
|
||||
|
||||
|
||||
/* Does window event processing (e.g. exposure events).
|
||||
A noop for the tty and X window-ports.
|
||||
*/
|
||||
void
|
||||
curses_get_nh_event()
|
||||
{
|
||||
#ifdef PDCURSES
|
||||
if (is_termresized()) {
|
||||
resize_term(0, 0);
|
||||
getmaxyx(base_term, term_rows, term_cols);
|
||||
curses_create_main_windows();
|
||||
curses_last_messages();
|
||||
doredraw();
|
||||
}
|
||||
#endif
|
||||
#ifdef NCURSES_VERSION /* Is there a better way to detect ncurses? */
|
||||
if (is_term_resized(term_rows, term_cols)) {
|
||||
if (!isendwin()) {
|
||||
endwin();
|
||||
}
|
||||
|
||||
refresh();
|
||||
getmaxyx(base_term, term_rows, term_cols);
|
||||
curses_create_main_windows();
|
||||
curses_last_messages();
|
||||
doredraw();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Exits the window system. This should dismiss all windows,
|
||||
except the "window" used for raw_print(). str is printed if possible.
|
||||
*/
|
||||
void
|
||||
curses_exit_nhwindows(const char *str)
|
||||
{
|
||||
curses_cleanup();
|
||||
curs_set(orig_cursor);
|
||||
endwin();
|
||||
iflags.window_inited = 0;
|
||||
if (str != NULL) {
|
||||
raw_print(str);
|
||||
}
|
||||
}
|
||||
|
||||
/* Prepare the window to be suspended. */
|
||||
void
|
||||
curses_suspend_nhwindows(const char *str)
|
||||
{
|
||||
endwin();
|
||||
}
|
||||
|
||||
|
||||
/* Restore the windows after being suspended. */
|
||||
void
|
||||
curses_resume_nhwindows()
|
||||
{
|
||||
curses_refresh_nethack_windows();
|
||||
}
|
||||
|
||||
/* Create a window of type "type" which can be
|
||||
NHW_MESSAGE (top line)
|
||||
NHW_STATUS (bottom lines)
|
||||
NHW_MAP (main dungeon)
|
||||
NHW_MENU (inventory or other "corner" windows)
|
||||
NHW_TEXT (help/text, full screen paged window)
|
||||
*/
|
||||
winid
|
||||
curses_create_nhwindow(int type)
|
||||
{
|
||||
winid wid = curses_get_wid(type);
|
||||
|
||||
if (curses_is_menu(wid) || curses_is_text(wid)) {
|
||||
curses_start_menu(wid);
|
||||
curses_add_wid(wid);
|
||||
}
|
||||
|
||||
return wid;
|
||||
}
|
||||
|
||||
|
||||
/* Clear the given window, when asked to. */
|
||||
void
|
||||
curses_clear_nhwindow(winid wid)
|
||||
{
|
||||
if (wid != NHW_MESSAGE) {
|
||||
curses_clear_nhwin(wid);
|
||||
}
|
||||
}
|
||||
|
||||
/* -- Display the window on the screen. If there is data
|
||||
pending for output in that window, it should be sent.
|
||||
If blocking is TRUE, display_nhwindow() will not
|
||||
return until the data has been displayed on the screen,
|
||||
and acknowledged by the user where appropriate.
|
||||
-- All calls are blocking in the tty window-port.
|
||||
-- Calling display_nhwindow(WIN_MESSAGE,???) will do a
|
||||
--more--, if necessary, in the tty window-port.
|
||||
*/
|
||||
void
|
||||
curses_display_nhwindow(winid wid, BOOLEAN_P block)
|
||||
{
|
||||
menu_item *selected = NULL;
|
||||
if (curses_is_menu(wid) || curses_is_text(wid)) {
|
||||
curses_end_menu(wid, "");
|
||||
curses_select_menu(wid, PICK_NONE, &selected);
|
||||
return;
|
||||
}
|
||||
|
||||
/* don't overwrite the splash screen first time through */
|
||||
if (!iflags.window_inited && wid == MAP_WIN)
|
||||
iflags.window_inited = TRUE;
|
||||
else {
|
||||
/* actually display the window */
|
||||
wnoutrefresh(curses_get_nhwin(wid));
|
||||
/* flush pending writes from other windows too */
|
||||
doupdate();
|
||||
}
|
||||
if ((wid == MAP_WIN) && block) {
|
||||
(void) curses_more();
|
||||
}
|
||||
|
||||
if ((wid == MESSAGE_WIN) && block) {
|
||||
(void) curses_block(TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Destroy will dismiss the window if the window has not
|
||||
* already been dismissed.
|
||||
*/
|
||||
void
|
||||
curses_destroy_nhwindow(winid wid)
|
||||
{
|
||||
curses_del_nhwin(wid);
|
||||
}
|
||||
|
||||
/* Next output to window will start at (x,y), also moves
|
||||
displayable cursor to (x,y). For backward compatibility,
|
||||
1 <= x < cols, 0 <= y < rows, where cols and rows are
|
||||
the size of window.
|
||||
*/
|
||||
void
|
||||
curses_curs(winid wid, int x, int y)
|
||||
{
|
||||
curses_move_cursor(wid, x, y);
|
||||
}
|
||||
|
||||
/*
|
||||
putstr(window, attr, str)
|
||||
-- Print str on the window with the given attribute. Only
|
||||
printable ASCII characters (040-0126) must be supported.
|
||||
Multiple putstr()s are output on separate lines.
|
||||
Attributes
|
||||
can be one of
|
||||
ATR_NONE (or 0)
|
||||
ATR_ULINE
|
||||
ATR_BOLD
|
||||
ATR_BLINK
|
||||
ATR_INVERSE
|
||||
If a window-port does not support all of these, it may map
|
||||
unsupported attributes to a supported one (e.g. map them
|
||||
all to ATR_INVERSE). putstr() may compress spaces out of
|
||||
str, break str, or truncate str, if necessary for the
|
||||
display. Where putstr() breaks a line, it has to clear
|
||||
to end-of-line.
|
||||
-- putstr should be implemented such that if two putstr()s
|
||||
are done consecutively the user will see the first and
|
||||
then the second. In the tty port, pline() achieves this
|
||||
by calling more() or displaying both on the same line.
|
||||
*/
|
||||
void
|
||||
curses_putstr(winid wid, int attr, const char *text)
|
||||
{
|
||||
int curses_attr = curses_convert_attr(attr);
|
||||
|
||||
/* We need to convert NetHack attributes to curses attributes */
|
||||
curses_puts(wid, curses_attr, text);
|
||||
}
|
||||
|
||||
/* Display the file named str. Complain about missing files
|
||||
iff complain is TRUE.
|
||||
*/
|
||||
void
|
||||
curses_display_file(const char *filename, BOOLEAN_P must_exist)
|
||||
{
|
||||
curses_view_file(filename, must_exist);
|
||||
}
|
||||
|
||||
/* Start using window as a menu. You must call start_menu()
|
||||
before add_menu(). After calling start_menu() you may not
|
||||
putstr() to the window. Only windows of type NHW_MENU may
|
||||
be used for menus.
|
||||
*/
|
||||
void
|
||||
curses_start_menu(winid wid)
|
||||
{
|
||||
if (inv_update)
|
||||
return;
|
||||
|
||||
curses_create_nhmenu(wid);
|
||||
}
|
||||
|
||||
/*
|
||||
add_menu(winid wid, int glyph, const anything identifier,
|
||||
char accelerator, char groupacc,
|
||||
int attr, char *str, boolean preselected)
|
||||
-- Add a text line str to the given menu window. If identifier
|
||||
is 0, then the line cannot be selected (e.g. a title).
|
||||
Otherwise, identifier is the value returned if the line is
|
||||
selected. Accelerator is a keyboard key that can be used
|
||||
to select the line. If the accelerator of a selectable
|
||||
item is 0, the window system is free to select its own
|
||||
accelerator. It is up to the window-port to make the
|
||||
accelerator visible to the user (e.g. put "a - " in front
|
||||
of str). The value attr is the same as in putstr().
|
||||
Glyph is an optional glyph to accompany the line. If
|
||||
window port cannot or does not want to display it, this
|
||||
is OK. If there is no glyph applicable, then this
|
||||
value will be NO_GLYPH.
|
||||
-- All accelerators should be in the range [A-Za-z].
|
||||
-- It is expected that callers do not mix accelerator
|
||||
choices. Either all selectable items have an accelerator
|
||||
or let the window system pick them. Don't do both.
|
||||
-- Groupacc is a group accelerator. It may be any character
|
||||
outside of the standard accelerator (see above) or a
|
||||
number. If 0, the item is unaffected by any group
|
||||
accelerator. If this accelerator conflicts with
|
||||
the menu command (or their user defined alises), it loses.
|
||||
The menu commands and aliases take care not to interfere
|
||||
with the default object class symbols.
|
||||
-- If you want this choice to be preselected when the
|
||||
menu is displayed, set preselected to TRUE.
|
||||
*/
|
||||
void
|
||||
curses_add_menu(winid wid, int glyph, const ANY_P * identifier,
|
||||
CHAR_P accelerator, CHAR_P group_accel, int attr,
|
||||
const char *str, BOOLEAN_P presel)
|
||||
{
|
||||
int curses_attr = curses_convert_attr(attr);
|
||||
|
||||
if (inv_update) {
|
||||
curses_add_inv(inv_update, glyph, accelerator, curses_attr, str);
|
||||
inv_update++;
|
||||
return;
|
||||
}
|
||||
|
||||
curses_add_nhmenu_item(wid, glyph, identifier, accelerator, group_accel,
|
||||
curses_attr, str, presel);
|
||||
}
|
||||
|
||||
/*
|
||||
end_menu(window, prompt)
|
||||
-- Stop adding entries to the menu and flushes the window
|
||||
to the screen (brings to front?). Prompt is a prompt
|
||||
to give the user. If prompt is NULL, no prompt will
|
||||
be printed.
|
||||
** This probably shouldn't flush the window any more (if
|
||||
** it ever did). That should be select_menu's job. -dean
|
||||
*/
|
||||
void
|
||||
curses_end_menu(winid wid, const char *prompt)
|
||||
{
|
||||
if (inv_update)
|
||||
return;
|
||||
|
||||
curses_finalize_nhmenu(wid, prompt);
|
||||
}
|
||||
|
||||
/*
|
||||
int select_menu(winid window, int how, menu_item **selected)
|
||||
-- Return the number of items selected; 0 if none were chosen,
|
||||
-1 when explicitly cancelled. If items were selected, then
|
||||
selected is filled in with an allocated array of menu_item
|
||||
structures, one for each selected line. The caller must
|
||||
free this array when done with it. The "count" field
|
||||
of selected is a user supplied count. If the user did
|
||||
not supply a count, then the count field is filled with
|
||||
-1 (meaning all). A count of zero is equivalent to not
|
||||
being selected and should not be in the list. If no items
|
||||
were selected, then selected is NULL'ed out. How is the
|
||||
mode of the menu. Three valid values are PICK_NONE,
|
||||
PICK_ONE, and PICK_N, meaning: nothing is selectable,
|
||||
only one thing is selectable, and any number valid items
|
||||
may selected. If how is PICK_NONE, this function should
|
||||
never return anything but 0 or -1.
|
||||
-- You may call select_menu() on a window multiple times --
|
||||
the menu is saved until start_menu() or destroy_nhwindow()
|
||||
is called on the window.
|
||||
-- Note that NHW_MENU windows need not have select_menu()
|
||||
called for them. There is no way of knowing whether
|
||||
select_menu() will be called for the window at
|
||||
create_nhwindow() time.
|
||||
*/
|
||||
int
|
||||
curses_select_menu(winid wid, int how, MENU_ITEM_P ** selected)
|
||||
{
|
||||
if (inv_update)
|
||||
return 0;
|
||||
|
||||
return curses_display_nhmenu(wid, how, selected);
|
||||
}
|
||||
|
||||
void
|
||||
curses_update_inventory(void)
|
||||
{
|
||||
/* Don't do anything if perm_invent is off unless we
|
||||
changed the option. */
|
||||
if (!iflags.perm_invent) {
|
||||
if (curses_get_nhwin(INV_WIN)) {
|
||||
curses_create_main_windows();
|
||||
curses_last_messages();
|
||||
doredraw();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update inventory sidebar. NetHack uses normal menu functions
|
||||
when drawing the inventory, and we don't want to change the
|
||||
underlying code. So instead, track if an inventory update is
|
||||
being performed with a static variable. */
|
||||
inv_update = 1;
|
||||
curses_update_inv();
|
||||
inv_update = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
mark_synch() -- Don't go beyond this point in I/O on any channel until
|
||||
all channels are caught up to here. Can be an empty call
|
||||
for the moment
|
||||
*/
|
||||
void
|
||||
curses_mark_synch()
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
wait_synch() -- Wait until all pending output is complete (*flush*() for
|
||||
streams goes here).
|
||||
-- May also deal with exposure events etc. so that the
|
||||
display is OK when return from wait_synch().
|
||||
*/
|
||||
void
|
||||
curses_wait_synch()
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
cliparound(x, y)-- Make sure that the user is more-or-less centered on the
|
||||
screen if the playing area is larger than the screen.
|
||||
-- This function is only defined if CLIPPING is defined.
|
||||
*/
|
||||
void
|
||||
curses_cliparound(int x, int y)
|
||||
{
|
||||
int sx, sy, ex, ey;
|
||||
boolean redraw = curses_map_borders(&sx, &sy, &ex, &ey, x, y);
|
||||
|
||||
if (redraw) {
|
||||
curses_draw_map(sx, sy, ex, ey);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
print_glyph(window, x, y, glyph, bkglyph)
|
||||
-- Print the glyph at (x,y) on the given window. Glyphs are
|
||||
integers at the interface, mapped to whatever the window-
|
||||
port wants (symbol, font, color, attributes, ...there's
|
||||
a 1-1 map between glyphs and distinct things on the map).
|
||||
bkglyph is to render the background behind the glyph.
|
||||
It's not used here.
|
||||
*/
|
||||
void
|
||||
curses_print_glyph(winid wid, XCHAR_P x, XCHAR_P y, int glyph, int bkglyph)
|
||||
{
|
||||
int ch;
|
||||
int color;
|
||||
unsigned int special;
|
||||
int attr = -1;
|
||||
|
||||
/* map glyph to character and color */
|
||||
mapglyph(glyph, &ch, &color, &special, x, y);
|
||||
if ((special & MG_PET) && iflags.hilite_pet) {
|
||||
attr = iflags.wc2_petattr;
|
||||
}
|
||||
if ((special & MG_DETECT) && iflags.use_inverse) {
|
||||
attr = A_REVERSE;
|
||||
}
|
||||
if (!symset[PRIMARY].name || !strcmpi(symset[PRIMARY].name, "curses")) {
|
||||
ch = curses_convert_glyph(ch, glyph);
|
||||
}
|
||||
if (wid == NHW_MAP) {
|
||||
/* hilite stairs not in 3.6, yet
|
||||
if ((special & MG_STAIRS) && iflags.hilite_hidden_stairs) {
|
||||
color = 16 + (color * 2);
|
||||
} else
|
||||
*/
|
||||
if ((special & MG_OBJPILE) && iflags.hilite_pile) {
|
||||
color = 16 + (color * 2) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
curses_putch(wid, x, y, ch, color, attr);
|
||||
}
|
||||
|
||||
/*
|
||||
raw_print(str) -- Print directly to a screen, or otherwise guarantee that
|
||||
the user sees str. raw_print() appends a newline to str.
|
||||
It need not recognize ASCII control characters. This is
|
||||
used during startup (before windowing system initialization
|
||||
-- maybe this means only error startup messages are raw),
|
||||
for error messages, and maybe other "msg" uses. E.g.
|
||||
updating status for micros (i.e, "saving").
|
||||
*/
|
||||
void
|
||||
curses_raw_print(const char *str)
|
||||
{
|
||||
puts(str);
|
||||
}
|
||||
|
||||
/*
|
||||
raw_print_bold(str)
|
||||
-- Like raw_print(), but prints in bold/standout (if possible).
|
||||
*/
|
||||
void
|
||||
curses_raw_print_bold(const char *str)
|
||||
{
|
||||
curses_raw_print(str);
|
||||
}
|
||||
|
||||
/*
|
||||
int nhgetch() -- Returns a single character input from the user.
|
||||
-- In the tty window-port, nhgetch() assumes that tgetch()
|
||||
will be the routine the OS provides to read a character.
|
||||
Returned character _must_ be non-zero.
|
||||
*/
|
||||
int
|
||||
curses_nhgetch()
|
||||
{
|
||||
int ch;
|
||||
|
||||
curses_prehousekeeping();
|
||||
ch = curses_read_char();
|
||||
curses_posthousekeeping();
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
/*
|
||||
int nh_poskey(int *x, int *y, int *mod)
|
||||
-- Returns a single character input from the user or a
|
||||
a positioning event (perhaps from a mouse). If the
|
||||
return value is non-zero, a character was typed, else,
|
||||
a position in the MAP window is returned in x, y and mod.
|
||||
mod may be one of
|
||||
|
||||
CLICK_1 -- mouse click type 1
|
||||
CLICK_2 -- mouse click type 2
|
||||
|
||||
The different click types can map to whatever the
|
||||
hardware supports. If no mouse is supported, this
|
||||
routine always returns a non-zero character.
|
||||
*/
|
||||
int
|
||||
curses_nh_poskey(int *x, int *y, int *mod)
|
||||
{
|
||||
int key = curses_nhgetch();
|
||||
|
||||
#ifdef NCURSES_MOUSE_VERSION
|
||||
/* Mouse event if mouse_support is true */
|
||||
if (key == KEY_MOUSE) {
|
||||
key = curses_get_mouse(x, y, mod);
|
||||
}
|
||||
#endif
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
/*
|
||||
nhbell() -- Beep at user. [This will exist at least until sounds are
|
||||
redone, since sounds aren't attributable to windows anyway.]
|
||||
*/
|
||||
void
|
||||
curses_nhbell()
|
||||
{
|
||||
beep();
|
||||
}
|
||||
|
||||
/*
|
||||
doprev_message()
|
||||
-- Display previous messages. Used by the ^P command.
|
||||
-- On the tty-port this scrolls WIN_MESSAGE back one line.
|
||||
*/
|
||||
int
|
||||
curses_doprev_message()
|
||||
{
|
||||
curses_prev_mesg();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
char yn_function(const char *ques, const char *choices, char default)
|
||||
-- Print a prompt made up of ques, choices and default.
|
||||
Read a single character response that is contained in
|
||||
choices or default. If choices is NULL, all possible
|
||||
inputs are accepted and returned. This overrides
|
||||
everything else. The choices are expected to be in
|
||||
lower case. Entering ESC always maps to 'q', or 'n',
|
||||
in that order, if present in choices, otherwise it maps
|
||||
to default. Entering any other quit character (SPACE,
|
||||
RETURN, NEWLINE) maps to default.
|
||||
-- If the choices string contains ESC, then anything after
|
||||
it is an acceptable response, but the ESC and whatever
|
||||
follows is not included in the prompt.
|
||||
-- If the choices string contains a '#' then accept a count.
|
||||
Place this value in the global "yn_number" and return '#'.
|
||||
-- This uses the top line in the tty window-port, other
|
||||
ports might use a popup.
|
||||
*/
|
||||
char
|
||||
curses_yn_function(const char *question, const char *choices, CHAR_P def)
|
||||
{
|
||||
return (char) curses_character_input_dialog(question, choices, def);
|
||||
}
|
||||
|
||||
/*
|
||||
getlin(const char *ques, char *input)
|
||||
-- Prints ques as a prompt and reads a single line of text,
|
||||
up to a newline. The string entered is returned without the
|
||||
newline. ESC is used to cancel, in which case the string
|
||||
"\033\000" is returned.
|
||||
-- getlin() must call flush_screen(1) before doing anything.
|
||||
-- This uses the top line in the tty window-port, other
|
||||
ports might use a popup.
|
||||
*/
|
||||
void
|
||||
curses_getlin(const char *question, char *input)
|
||||
{
|
||||
curses_line_input_dialog(question, input, BUFSZ);
|
||||
}
|
||||
|
||||
/*
|
||||
int get_ext_cmd(void)
|
||||
-- Get an extended command in a window-port specific way.
|
||||
An index into extcmdlist[] is returned on a successful
|
||||
selection, -1 otherwise.
|
||||
*/
|
||||
int
|
||||
curses_get_ext_cmd()
|
||||
{
|
||||
return curses_ext_cmd();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
number_pad(state)
|
||||
-- Initialize the number pad to the given state.
|
||||
*/
|
||||
void
|
||||
curses_number_pad(int state)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
delay_output() -- Causes a visible delay of 50ms in the output.
|
||||
Conceptually, this is similar to wait_synch() followed
|
||||
by a nap(50ms), but allows asynchronous operation.
|
||||
*/
|
||||
void
|
||||
curses_delay_output()
|
||||
{
|
||||
/* refreshing the whole display is a waste of time,
|
||||
* but that's why we're here */
|
||||
refresh();
|
||||
napms(50);
|
||||
}
|
||||
|
||||
/*
|
||||
start_screen() -- Only used on Unix tty ports, but must be declared for
|
||||
completeness. Sets up the tty to work in full-screen
|
||||
graphics mode. Look at win/tty/termcap.c for an
|
||||
example. If your window-port does not need this function
|
||||
just declare an empty function.
|
||||
*/
|
||||
void
|
||||
curses_start_screen()
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
end_screen() -- Only used on Unix tty ports, but must be declared for
|
||||
completeness. The complement of start_screen().
|
||||
*/
|
||||
void
|
||||
curses_end_screen()
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
outrip(winid, int)
|
||||
-- The tombstone code. If you want the traditional code use
|
||||
genl_outrip for the value and check the #if in rip.c.
|
||||
*/
|
||||
void
|
||||
curses_outrip(winid wid, int how)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
preference_update(preference)
|
||||
-- The player has just changed one of the wincap preference
|
||||
settings, and the NetHack core is notifying your window
|
||||
port of that change. If your window-port is capable of
|
||||
dynamically adjusting to the change then it should do so.
|
||||
Your window-port will only be notified of a particular
|
||||
change if it indicated that it wants to be by setting the
|
||||
corresponding bit in the wincap mask.
|
||||
*/
|
||||
void
|
||||
curses_preference_update(const char *pref)
|
||||
{
|
||||
if ((strcmp(pref, "align_status") == 0) ||
|
||||
(strcmp(pref, "align_message") == 0)) {
|
||||
curses_create_main_windows();
|
||||
curses_last_messages();
|
||||
doredraw();
|
||||
}
|
||||
}
|
||||
|
||||
630
win/curses/cursmesg.c
Normal file
630
win/curses/cursmesg.c
Normal file
@@ -0,0 +1,630 @@
|
||||
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
|
||||
|
||||
#include "curses.h"
|
||||
#include "hack.h"
|
||||
#include "wincurs.h"
|
||||
#include "cursmesg.h"
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
/* Message window routines for curses interface */
|
||||
|
||||
/* Private declatations */
|
||||
|
||||
typedef struct nhpm {
|
||||
char *str; /* Message text */
|
||||
long turn; /* Turn number for message */
|
||||
struct nhpm *prev_mesg; /* Pointer to previous message */
|
||||
struct nhpm *next_mesg; /* Pointer to next message */
|
||||
} nhprev_mesg;
|
||||
|
||||
static void scroll_window(winid wid);
|
||||
static void unscroll_window(winid wid);
|
||||
static void directional_scroll(winid wid, int nlines);
|
||||
static void mesg_add_line(char *mline);
|
||||
static nhprev_mesg *get_msg_line(boolean reverse, int mindex);
|
||||
|
||||
static int turn_lines = 1;
|
||||
static int mx = 0;
|
||||
static int my = 0; /* message window text location */
|
||||
static nhprev_mesg *first_mesg = NULL;
|
||||
static nhprev_mesg *last_mesg = NULL;
|
||||
static int max_messages;
|
||||
static int num_messages = 0;
|
||||
|
||||
|
||||
|
||||
/* Write a string to the message window. Attributes set by calling function. */
|
||||
|
||||
void
|
||||
curses_message_win_puts(const char *message, boolean recursed)
|
||||
{
|
||||
int height, width, linespace;
|
||||
char *tmpstr;
|
||||
WINDOW *win = curses_get_nhwin(MESSAGE_WIN);
|
||||
boolean border = curses_window_has_border(MESSAGE_WIN);
|
||||
int message_length = strlen(message);
|
||||
int border_space = 0;
|
||||
static long suppress_turn = -1;
|
||||
|
||||
if (strncmp("Count:", message, 6) == 0) {
|
||||
curses_count_window(message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (suppress_turn == moves) {
|
||||
return;
|
||||
}
|
||||
|
||||
curses_get_window_size(MESSAGE_WIN, &height, &width);
|
||||
if (border) {
|
||||
border_space = 1;
|
||||
if (mx < 1) {
|
||||
mx = 1;
|
||||
}
|
||||
if (my < 1) {
|
||||
my = 1;
|
||||
}
|
||||
}
|
||||
|
||||
linespace = ((width + border_space) - 3) - mx;
|
||||
|
||||
if (strcmp(message, "#") == 0) { /* Extended command or Count: */
|
||||
if ((strcmp(toplines, "#") != 0) && (my >= (height - 1 + border_space)) && (height != 1)) { /* Bottom of message window */
|
||||
scroll_window(MESSAGE_WIN);
|
||||
mx = width;
|
||||
my--;
|
||||
strcpy(toplines, message);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!recursed) {
|
||||
strcpy(toplines, message);
|
||||
mesg_add_line((char *) message);
|
||||
}
|
||||
|
||||
if (linespace < message_length) {
|
||||
if (my >= (height - 1 + border_space)) { /* bottom of message win */
|
||||
if ((turn_lines > height) || (height == 1)) {
|
||||
/* Pause until key is hit - Esc suppresses any further
|
||||
messages that turn */
|
||||
if (curses_more() == '\033') {
|
||||
suppress_turn = moves;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
scroll_window(MESSAGE_WIN);
|
||||
turn_lines++;
|
||||
}
|
||||
} else {
|
||||
if (mx != border_space) {
|
||||
my++;
|
||||
mx = border_space;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (height > 1) {
|
||||
curses_toggle_color_attr(win, NONE, A_BOLD, ON);
|
||||
}
|
||||
|
||||
if ((mx == border_space) && ((message_length + 2) > width)) {
|
||||
tmpstr = curses_break_str(message, (width - 2), 1);
|
||||
mvwprintw(win, my, mx, "%s", tmpstr);
|
||||
mx += strlen(tmpstr);
|
||||
if (strlen(tmpstr) < (size_t) (width - 2)) {
|
||||
mx++;
|
||||
}
|
||||
free(tmpstr);
|
||||
if (height > 1) {
|
||||
curses_toggle_color_attr(win, NONE, A_BOLD, OFF);
|
||||
}
|
||||
curses_message_win_puts(tmpstr = curses_str_remainder(message, (width - 2), 1),
|
||||
TRUE);
|
||||
free(tmpstr);
|
||||
} else {
|
||||
mvwprintw(win, my, mx, "%s", message);
|
||||
curses_toggle_color_attr(win, NONE, A_BOLD, OFF);
|
||||
mx += message_length + 1;
|
||||
}
|
||||
wrefresh(win);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
curses_block(boolean noscroll)
|
||||
/* noscroll - blocking because of msgtype = stop/alert */
|
||||
/* else blocking because window is full, so need to scroll after */
|
||||
{
|
||||
int height, width, ret;
|
||||
WINDOW *win = curses_get_nhwin(MESSAGE_WIN);
|
||||
char *resp = " \n\033"; /* space, enter, esc */
|
||||
|
||||
|
||||
curses_get_window_size(MESSAGE_WIN, &height, &width);
|
||||
curses_toggle_color_attr(win, MORECOLOR, NONE, ON);
|
||||
mvwprintw(win, my, mx, iflags.msg_is_alert ? "<TAB!>" : ">>");
|
||||
curses_toggle_color_attr(win, MORECOLOR, NONE, OFF);
|
||||
if (iflags.msg_is_alert)
|
||||
curses_alert_main_borders(TRUE);
|
||||
wrefresh(win);
|
||||
while (iflags.msg_is_alert && (ret = wgetch(win) != '\t'));
|
||||
/* msgtype=stop should require space/enter rather than
|
||||
* just any key, as we want to prevent YASD from
|
||||
* riding direction keys. */
|
||||
while (!iflags.msg_is_alert && (ret = wgetch(win)) && !index(resp,(char)ret));
|
||||
if (iflags.msg_is_alert)
|
||||
curses_alert_main_borders(FALSE);
|
||||
if (height == 1) {
|
||||
curses_clear_unhighlight_message_window();
|
||||
} else {
|
||||
mvwprintw(win, my, mx, " ");
|
||||
if (!noscroll) {
|
||||
scroll_window(MESSAGE_WIN);
|
||||
turn_lines = 1;
|
||||
}
|
||||
wrefresh(win);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
curses_more()
|
||||
{
|
||||
return curses_block(FALSE);
|
||||
}
|
||||
|
||||
|
||||
/* Clear the message window if one line; otherwise unhighlight old messages */
|
||||
|
||||
void
|
||||
curses_clear_unhighlight_message_window()
|
||||
{
|
||||
int mh, mw, count;
|
||||
boolean border = curses_window_has_border(MESSAGE_WIN);
|
||||
WINDOW *win = curses_get_nhwin(MESSAGE_WIN);
|
||||
|
||||
turn_lines = 1;
|
||||
|
||||
curses_get_window_size(MESSAGE_WIN, &mh, &mw);
|
||||
|
||||
mx = 0;
|
||||
|
||||
if (border) {
|
||||
mx++;
|
||||
}
|
||||
|
||||
if (mh == 1) {
|
||||
curses_clear_nhwin(MESSAGE_WIN);
|
||||
} else {
|
||||
mx += mw; /* Force new line on new turn */
|
||||
|
||||
if (border) {
|
||||
|
||||
for (count = 0; count < mh; count++) {
|
||||
mvwchgat(win, count + 1, 1, mw, COLOR_PAIR(8), A_NORMAL, NULL);
|
||||
}
|
||||
} else {
|
||||
for (count = 0; count < mh; count++) {
|
||||
mvwchgat(win, count, 0, mw, COLOR_PAIR(8), A_NORMAL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
wnoutrefresh(win);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Reset message window cursor to starting position, and display most
|
||||
recent messages. */
|
||||
|
||||
void
|
||||
curses_last_messages()
|
||||
{
|
||||
boolean border = curses_window_has_border(MESSAGE_WIN);
|
||||
|
||||
if (border) {
|
||||
mx = 1;
|
||||
my = 1;
|
||||
} else {
|
||||
mx = 0;
|
||||
my = 0;
|
||||
}
|
||||
|
||||
nhprev_mesg *mesg;
|
||||
int i;
|
||||
for (i = (num_messages - 1); i > 0; i--) {
|
||||
mesg = get_msg_line(TRUE, i);
|
||||
if (mesg && mesg->str && strcmp(mesg->str, ""))
|
||||
curses_message_win_puts(mesg->str, TRUE);
|
||||
}
|
||||
curses_message_win_puts(toplines, TRUE);
|
||||
}
|
||||
|
||||
|
||||
/* Initialize list for message history */
|
||||
|
||||
void
|
||||
curses_init_mesg_history()
|
||||
{
|
||||
max_messages = iflags.msg_history;
|
||||
|
||||
if (max_messages < 1) {
|
||||
max_messages = 1;
|
||||
}
|
||||
|
||||
if (max_messages > MESG_HISTORY_MAX) {
|
||||
max_messages = MESG_HISTORY_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Display previous message window messages in reverse chron order */
|
||||
|
||||
void
|
||||
curses_prev_mesg()
|
||||
{
|
||||
int count;
|
||||
winid wid;
|
||||
long turn = 0;
|
||||
anything *identifier;
|
||||
nhprev_mesg *mesg;
|
||||
menu_item *selected = NULL;
|
||||
|
||||
wid = curses_get_wid(NHW_MENU);
|
||||
curses_create_nhmenu(wid);
|
||||
identifier = malloc(sizeof (anything));
|
||||
identifier->a_void = NULL;
|
||||
|
||||
for (count = 0; count < num_messages; count++) {
|
||||
mesg = get_msg_line(TRUE, count);
|
||||
if ((turn != mesg->turn) && (count != 0)) {
|
||||
curses_add_menu(wid, NO_GLYPH, identifier, 0, 0, A_NORMAL,
|
||||
"---", FALSE);
|
||||
}
|
||||
curses_add_menu(wid, NO_GLYPH, identifier, 0, 0, A_NORMAL,
|
||||
mesg->str, FALSE);
|
||||
turn = mesg->turn;
|
||||
}
|
||||
|
||||
curses_end_menu(wid, "");
|
||||
curses_select_menu(wid, PICK_NONE, &selected);
|
||||
}
|
||||
|
||||
|
||||
/* Shows Count: in a separate window, or at the bottom of the message
|
||||
window, depending on the user's settings */
|
||||
|
||||
void
|
||||
curses_count_window(const char *count_text)
|
||||
{
|
||||
int startx, starty, winx, winy;
|
||||
int messageh, messagew;
|
||||
static WINDOW *countwin = NULL;
|
||||
|
||||
if ((count_text == NULL) && (countwin != NULL)) {
|
||||
delwin(countwin);
|
||||
countwin = NULL;
|
||||
counting = FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
counting = TRUE;
|
||||
|
||||
if (iflags.wc_popup_dialog) { /* Display count in popup window */
|
||||
startx = 1;
|
||||
starty = 1;
|
||||
|
||||
if (countwin == NULL) {
|
||||
countwin = curses_create_window(25, 1, UP);
|
||||
}
|
||||
|
||||
} else { /* Display count at bottom of message window */
|
||||
|
||||
curses_get_window_xy(MESSAGE_WIN, &winx, &winy);
|
||||
curses_get_window_size(MESSAGE_WIN, &messageh, &messagew);
|
||||
|
||||
if (curses_window_has_border(MESSAGE_WIN)) {
|
||||
winx++;
|
||||
winy++;
|
||||
}
|
||||
|
||||
winy += messageh - 1;
|
||||
|
||||
if (countwin == NULL) {
|
||||
pline("#");
|
||||
#ifndef PDCURSES
|
||||
countwin = newwin(1, 25, winy, winx);
|
||||
#endif /* !PDCURSES */
|
||||
}
|
||||
#ifdef PDCURSES
|
||||
else {
|
||||
curses_destroy_win(countwin);
|
||||
}
|
||||
|
||||
countwin = newwin(1, 25, winy, winx);
|
||||
#endif /* PDCURSES */
|
||||
startx = 0;
|
||||
starty = 0;
|
||||
}
|
||||
|
||||
mvwprintw(countwin, starty, startx, "%s", count_text);
|
||||
wrefresh(countwin);
|
||||
}
|
||||
|
||||
/* Gets a "line" (buffer) of input. */
|
||||
void
|
||||
curses_message_win_getline(const char *prompt, char *answer, int buffer)
|
||||
{
|
||||
int height, width; /* of window */
|
||||
char *tmpbuf, *p_answer; /* combined prompt + answer */
|
||||
int nlines, maxlines, i; /* prompt + answer */
|
||||
int promptline;
|
||||
int promptx;
|
||||
char **linestarts; /* pointers to start of each line */
|
||||
char *tmpstr; /* for free() */
|
||||
int maxy, maxx; /* linewrap / scroll */
|
||||
int ch;
|
||||
|
||||
WINDOW *win = curses_get_nhwin(MESSAGE_WIN);
|
||||
int border_space = 0;
|
||||
int len = 0; /* of answer string */
|
||||
boolean border = curses_window_has_border(MESSAGE_WIN);
|
||||
int orig_cursor = curs_set(0);
|
||||
|
||||
curses_get_window_size(MESSAGE_WIN, &height, &width);
|
||||
if (border) {
|
||||
border_space = 1;
|
||||
if (mx < 1) mx = 1;
|
||||
if (my < 1) my = 1;
|
||||
}
|
||||
maxy = height - 1 + border_space;
|
||||
maxx = width - 1 + border_space;
|
||||
|
||||
tmpbuf = (char *)malloc(strlen(prompt) + buffer + 2);
|
||||
maxlines = buffer / width * 2;
|
||||
strcpy(tmpbuf, prompt);
|
||||
strcat(tmpbuf, " ");
|
||||
nlines = curses_num_lines(tmpbuf,width);
|
||||
maxlines += nlines * 2;
|
||||
linestarts = (char **)malloc(sizeof(char*) * maxlines);
|
||||
p_answer = tmpbuf + strlen(tmpbuf);
|
||||
linestarts[0] = tmpbuf;
|
||||
|
||||
if (mx > border_space) { /* newline */
|
||||
if (my >= maxy) scroll_window(MESSAGE_WIN);
|
||||
else my++;
|
||||
mx = border_space;
|
||||
}
|
||||
|
||||
curses_toggle_color_attr(win, NONE, A_BOLD, ON);
|
||||
|
||||
for (i = 0; i < nlines-1; i++) {
|
||||
tmpstr = curses_break_str(linestarts[i],width-1,1);
|
||||
linestarts[i+1] = linestarts[i] + strlen(tmpstr);
|
||||
if (*linestarts[i+1] == ' ') linestarts[i+1]++;
|
||||
mvwaddstr(win,my,mx,tmpstr);
|
||||
free(tmpstr);
|
||||
if (++my >= maxy) {
|
||||
scroll_window(MESSAGE_WIN);
|
||||
my--;
|
||||
}
|
||||
}
|
||||
mvwaddstr(win,my,mx,linestarts[nlines-1]);
|
||||
mx = promptx = strlen(linestarts[nlines-1]) + border_space;
|
||||
promptline = nlines - 1;
|
||||
|
||||
while(1) {
|
||||
mx = strlen(linestarts[nlines - 1]) + border_space;
|
||||
if (mx > maxx) {
|
||||
if (nlines < maxlines) {
|
||||
tmpstr = curses_break_str(linestarts[nlines - 1], width - 1, 1);
|
||||
mx = strlen(tmpstr) + border_space;
|
||||
mvwprintw(win, my, mx, "%*c", maxx - mx + 1, ' ');
|
||||
if (++my > maxy) {
|
||||
scroll_window(MESSAGE_WIN);
|
||||
my--;
|
||||
}
|
||||
mx = border_space;
|
||||
linestarts[nlines] = linestarts[nlines - 1] + strlen(tmpstr);
|
||||
if (*linestarts[nlines] == ' ') linestarts[nlines]++;
|
||||
mvwaddstr(win, my, mx, linestarts[nlines]);
|
||||
mx = strlen(linestarts[nlines]) + border_space;
|
||||
nlines++;
|
||||
free(tmpstr);
|
||||
} else {
|
||||
p_answer[--len] = '\0';
|
||||
mvwaddch(win, my, --mx, ' ');
|
||||
}
|
||||
}
|
||||
wmove(win, my, mx);
|
||||
curs_set(1);
|
||||
wrefresh(win);
|
||||
ch = getch();
|
||||
curs_set(0);
|
||||
switch(ch) {
|
||||
case '\033': /* DOESCAPE */
|
||||
/* blank the input but don't exit */
|
||||
while(nlines - 1 > promptline) {
|
||||
if (nlines-- > height) {
|
||||
unscroll_window(MESSAGE_WIN);
|
||||
tmpstr = curses_break_str(linestarts[nlines - height], width - 1, 1);
|
||||
mvwaddstr(win, border_space, border_space, tmpstr);
|
||||
free(tmpstr);
|
||||
} else {
|
||||
mx = border_space;
|
||||
mvwprintw(win, my, mx, "%*c", maxx - mx, ' ');
|
||||
my--;
|
||||
}
|
||||
}
|
||||
mx = promptx;
|
||||
mvwprintw(win, my, mx, "%*c", maxx - mx, ' ');
|
||||
*p_answer = '\0';
|
||||
len = 0;
|
||||
break;
|
||||
case ERR: /* should not happen */
|
||||
*answer = '\0';
|
||||
free(tmpbuf);
|
||||
free(linestarts);
|
||||
curs_set(orig_cursor);
|
||||
curses_toggle_color_attr(win, NONE, A_BOLD, OFF);
|
||||
return;
|
||||
case '\r':
|
||||
case '\n':
|
||||
free(linestarts);
|
||||
strncpy(answer, p_answer, buffer);
|
||||
strcpy(toplines, tmpbuf);
|
||||
mesg_add_line((char *) tmpbuf);
|
||||
free(tmpbuf);
|
||||
curs_set(orig_cursor);
|
||||
curses_toggle_color_attr(win, NONE, A_BOLD, OFF);
|
||||
if (++my > maxy) {
|
||||
scroll_window(MESSAGE_WIN);
|
||||
my--;
|
||||
}
|
||||
mx = border_space;
|
||||
return;
|
||||
case '\b':
|
||||
case KEY_BACKSPACE:
|
||||
if (len < 1) {
|
||||
len = 1;
|
||||
mx = promptx;
|
||||
}
|
||||
p_answer[--len] = '\0';
|
||||
mvwaddch(win, my, --mx, ' ');
|
||||
/* try to unwrap back to the previous line if there is one */
|
||||
if (nlines > 1 && strlen(linestarts[nlines - 2]) < (size_t) width) {
|
||||
mvwaddstr(win, my - 1, border_space, linestarts[nlines - 2]);
|
||||
if (nlines-- > height) {
|
||||
unscroll_window(MESSAGE_WIN);
|
||||
tmpstr = curses_break_str(linestarts[nlines - height], width - 1, 1);
|
||||
mvwaddstr(win, border_space, border_space, tmpstr);
|
||||
free(tmpstr);
|
||||
} else {
|
||||
/* clean up the leftovers on the next line, if we didn't scroll it away */
|
||||
mvwprintw(win, my--, border_space, "%*c", strlen(linestarts[nlines]), ' ');
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
p_answer[len++] = ch;
|
||||
if (len >= buffer) len = buffer-1;
|
||||
else mvwaddch(win, my, mx, ch);
|
||||
p_answer[len] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Scroll lines upward in given window, or clear window if only one line. */
|
||||
static void
|
||||
scroll_window(winid wid)
|
||||
{
|
||||
directional_scroll(wid,1);
|
||||
}
|
||||
|
||||
static void
|
||||
unscroll_window(winid wid)
|
||||
{
|
||||
directional_scroll(wid,-1);
|
||||
}
|
||||
|
||||
static void
|
||||
directional_scroll(winid wid, int nlines)
|
||||
{
|
||||
int wh, ww, s_top, s_bottom;
|
||||
boolean border = curses_window_has_border(wid);
|
||||
WINDOW *win = curses_get_nhwin(wid);
|
||||
|
||||
curses_get_window_size(wid, &wh, &ww);
|
||||
if (wh == 1) {
|
||||
curses_clear_nhwin(wid);
|
||||
return;
|
||||
}
|
||||
if (border) {
|
||||
s_top = 1;
|
||||
s_bottom = wh;
|
||||
} else {
|
||||
s_top = 0;
|
||||
s_bottom = wh - 1;
|
||||
}
|
||||
scrollok(win, TRUE);
|
||||
wsetscrreg(win, s_top, s_bottom);
|
||||
wscrl(win, nlines);
|
||||
scrollok(win, FALSE);
|
||||
if (wid == MESSAGE_WIN) {
|
||||
if (border)
|
||||
mx = 1;
|
||||
else
|
||||
mx = 0;
|
||||
}
|
||||
if (border) {
|
||||
box(win, 0, 0);
|
||||
}
|
||||
wrefresh(win);
|
||||
}
|
||||
|
||||
|
||||
/* Add given line to message history */
|
||||
|
||||
static void
|
||||
mesg_add_line(char *mline)
|
||||
{
|
||||
nhprev_mesg *tmp_mesg = NULL;
|
||||
nhprev_mesg *current_mesg = malloc(sizeof (nhprev_mesg));
|
||||
|
||||
current_mesg->str = curses_copy_of(mline);
|
||||
current_mesg->turn = moves;
|
||||
current_mesg->next_mesg = NULL;
|
||||
|
||||
if (num_messages == 0) {
|
||||
first_mesg = current_mesg;
|
||||
}
|
||||
|
||||
if (last_mesg != NULL) {
|
||||
last_mesg->next_mesg = current_mesg;
|
||||
}
|
||||
current_mesg->prev_mesg = last_mesg;
|
||||
last_mesg = current_mesg;
|
||||
|
||||
|
||||
if (num_messages < max_messages) {
|
||||
num_messages++;
|
||||
} else {
|
||||
tmp_mesg = first_mesg->next_mesg;
|
||||
free(first_mesg);
|
||||
first_mesg = tmp_mesg;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Returns specified line from message history, or NULL if out of bounds */
|
||||
|
||||
static nhprev_mesg *
|
||||
get_msg_line(boolean reverse, int mindex)
|
||||
{
|
||||
int count;
|
||||
nhprev_mesg *current_mesg;
|
||||
|
||||
if (reverse) {
|
||||
current_mesg = last_mesg;
|
||||
for (count = 0; count < mindex; count++) {
|
||||
if (current_mesg == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
current_mesg = current_mesg->prev_mesg;
|
||||
}
|
||||
return current_mesg;
|
||||
} else {
|
||||
current_mesg = first_mesg;
|
||||
for (count = 0; count < mindex; count++) {
|
||||
if (current_mesg == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
current_mesg = current_mesg->next_mesg;
|
||||
}
|
||||
return current_mesg;
|
||||
}
|
||||
}
|
||||
19
win/curses/cursmesg.h
Normal file
19
win/curses/cursmesg.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
|
||||
|
||||
#ifndef CURSMESG_H
|
||||
# define CURSMESG_H
|
||||
|
||||
|
||||
/* Global declarations */
|
||||
|
||||
void curses_message_win_puts(const char *message, boolean recursed);
|
||||
int curses_block(boolean require_tab);
|
||||
int curses_more(void);
|
||||
void curses_clear_unhighlight_message_window(void);
|
||||
void curses_message_win_getline(const char *prompt, char *answer, int buffer);
|
||||
void curses_last_messages(void);
|
||||
void curses_init_mesg_history(void);
|
||||
void curses_prev_mesg(void);
|
||||
void curses_count_window(const char *count_text);
|
||||
|
||||
#endif /* CURSMESG_H */
|
||||
880
win/curses/cursmisc.c
Normal file
880
win/curses/cursmisc.c
Normal file
@@ -0,0 +1,880 @@
|
||||
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
|
||||
|
||||
#include "curses.h"
|
||||
#include "hack.h"
|
||||
#include "wincurs.h"
|
||||
#include "cursmisc.h"
|
||||
#include "func_tab.h"
|
||||
#include "dlb.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
/* Misc. curses interface functions */
|
||||
|
||||
/* Private declarations */
|
||||
|
||||
static int curs_x = -1;
|
||||
static int curs_y = -1;
|
||||
|
||||
static int parse_escape_sequence(void);
|
||||
|
||||
/* Macros for Control and Alt keys */
|
||||
|
||||
#ifndef M
|
||||
# ifndef NHSTDC
|
||||
# define M(c) (0x80 | (c))
|
||||
# else
|
||||
# define M(c) ((c) - 128)
|
||||
# endif/* NHSTDC */
|
||||
#endif
|
||||
#ifndef C
|
||||
# define C(c) (0x1f & (c))
|
||||
#endif
|
||||
|
||||
|
||||
/* Read a character of input from the user */
|
||||
|
||||
int
|
||||
curses_read_char()
|
||||
{
|
||||
int ch, tmpch;
|
||||
|
||||
ch = getch();
|
||||
tmpch = ch;
|
||||
ch = curses_convert_keys(ch);
|
||||
|
||||
if (ch == 0) {
|
||||
ch = '\033'; /* map NUL to ESC since nethack doesn't expect NUL */
|
||||
}
|
||||
#if defined(ALT_0) && defined(ALT_9) /* PDCurses, maybe others */
|
||||
if ((ch >= ALT_0) && (ch <= ALT_9)) {
|
||||
tmpch = (ch - ALT_0) + '0';
|
||||
ch = M(tmpch);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ALT_A) && defined(ALT_Z) /* PDCurses, maybe others */
|
||||
if ((ch >= ALT_A) && (ch <= ALT_Z)) {
|
||||
tmpch = (ch - ALT_A) + 'a';
|
||||
ch = M(tmpch);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef KEY_RESIZE
|
||||
/* Handle resize events via get_nh_event, not this code */
|
||||
if (ch == KEY_RESIZE) {
|
||||
ch = '\033'; /* NetHack doesn't know what to do with KEY_RESIZE */
|
||||
}
|
||||
#endif
|
||||
|
||||
if (counting && !isdigit(ch)) { /* Dismiss count window if necissary */
|
||||
curses_count_window(NULL);
|
||||
curses_refresh_nethack_windows();
|
||||
}
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
/* Turn on or off the specified color and / or attribute */
|
||||
|
||||
void
|
||||
curses_toggle_color_attr(WINDOW * win, int color, int attr, int onoff)
|
||||
{
|
||||
#ifdef TEXTCOLOR
|
||||
int curses_color;
|
||||
|
||||
/* Map color disabled */
|
||||
if ((!iflags.wc_color) && (win == mapwin)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* GUI color disabled */
|
||||
// if ((!iflags.wc2_guicolor) && (win != mapwin)) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (color == 0) { /* make black fg visible */
|
||||
# ifdef USE_DARKGRAY
|
||||
if (iflags.wc2_darkgray) {
|
||||
if (can_change_color() && (COLORS > 16)) {
|
||||
/* colorpair for black is already darkgray */
|
||||
} else { /* Use bold for a bright black */
|
||||
|
||||
wattron(win, A_BOLD);
|
||||
}
|
||||
} else
|
||||
# endif/* USE_DARKGRAY */
|
||||
color = CLR_BLUE;
|
||||
}
|
||||
curses_color = color + 1;
|
||||
if (COLORS < 16) {
|
||||
if (curses_color > 8 && curses_color < 17)
|
||||
curses_color -= 8;
|
||||
else if (curses_color > (17 + 16))
|
||||
curses_color -= 16;
|
||||
}
|
||||
if (onoff == ON) { /* Turn on color/attributes */
|
||||
if (color != NONE) {
|
||||
if ((((color > 7) && (color < 17)) ||
|
||||
(color > 17 + 17)) && (COLORS < 16)) {
|
||||
wattron(win, A_BOLD);
|
||||
}
|
||||
wattron(win, COLOR_PAIR(curses_color));
|
||||
}
|
||||
|
||||
if (attr != NONE) {
|
||||
wattron(win, attr);
|
||||
}
|
||||
} else { /* Turn off color/attributes */
|
||||
|
||||
if (color != NONE) {
|
||||
if ((color > 7) && (COLORS < 16)) {
|
||||
wattroff(win, A_BOLD);
|
||||
}
|
||||
# ifdef USE_DARKGRAY
|
||||
if ((color == 0) && (!can_change_color() || (COLORS <= 16))) {
|
||||
wattroff(win, A_BOLD);
|
||||
}
|
||||
# else
|
||||
if (iflags.use_inverse) {
|
||||
wattroff(win, A_REVERSE);
|
||||
}
|
||||
# endif/* DARKGRAY */
|
||||
wattroff(win, COLOR_PAIR(curses_color));
|
||||
}
|
||||
|
||||
if (attr != NONE) {
|
||||
wattroff(win, attr);
|
||||
}
|
||||
}
|
||||
#endif /* TEXTCOLOR */
|
||||
}
|
||||
|
||||
|
||||
/* clean up and quit - taken from tty port */
|
||||
|
||||
void
|
||||
curses_bail(const char *mesg)
|
||||
{
|
||||
clearlocks();
|
||||
curses_exit_nhwindows(mesg);
|
||||
nh_terminate(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
/* Return a winid for a new window of the given type */
|
||||
|
||||
winid
|
||||
curses_get_wid(int type)
|
||||
{
|
||||
winid ret;
|
||||
static winid menu_wid = 20; /* Always even */
|
||||
static winid text_wid = 21; /* Always odd */
|
||||
|
||||
switch (type) {
|
||||
case NHW_MESSAGE:
|
||||
return MESSAGE_WIN;
|
||||
case NHW_MAP:
|
||||
return MAP_WIN;
|
||||
case NHW_STATUS:
|
||||
return STATUS_WIN;
|
||||
case NHW_MENU:
|
||||
ret = menu_wid;
|
||||
break;
|
||||
case NHW_TEXT:
|
||||
ret = text_wid;
|
||||
break;
|
||||
default:
|
||||
panic("curses_get_wid: unsupported window type");
|
||||
ret = -1; /* Not reached */
|
||||
}
|
||||
|
||||
while (curses_window_exists(ret)) {
|
||||
ret += 2;
|
||||
if ((ret + 2) > 10000) { /* Avoid "wid2k" problem */
|
||||
ret -= 9900;
|
||||
}
|
||||
}
|
||||
|
||||
if (type == NHW_MENU) {
|
||||
menu_wid += 2;
|
||||
} else {
|
||||
text_wid += 2;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Allocate a copy of the given string. If null, return a string of
|
||||
* zero length.
|
||||
*
|
||||
* This is taken from copy_of() in tty/wintty.c.
|
||||
*/
|
||||
|
||||
char *
|
||||
curses_copy_of(const char *s)
|
||||
{
|
||||
if (!s)
|
||||
s = "";
|
||||
return strcpy((char *) alloc((unsigned) (strlen(s) + 1)), s);
|
||||
}
|
||||
|
||||
|
||||
/* Determine the number of lines needed for a string for a dialog window
|
||||
of the given width */
|
||||
|
||||
int
|
||||
curses_num_lines(const char *str, int width)
|
||||
{
|
||||
int last_space, count;
|
||||
int curline = 1;
|
||||
char substr[BUFSZ];
|
||||
char tmpstr[BUFSZ];
|
||||
|
||||
strncpy(substr, str, BUFSZ-1);
|
||||
substr[BUFSZ-1] = '\0';
|
||||
|
||||
while (strlen(substr) > (size_t) width) {
|
||||
last_space = 0;
|
||||
|
||||
for (count = 0; count <= width; count++) {
|
||||
if (substr[count] == ' ')
|
||||
last_space = count;
|
||||
|
||||
}
|
||||
if (last_space == 0) { /* No spaces found */
|
||||
last_space = count - 1;
|
||||
}
|
||||
for (count = (last_space + 1); (size_t) count < strlen(substr); count++) {
|
||||
tmpstr[count - (last_space + 1)] = substr[count];
|
||||
}
|
||||
tmpstr[count - (last_space + 1)] = '\0';
|
||||
strcpy(substr, tmpstr);
|
||||
curline++;
|
||||
}
|
||||
|
||||
return curline;
|
||||
}
|
||||
|
||||
|
||||
/* Break string into smaller lines to fit into a dialog window of the
|
||||
given width */
|
||||
|
||||
char *
|
||||
curses_break_str(const char *str, int width, int line_num)
|
||||
{
|
||||
int last_space, count;
|
||||
char *retstr;
|
||||
int curline = 0;
|
||||
int strsize = strlen(str) + 1;
|
||||
#if __STDC_VERSION__ >= 199901L
|
||||
char substr[strsize];
|
||||
char curstr[strsize];
|
||||
char tmpstr[strsize];
|
||||
|
||||
strcpy(substr, str);
|
||||
#else
|
||||
#ifndef BUFSZ
|
||||
#define BUFSZ 256
|
||||
#endif
|
||||
char substr[BUFSZ * 2];
|
||||
char curstr[BUFSZ * 2];
|
||||
char tmpstr[BUFSZ * 2];
|
||||
|
||||
if (strsize > (BUFSZ * 2) - 1) {
|
||||
paniclog("curses", "curses_break_str() string too long.");
|
||||
strncpy(substr, str, (BUFSZ * 2) - 2);
|
||||
substr[(BUFSZ * 2) - 1] = '\0';
|
||||
} else
|
||||
strcpy(substr, str);
|
||||
#endif
|
||||
|
||||
while (curline < line_num) {
|
||||
if (strlen(substr) == 0) {
|
||||
break;
|
||||
}
|
||||
curline++;
|
||||
last_space = 0;
|
||||
for (count = 0; count <= width; count++) {
|
||||
if (substr[count] == ' ') {
|
||||
last_space = count;
|
||||
} else if (substr[count] == '\0') {
|
||||
last_space = count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (last_space == 0) { /* No spaces found */
|
||||
last_space = count - 1;
|
||||
}
|
||||
for (count = 0; count < last_space; count++) {
|
||||
curstr[count] = substr[count];
|
||||
}
|
||||
curstr[count] = '\0';
|
||||
if (substr[count] == '\0') {
|
||||
break;
|
||||
}
|
||||
for (count = (last_space + 1); (size_t) count < strlen(substr); count++) {
|
||||
tmpstr[count - (last_space + 1)] = substr[count];
|
||||
}
|
||||
tmpstr[count - (last_space + 1)] = '\0';
|
||||
strcpy(substr, tmpstr);
|
||||
}
|
||||
|
||||
if (curline < line_num) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
retstr = curses_copy_of(curstr);
|
||||
|
||||
return retstr;
|
||||
}
|
||||
|
||||
|
||||
/* Return the remaining portion of a string after hacking-off line_num lines */
|
||||
|
||||
char *
|
||||
curses_str_remainder(const char *str, int width, int line_num)
|
||||
{
|
||||
int last_space, count;
|
||||
char *retstr;
|
||||
int curline = 0;
|
||||
int strsize = strlen(str) + 1;
|
||||
#if __STDC_VERSION__ >= 199901L
|
||||
char substr[strsize];
|
||||
char curstr[strsize];
|
||||
char tmpstr[strsize];
|
||||
|
||||
strcpy(substr, str);
|
||||
#else
|
||||
#ifndef BUFSZ
|
||||
#define BUFSZ 256
|
||||
#endif
|
||||
char substr[BUFSZ * 2];
|
||||
char curstr[BUFSZ * 2];
|
||||
char tmpstr[BUFSZ * 2];
|
||||
|
||||
if (strsize > (BUFSZ * 2) - 1) {
|
||||
paniclog("curses", "curses_str_remainder() string too long.");
|
||||
strncpy(substr, str, (BUFSZ * 2) - 2);
|
||||
substr[(BUFSZ * 2) - 1] = '\0';
|
||||
} else
|
||||
strcpy(substr, str);
|
||||
#endif
|
||||
|
||||
while (curline < line_num) {
|
||||
if (strlen(substr) == 0) {
|
||||
break;
|
||||
}
|
||||
curline++;
|
||||
last_space = 0;
|
||||
for (count = 0; count <= width; count++) {
|
||||
if (substr[count] == ' ') {
|
||||
last_space = count;
|
||||
} else if (substr[count] == '\0') {
|
||||
last_space = count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (last_space == 0) { /* No spaces found */
|
||||
last_space = count - 1;
|
||||
}
|
||||
for (count = 0; count < last_space; count++) {
|
||||
curstr[count] = substr[count];
|
||||
}
|
||||
curstr[count] = '\0';
|
||||
if (substr[count] == '\0') {
|
||||
break;
|
||||
}
|
||||
for (count = (last_space + 1); (size_t) count < strlen(substr); count++) {
|
||||
tmpstr[count - (last_space + 1)] = substr[count];
|
||||
}
|
||||
tmpstr[count - (last_space + 1)] = '\0';
|
||||
strcpy(substr, tmpstr);
|
||||
}
|
||||
|
||||
if (curline < line_num) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
retstr = curses_copy_of(substr);
|
||||
|
||||
return retstr;
|
||||
}
|
||||
|
||||
|
||||
/* Determine if the given NetHack winid is a menu window */
|
||||
|
||||
boolean
|
||||
curses_is_menu(winid wid)
|
||||
{
|
||||
if ((wid > 19) && !(wid % 2)) { /* Even number */
|
||||
return TRUE;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Determine if the given NetHack winid is a text window */
|
||||
|
||||
boolean
|
||||
curses_is_text(winid wid)
|
||||
{
|
||||
if ((wid > 19) && (wid % 2)) { /* Odd number */
|
||||
return TRUE;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Replace certain characters with portable drawing characters if
|
||||
cursesgraphics option is enabled */
|
||||
|
||||
int
|
||||
curses_convert_glyph(int ch, int glyph)
|
||||
{
|
||||
int symbol;
|
||||
|
||||
if (Is_rogue_level(&u.uz)) {
|
||||
return ch;
|
||||
}
|
||||
|
||||
/* Save some processing time by returning if the glyph represents
|
||||
an object that we don't have custom characters for */
|
||||
if (!glyph_is_cmap(glyph)) {
|
||||
return ch;
|
||||
}
|
||||
|
||||
symbol = glyph_to_cmap(glyph);
|
||||
|
||||
/* If user selected a custom character for this object, don't
|
||||
override this. */
|
||||
if (((glyph_is_cmap(glyph)) && (ch != showsyms[symbol]))) {
|
||||
return ch;
|
||||
}
|
||||
|
||||
switch (symbol) {
|
||||
case S_vwall:
|
||||
return ACS_VLINE;
|
||||
case S_hwall:
|
||||
return ACS_HLINE;
|
||||
case S_tlcorn:
|
||||
return ACS_ULCORNER;
|
||||
case S_trcorn:
|
||||
return ACS_URCORNER;
|
||||
case S_blcorn:
|
||||
return ACS_LLCORNER;
|
||||
case S_brcorn:
|
||||
return ACS_LRCORNER;
|
||||
case S_crwall:
|
||||
return ACS_PLUS;
|
||||
case S_tuwall:
|
||||
return ACS_BTEE;
|
||||
case S_tdwall:
|
||||
return ACS_TTEE;
|
||||
case S_tlwall:
|
||||
return ACS_RTEE;
|
||||
case S_trwall:
|
||||
return ACS_LTEE;
|
||||
case S_tree:
|
||||
return ACS_PLMINUS;
|
||||
case S_corr:
|
||||
return ACS_CKBOARD;
|
||||
case S_litcorr:
|
||||
return ACS_CKBOARD;
|
||||
}
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
|
||||
/* Move text cursor to specified coordinates in the given NetHack window */
|
||||
|
||||
void
|
||||
curses_move_cursor(winid wid, int x, int y)
|
||||
{
|
||||
int sx, sy, ex, ey;
|
||||
int xadj = 0;
|
||||
int yadj = 0;
|
||||
|
||||
#ifndef PDCURSES
|
||||
WINDOW *win = curses_get_nhwin(MAP_WIN);
|
||||
#endif
|
||||
|
||||
if (wid != MAP_WIN) {
|
||||
return;
|
||||
}
|
||||
#ifdef PDCURSES
|
||||
/* PDCurses seems to not handle wmove correctly, so we use move and
|
||||
physical screen coordinates instead */
|
||||
curses_get_window_xy(wid, &xadj, &yadj);
|
||||
#endif
|
||||
curs_x = x + xadj;
|
||||
curs_y = y + yadj;
|
||||
curses_map_borders(&sx, &sy, &ex, &ey, x, y);
|
||||
|
||||
if (curses_window_has_border(wid)) {
|
||||
curs_x++;
|
||||
curs_y++;
|
||||
}
|
||||
|
||||
if ((x >= sx) && (x <= ex) && (y >= sy) && (y <= ey)) {
|
||||
curs_x -= sx;
|
||||
curs_y -= sy;
|
||||
#ifdef PDCURSES
|
||||
move(curs_y, curs_x);
|
||||
#else
|
||||
wmove(win, curs_y, curs_x);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Perform actions that should be done every turn before nhgetch() */
|
||||
|
||||
void
|
||||
curses_prehousekeeping()
|
||||
{
|
||||
#ifndef PDCURSES
|
||||
WINDOW *win = curses_get_nhwin(MAP_WIN);
|
||||
#endif /* PDCURSES */
|
||||
|
||||
if ((curs_x > -1) && (curs_y > -1)) {
|
||||
curs_set(1);
|
||||
#ifdef PDCURSES
|
||||
/* PDCurses seems to not handle wmove correctly, so we use move
|
||||
and physical screen coordinates instead */
|
||||
move(curs_y, curs_x);
|
||||
#else
|
||||
wmove(win, curs_y, curs_x);
|
||||
#endif /* PDCURSES */
|
||||
curses_refresh_nhwin(MAP_WIN);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Perform actions that should be done every turn after nhgetch() */
|
||||
|
||||
void
|
||||
curses_posthousekeeping()
|
||||
{
|
||||
curs_set(0);
|
||||
//curses_decrement_highlights(FALSE);
|
||||
curses_clear_unhighlight_message_window();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
curses_view_file(const char *filename, boolean must_exist)
|
||||
{
|
||||
winid wid;
|
||||
anything *identifier;
|
||||
char buf[BUFSZ];
|
||||
menu_item *selected = NULL;
|
||||
dlb *fp = dlb_fopen(filename, "r");
|
||||
|
||||
if ((fp == NULL) && (must_exist)) {
|
||||
pline("Cannot open %s for reading!", filename);
|
||||
}
|
||||
|
||||
if (fp == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
wid = curses_get_wid(NHW_MENU);
|
||||
curses_create_nhmenu(wid);
|
||||
identifier = malloc(sizeof (anything));
|
||||
identifier->a_void = NULL;
|
||||
|
||||
while (dlb_fgets(buf, BUFSZ, fp) != NULL) {
|
||||
curses_add_menu(wid, NO_GLYPH, identifier, 0, 0, A_NORMAL, buf, FALSE);
|
||||
}
|
||||
|
||||
dlb_fclose(fp);
|
||||
curses_end_menu(wid, "");
|
||||
curses_select_menu(wid, PICK_NONE, &selected);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
curses_rtrim(char *str)
|
||||
{
|
||||
char *s;
|
||||
|
||||
for (s = str; *s != '\0'; ++s);
|
||||
for (--s; isspace(*s) && s > str; --s);
|
||||
if (s == str)
|
||||
*s = '\0';
|
||||
else
|
||||
*(++s) = '\0';
|
||||
}
|
||||
|
||||
|
||||
/* Read numbers until non-digit is encountered, and return number
|
||||
in int form. */
|
||||
|
||||
int
|
||||
curses_get_count(int first_digit)
|
||||
{
|
||||
long current_count = first_digit;
|
||||
int current_char;
|
||||
|
||||
current_char = curses_read_char();
|
||||
|
||||
while (isdigit(current_char)) {
|
||||
current_count = (current_count * 10) + (current_char - '0');
|
||||
if (current_count > LARGEST_INT) {
|
||||
current_count = LARGEST_INT;
|
||||
}
|
||||
|
||||
pline("Count: %ld", current_count);
|
||||
current_char = curses_read_char();
|
||||
}
|
||||
|
||||
ungetch(current_char);
|
||||
|
||||
if (current_char == '\033') { /* Cancelled with escape */
|
||||
current_count = -1;
|
||||
}
|
||||
|
||||
return current_count;
|
||||
}
|
||||
|
||||
|
||||
/* Convert the given NetHack text attributes into the format curses
|
||||
understands, and return that format mask. */
|
||||
|
||||
int
|
||||
curses_convert_attr(int attr)
|
||||
{
|
||||
int curses_attr;
|
||||
|
||||
switch (attr) {
|
||||
case ATR_NONE:
|
||||
curses_attr = A_NORMAL;
|
||||
break;
|
||||
case ATR_ULINE:
|
||||
curses_attr = A_UNDERLINE;
|
||||
break;
|
||||
case ATR_BOLD:
|
||||
curses_attr = A_BOLD;
|
||||
break;
|
||||
case ATR_BLINK:
|
||||
curses_attr = A_BLINK;
|
||||
break;
|
||||
case ATR_INVERSE:
|
||||
curses_attr = A_REVERSE;
|
||||
break;
|
||||
default:
|
||||
curses_attr = A_NORMAL;
|
||||
}
|
||||
|
||||
return curses_attr;
|
||||
}
|
||||
|
||||
|
||||
/* Map letter attributes from a string to bitmask. Return mask on
|
||||
success, or 0 if not found */
|
||||
|
||||
int
|
||||
curses_read_attrs(char *attrs)
|
||||
{
|
||||
int retattr = 0;
|
||||
|
||||
if (strchr(attrs, 'b') || strchr(attrs, 'B')) {
|
||||
retattr = retattr | A_BOLD;
|
||||
}
|
||||
if (strchr(attrs, 'i') || strchr(attrs, 'I')) {
|
||||
retattr = retattr | A_REVERSE;
|
||||
}
|
||||
if (strchr(attrs, 'u') || strchr(attrs, 'U')) {
|
||||
retattr = retattr | A_UNDERLINE;
|
||||
}
|
||||
if (strchr(attrs, 'k') || strchr(attrs, 'K')) {
|
||||
retattr = retattr | A_BLINK;
|
||||
}
|
||||
#ifdef A_ITALIC
|
||||
if (strchr(attrs, 't') || strchr(attrs, 'T')) {
|
||||
retattr = retattr | A_ITALIC;
|
||||
}
|
||||
#endif
|
||||
#ifdef A_RIGHTLINE
|
||||
if (strchr(attrs, 'r') || strchr(attrs, 'R')) {
|
||||
retattr = retattr | A_RIGHTLINE;
|
||||
}
|
||||
#endif
|
||||
#ifdef A_LEFTLINE
|
||||
if (strchr(attrs, 'l') || strchr(attrs, 'L')) {
|
||||
retattr = retattr | A_LEFTLINE;
|
||||
}
|
||||
#endif
|
||||
|
||||
return retattr;
|
||||
}
|
||||
|
||||
|
||||
/* Convert special keys into values that NetHack can understand.
|
||||
Currently this is limited to arrow keys, but this may be expanded. */
|
||||
|
||||
int
|
||||
curses_convert_keys(int key)
|
||||
{
|
||||
int ret = key;
|
||||
|
||||
if (ret == '\033') {
|
||||
ret = parse_escape_sequence();
|
||||
}
|
||||
|
||||
/* Handle arrow keys */
|
||||
switch (key) {
|
||||
case KEY_LEFT:
|
||||
if (iflags.num_pad) {
|
||||
ret = '4';
|
||||
} else {
|
||||
ret = 'h';
|
||||
}
|
||||
break;
|
||||
case KEY_RIGHT:
|
||||
if (iflags.num_pad) {
|
||||
ret = '6';
|
||||
} else {
|
||||
ret = 'l';
|
||||
}
|
||||
break;
|
||||
case KEY_UP:
|
||||
if (iflags.num_pad) {
|
||||
ret = '8';
|
||||
} else {
|
||||
ret = 'k';
|
||||
}
|
||||
break;
|
||||
case KEY_DOWN:
|
||||
if (iflags.num_pad) {
|
||||
ret = '2';
|
||||
} else {
|
||||
ret = 'j';
|
||||
}
|
||||
break;
|
||||
#ifdef KEY_A1
|
||||
case KEY_A1:
|
||||
if (iflags.num_pad) {
|
||||
ret = '7';
|
||||
} else {
|
||||
ret = 'y';
|
||||
}
|
||||
break;
|
||||
#endif /* KEY_A1 */
|
||||
#ifdef KEY_A3
|
||||
case KEY_A3:
|
||||
if (iflags.num_pad) {
|
||||
ret = '9';
|
||||
} else {
|
||||
ret = 'u';
|
||||
}
|
||||
break;
|
||||
#endif /* KEY_A3 */
|
||||
#ifdef KEY_C1
|
||||
case KEY_C1:
|
||||
if (iflags.num_pad) {
|
||||
ret = '1';
|
||||
} else {
|
||||
ret = 'b';
|
||||
}
|
||||
break;
|
||||
#endif /* KEY_C1 */
|
||||
#ifdef KEY_C3
|
||||
case KEY_C3:
|
||||
if (iflags.num_pad) {
|
||||
ret = '3';
|
||||
} else {
|
||||
ret = 'n';
|
||||
}
|
||||
break;
|
||||
#endif /* KEY_C3 */
|
||||
#ifdef KEY_B2
|
||||
case KEY_B2:
|
||||
if (iflags.num_pad) {
|
||||
ret = '5';
|
||||
} else {
|
||||
ret = 'g';
|
||||
}
|
||||
break;
|
||||
#endif /* KEY_B2 */
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Process mouse events. Mouse movement is processed until no further
|
||||
mouse movement events are available. Returns 0 for a mouse click
|
||||
event, or the first non-mouse key event in the case of mouse
|
||||
movement. */
|
||||
|
||||
int
|
||||
curses_get_mouse(int *mousex, int *mousey, int *mod)
|
||||
{
|
||||
int key = '\033';
|
||||
|
||||
#ifdef NCURSES_MOUSE_VERSION
|
||||
MEVENT event;
|
||||
|
||||
if (getmouse(&event) == OK) { /* When the user clicks left mouse button */
|
||||
if (event.bstate & BUTTON1_CLICKED) {
|
||||
/* See if coords are in map window & convert coords */
|
||||
if (wmouse_trafo(mapwin, &event.y, &event.x, TRUE)) {
|
||||
key = 0; /* Flag mouse click */
|
||||
*mousex = event.x;
|
||||
*mousey = event.y;
|
||||
|
||||
if (curses_window_has_border(MAP_WIN)) {
|
||||
(*mousex)--;
|
||||
(*mousey)--;
|
||||
}
|
||||
|
||||
*mod = CLICK_1;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* NCURSES_MOUSE_VERSION */
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
parse_escape_sequence(void)
|
||||
{
|
||||
#ifndef PDCURSES
|
||||
int ret;
|
||||
|
||||
timeout(10);
|
||||
|
||||
ret = getch();
|
||||
|
||||
if (ret != ERR) { /* Likely an escape sequence */
|
||||
if (((ret >= 'a') && (ret <= 'z')) || ((ret >= '0') && (ret <= '9'))) {
|
||||
ret |= 0x80; /* Meta key support for most terminals */
|
||||
} else if (ret == 'O') { /* Numeric keypad */
|
||||
ret = getch();
|
||||
if ((ret != ERR) && (ret >= 112) && (ret <= 121)) {
|
||||
ret = ret - 112 + '0'; /* Convert to number */
|
||||
} else {
|
||||
ret = '\033'; /* Escape */
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret = '\033'; /* Just an escape character */
|
||||
}
|
||||
|
||||
timeout(-1);
|
||||
|
||||
return ret;
|
||||
#else
|
||||
return '\033';
|
||||
#endif /* !PDCURSES */
|
||||
}
|
||||
|
||||
30
win/curses/cursmisc.h
Normal file
30
win/curses/cursmisc.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
|
||||
|
||||
#ifndef CURSMISC_H
|
||||
# define CURSMISC_H
|
||||
|
||||
/* Global declarations */
|
||||
|
||||
int curses_read_char(void);
|
||||
void curses_toggle_color_attr(WINDOW * win, int color, int attr, int onoff);
|
||||
void curses_bail(const char *mesg);
|
||||
winid curses_get_wid(int type);
|
||||
char *curses_copy_of(const char *s);
|
||||
int curses_num_lines(const char *str, int width);
|
||||
char *curses_break_str(const char *str, int width, int line_num);
|
||||
char *curses_str_remainder(const char *str, int width, int line_num);
|
||||
boolean curses_is_menu(winid wid);
|
||||
boolean curses_is_text(winid wid);
|
||||
int curses_convert_glyph(int ch, int glyph);
|
||||
void curses_move_cursor(winid wid, int x, int y);
|
||||
void curses_prehousekeeping(void);
|
||||
void curses_posthousekeeping(void);
|
||||
void curses_view_file(const char *filename, boolean must_exist);
|
||||
void curses_rtrim(char *str);
|
||||
int curses_get_count(int first_digit);
|
||||
int curses_convert_attr(int attr);
|
||||
int curses_read_attrs(char *attrs);
|
||||
int curses_convert_keys(int key);
|
||||
int curses_get_mouse(int *mousex, int *mousey, int *mod);
|
||||
|
||||
#endif /* CURSMISC_H */
|
||||
1583
win/curses/cursstat.c
Normal file
1583
win/curses/cursstat.c
Normal file
File diff suppressed because it is too large
Load Diff
21
win/curses/cursstat.h
Normal file
21
win/curses/cursstat.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
|
||||
|
||||
#ifndef CURSSTAT_H
|
||||
# define CURSSTAT_H
|
||||
|
||||
/* Used by handle_stat_change to handle some stats differently. Not an enum
|
||||
because this is how NetHack code generally handles them. */
|
||||
# define STAT_OTHER 0
|
||||
# define STAT_STR 1
|
||||
# define STAT_GOLD 2
|
||||
# define STAT_AC 4
|
||||
# define STAT_TIME 5
|
||||
# define STAT_TROUBLE 6
|
||||
|
||||
/* Global declarations */
|
||||
|
||||
void curses_update_stats();
|
||||
void curses_decrement_highlights(boolean);
|
||||
attr_t curses_color_attr(int nh_color, int bg_color);
|
||||
|
||||
#endif /* CURSSTAT_H */
|
||||
752
win/curses/curswins.c
Normal file
752
win/curses/curswins.c
Normal file
@@ -0,0 +1,752 @@
|
||||
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
|
||||
|
||||
#include "curses.h"
|
||||
#include "hack.h"
|
||||
#include "wincurs.h"
|
||||
#include "curswins.h"
|
||||
|
||||
/* Window handling for curses interface */
|
||||
|
||||
/* Private declarations */
|
||||
|
||||
typedef struct nhw {
|
||||
winid nhwin; /* NetHack window id */
|
||||
WINDOW *curwin; /* Curses window pointer */
|
||||
int width; /* Usable width not counting border */
|
||||
int height; /* Usable height not counting border */
|
||||
int x; /* start of window on terminal (left) */
|
||||
int y; /* start of window on termial (top) */
|
||||
int orientation; /* Placement of window relative to map */
|
||||
boolean border; /* Whether window has a visible border */
|
||||
} nethack_window;
|
||||
|
||||
typedef struct nhwd {
|
||||
winid nhwid; /* NetHack window id */
|
||||
struct nhwd *prev_wid; /* Pointer to previous entry */
|
||||
struct nhwd *next_wid; /* Pointer to next entry */
|
||||
} nethack_wid;
|
||||
|
||||
typedef struct nhchar {
|
||||
int ch; /* character */
|
||||
int color; /* color info for character */
|
||||
int attr; /* attributes of character */
|
||||
} nethack_char;
|
||||
|
||||
static boolean map_clipped; /* Map window smaller than 80x21 */
|
||||
static nethack_window nhwins[NHWIN_MAX]; /* NetHack window array */
|
||||
static nethack_char map[ROWNO][COLNO]; /* Map window contents */
|
||||
static nethack_wid *nhwids = NULL; /* NetHack wid array */
|
||||
|
||||
static boolean is_main_window(winid wid);
|
||||
static void write_char(WINDOW * win, int x, int y, nethack_char ch);
|
||||
static void clear_map(void);
|
||||
|
||||
/* Create a window with the specified size and orientation */
|
||||
|
||||
WINDOW *
|
||||
curses_create_window(int width, int height, orient orientation)
|
||||
{
|
||||
int mapx, mapy, maph, mapw = 0;
|
||||
int startx = 0;
|
||||
int starty = 0;
|
||||
WINDOW *win;
|
||||
boolean map_border = FALSE;
|
||||
int mapb_offset = 0;
|
||||
|
||||
if ((orientation == UP) || (orientation == DOWN) ||
|
||||
(orientation == LEFT) || (orientation == RIGHT)) {
|
||||
if (invent || (moves > 1)) {
|
||||
map_border = curses_window_has_border(MAP_WIN);
|
||||
curses_get_window_xy(MAP_WIN, &mapx, &mapy);
|
||||
curses_get_window_size(MAP_WIN, &maph, &mapw);
|
||||
} else {
|
||||
map_border = TRUE;
|
||||
mapx = 0;
|
||||
mapy = 0;
|
||||
maph = term_rows;
|
||||
mapw = term_cols;
|
||||
}
|
||||
}
|
||||
|
||||
if (map_border) {
|
||||
mapb_offset = 1;
|
||||
}
|
||||
|
||||
width += 2; /* leave room for bounding box */
|
||||
height += 2;
|
||||
|
||||
if ((width > term_cols) || (height > term_rows))
|
||||
panic("curses_create_window: Terminal too small for dialog window");
|
||||
switch (orientation) {
|
||||
case CENTER:
|
||||
startx = (term_cols / 2) - (width / 2);
|
||||
starty = (term_rows / 2) - (height / 2);
|
||||
break;
|
||||
case UP:
|
||||
if (invent || (moves > 1)) {
|
||||
startx = (mapw / 2) - (width / 2) + mapx + mapb_offset;
|
||||
} else {
|
||||
startx = 0;
|
||||
}
|
||||
|
||||
starty = mapy + mapb_offset;
|
||||
break;
|
||||
case DOWN:
|
||||
if (invent || (moves > 1)) {
|
||||
startx = (mapw / 2) - (width / 2) + mapx + mapb_offset;
|
||||
} else {
|
||||
startx = 0;
|
||||
}
|
||||
|
||||
starty = height - mapy - 1 - mapb_offset;
|
||||
break;
|
||||
case LEFT:
|
||||
if (map_border && (width < term_cols))
|
||||
startx = 1;
|
||||
else
|
||||
startx = 0;
|
||||
starty = term_rows - height;
|
||||
break;
|
||||
case RIGHT:
|
||||
if (invent || (moves > 1)) {
|
||||
startx = (mapw + mapx + (mapb_offset * 2)) - width;
|
||||
} else {
|
||||
startx = term_cols - width;
|
||||
}
|
||||
|
||||
starty = 0;
|
||||
break;
|
||||
default:
|
||||
panic("curses_create_window: Bad orientation");
|
||||
break;
|
||||
}
|
||||
|
||||
if (startx < 0) {
|
||||
startx = 0;
|
||||
}
|
||||
|
||||
if (starty < 0) {
|
||||
starty = 0;
|
||||
}
|
||||
|
||||
win = newwin(height, width, starty, startx);
|
||||
curses_toggle_color_attr(win, DIALOG_BORDER_COLOR, NONE, ON);
|
||||
box(win, 0, 0);
|
||||
curses_toggle_color_attr(win, DIALOG_BORDER_COLOR, NONE, OFF);
|
||||
return win;
|
||||
}
|
||||
|
||||
|
||||
/* Erase and delete curses window, and refresh standard windows */
|
||||
|
||||
void
|
||||
curses_destroy_win(WINDOW * win)
|
||||
{
|
||||
werase(win);
|
||||
wrefresh(win);
|
||||
delwin(win);
|
||||
curses_refresh_nethack_windows();
|
||||
}
|
||||
|
||||
|
||||
/* Refresh nethack windows if they exist, or base window if not */
|
||||
|
||||
void
|
||||
curses_refresh_nethack_windows()
|
||||
{
|
||||
WINDOW *status_window, *message_window, *map_window, *inv_window;
|
||||
|
||||
status_window = curses_get_nhwin(STATUS_WIN);
|
||||
message_window = curses_get_nhwin(MESSAGE_WIN);
|
||||
map_window = curses_get_nhwin(MAP_WIN);
|
||||
inv_window = curses_get_nhwin(INV_WIN);
|
||||
|
||||
if ((moves <= 1) && !invent) {
|
||||
/* Main windows not yet displayed; refresh base window instead */
|
||||
touchwin(stdscr);
|
||||
refresh();
|
||||
} else {
|
||||
touchwin(status_window);
|
||||
wnoutrefresh(status_window);
|
||||
touchwin(map_window);
|
||||
wnoutrefresh(map_window);
|
||||
touchwin(message_window);
|
||||
wnoutrefresh(message_window);
|
||||
if (inv_window) {
|
||||
touchwin(inv_window);
|
||||
wnoutrefresh(inv_window);
|
||||
}
|
||||
doupdate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Return curses window pointer for given NetHack winid */
|
||||
|
||||
WINDOW *
|
||||
curses_get_nhwin(winid wid)
|
||||
{
|
||||
if (!is_main_window(wid)) {
|
||||
panic("curses_get_nhwin: wid out of range. Not a main window.");
|
||||
}
|
||||
|
||||
return nhwins[wid].curwin;
|
||||
}
|
||||
|
||||
|
||||
/* Add curses window pointer and window info to list for given NetHack winid */
|
||||
|
||||
void
|
||||
curses_add_nhwin(winid wid, int height, int width, int y, int x,
|
||||
orient orientation, boolean border)
|
||||
{
|
||||
WINDOW *win;
|
||||
int real_width = width;
|
||||
int real_height = height;
|
||||
|
||||
if (!is_main_window(wid)) {
|
||||
panic("curses_add_nhwin: wid out of range. Not a main window.");
|
||||
}
|
||||
|
||||
nhwins[wid].nhwin = wid;
|
||||
nhwins[wid].border = border;
|
||||
nhwins[wid].width = width;
|
||||
nhwins[wid].height = height;
|
||||
nhwins[wid].x = x;
|
||||
nhwins[wid].y = y;
|
||||
nhwins[wid].orientation = orientation;
|
||||
|
||||
if (border) {
|
||||
real_width += 2; /* leave room for bounding box */
|
||||
real_height += 2;
|
||||
}
|
||||
|
||||
win = newwin(real_height, real_width, y, x);
|
||||
|
||||
switch (wid) {
|
||||
case MESSAGE_WIN:
|
||||
messagewin = win;
|
||||
break;
|
||||
case STATUS_WIN:
|
||||
statuswin = win;
|
||||
break;
|
||||
case MAP_WIN:
|
||||
mapwin = win;
|
||||
|
||||
if ((width < COLNO) || (height < ROWNO)) {
|
||||
map_clipped = TRUE;
|
||||
} else {
|
||||
map_clipped = FALSE;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (border) {
|
||||
box(win, 0, 0);
|
||||
}
|
||||
|
||||
nhwins[wid].curwin = win;
|
||||
}
|
||||
|
||||
|
||||
/* Add wid to list of known window IDs */
|
||||
|
||||
void
|
||||
curses_add_wid(winid wid)
|
||||
{
|
||||
nethack_wid *new_wid;
|
||||
nethack_wid *widptr = nhwids;
|
||||
|
||||
new_wid = malloc(sizeof (nethack_wid));
|
||||
new_wid->nhwid = wid;
|
||||
|
||||
new_wid->next_wid = NULL;
|
||||
|
||||
if (widptr == NULL) {
|
||||
new_wid->prev_wid = NULL;
|
||||
nhwids = new_wid;
|
||||
} else {
|
||||
while (widptr->next_wid != NULL) {
|
||||
widptr = widptr->next_wid;
|
||||
}
|
||||
new_wid->prev_wid = widptr;
|
||||
widptr->next_wid = new_wid;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* refresh a curses window via given nethack winid */
|
||||
|
||||
void
|
||||
curses_refresh_nhwin(winid wid)
|
||||
{
|
||||
wnoutrefresh(curses_get_nhwin(wid));
|
||||
doupdate();
|
||||
}
|
||||
|
||||
|
||||
/* Delete curses window via given NetHack winid and remove entry from list */
|
||||
|
||||
void
|
||||
curses_del_nhwin(winid wid)
|
||||
{
|
||||
if (curses_is_menu(wid) || curses_is_text(wid)) {
|
||||
curses_del_menu(wid);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_main_window(wid)) {
|
||||
panic("curses_del_nhwin: wid out of range. Not a main window.");
|
||||
}
|
||||
|
||||
nhwins[wid].curwin = NULL;
|
||||
nhwins[wid].nhwin = -1;
|
||||
}
|
||||
|
||||
|
||||
/* Delete wid from list of known window IDs */
|
||||
|
||||
void
|
||||
curses_del_wid(winid wid)
|
||||
{
|
||||
nethack_wid *tmpwid;
|
||||
nethack_wid *widptr = nhwids;
|
||||
|
||||
if (curses_is_menu(wid) || curses_is_text(wid)) {
|
||||
curses_del_menu(wid);
|
||||
}
|
||||
|
||||
while (widptr != NULL) {
|
||||
if (widptr->nhwid == wid) {
|
||||
if (widptr->prev_wid != NULL) {
|
||||
tmpwid = widptr->prev_wid;
|
||||
tmpwid->next_wid = widptr->next_wid;
|
||||
} else {
|
||||
nhwids = widptr->next_wid; /* New head mode, or NULL */
|
||||
}
|
||||
if (widptr->next_wid != NULL) {
|
||||
tmpwid = widptr->next_wid;
|
||||
tmpwid->prev_wid = widptr->prev_wid;
|
||||
}
|
||||
free(widptr);
|
||||
break;
|
||||
}
|
||||
widptr = widptr->next_wid;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Print a single character in the given window at the given coordinates */
|
||||
|
||||
void
|
||||
curses_putch(winid wid, int x, int y, int ch, int color, int attr)
|
||||
{
|
||||
int sx, sy, ex, ey;
|
||||
boolean border = curses_window_has_border(wid);
|
||||
nethack_char nch;
|
||||
static boolean map_initted = FALSE;
|
||||
/*
|
||||
if (wid == STATUS_WIN) {
|
||||
curses_update_stats();
|
||||
}
|
||||
*/
|
||||
if (wid != MAP_WIN) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!map_initted) {
|
||||
clear_map();
|
||||
map_initted = TRUE;
|
||||
}
|
||||
|
||||
map[y][x].ch = ch;
|
||||
map[y][x].color = color;
|
||||
map[y][x].attr = attr;
|
||||
nch = map[y][x];
|
||||
|
||||
(void) curses_map_borders(&sx, &sy, &ex, &ey, -1, -1);
|
||||
|
||||
if ((x >= sx) && (x <= ex) && (y >= sy) && (y <= ey)) {
|
||||
if (border) {
|
||||
x++;
|
||||
y++;
|
||||
}
|
||||
|
||||
write_char(mapwin, x - sx, y - sy, nch);
|
||||
}
|
||||
/* refresh after every character?
|
||||
* Fair go, mate! Some of us are playing from Australia! */
|
||||
/* wrefresh(mapwin); */
|
||||
}
|
||||
|
||||
|
||||
/* Get x, y coordinates of curses window on the physical terminal window */
|
||||
|
||||
void
|
||||
curses_get_window_xy(winid wid, int *x, int *y)
|
||||
{
|
||||
if (!is_main_window(wid)) {
|
||||
panic("curses_get_window_xy: wid out of range. Not a main window.");
|
||||
}
|
||||
|
||||
*x = nhwins[wid].x;
|
||||
*y = nhwins[wid].y;
|
||||
}
|
||||
|
||||
|
||||
/* Get usable width and height curses window on the physical terminal window */
|
||||
|
||||
void
|
||||
curses_get_window_size(winid wid, int *height, int *width)
|
||||
{
|
||||
*height = nhwins[wid].height;
|
||||
*width = nhwins[wid].width;
|
||||
}
|
||||
|
||||
|
||||
/* Determine if given window has a visible border */
|
||||
|
||||
boolean
|
||||
curses_window_has_border(winid wid)
|
||||
{
|
||||
return nhwins[wid].border;
|
||||
}
|
||||
|
||||
|
||||
/* Determine if window for given winid exists */
|
||||
|
||||
boolean
|
||||
curses_window_exists(winid wid)
|
||||
{
|
||||
nethack_wid *widptr = nhwids;
|
||||
|
||||
while (widptr != NULL) {
|
||||
if (widptr->nhwid == wid) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
widptr = widptr->next_wid;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/* Return the orientation of the specified window */
|
||||
|
||||
int
|
||||
curses_get_window_orientation(winid wid)
|
||||
{
|
||||
if (!is_main_window(wid)) {
|
||||
panic
|
||||
("curses_get_window_orientation: wid out of range. Not a main window.");
|
||||
}
|
||||
|
||||
return nhwins[wid].orientation;
|
||||
}
|
||||
|
||||
|
||||
/* Output a line of text to specified NetHack window with given coordinates
|
||||
and text attributes */
|
||||
|
||||
void
|
||||
curses_puts(winid wid, int attr, const char *text)
|
||||
{
|
||||
anything *identifier;
|
||||
WINDOW *win = NULL;
|
||||
|
||||
if (is_main_window(wid)) {
|
||||
win = curses_get_nhwin(wid);
|
||||
}
|
||||
|
||||
if (wid == MESSAGE_WIN) {
|
||||
curses_message_win_puts(text, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (wid == STATUS_WIN) {
|
||||
curses_update_stats(); /* We will do the write ourselves */
|
||||
/* Inventory updating isn't performed on redraws, so
|
||||
also update inventory here... */
|
||||
curses_update_inventory();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (curses_is_menu(wid) || curses_is_text(wid)) {
|
||||
if (!curses_menu_exists(wid)) {
|
||||
panic("curses_puts: Attempted write to nonexistant window!");
|
||||
}
|
||||
identifier = malloc(sizeof (anything));
|
||||
identifier->a_void = NULL;
|
||||
curses_add_nhmenu_item(wid, NO_GLYPH, identifier, 0, 0, attr, text,
|
||||
FALSE);
|
||||
} else {
|
||||
waddstr(win, text);
|
||||
wnoutrefresh(win);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Clear the contents of a window via the given NetHack winid */
|
||||
|
||||
void
|
||||
curses_clear_nhwin(winid wid)
|
||||
{
|
||||
WINDOW *win = curses_get_nhwin(wid);
|
||||
boolean border = curses_window_has_border(wid);
|
||||
|
||||
if (wid == MAP_WIN) {
|
||||
clearok(win, TRUE); /* Redraw entire screen when refreshed */
|
||||
clear_map();
|
||||
}
|
||||
|
||||
werase(win);
|
||||
|
||||
if (border) {
|
||||
box(win, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Change colour of window border to alert player to something */
|
||||
void
|
||||
curses_alert_win_border(winid wid, boolean onoff)
|
||||
{
|
||||
WINDOW *win = curses_get_nhwin(wid);
|
||||
|
||||
if (!win || !curses_window_has_border(wid))
|
||||
return;
|
||||
if (onoff)
|
||||
curses_toggle_color_attr(win, ALERT_BORDER_COLOR, NONE, ON);
|
||||
box(win, 0, 0);
|
||||
if (onoff)
|
||||
curses_toggle_color_attr(win, ALERT_BORDER_COLOR, NONE, OFF);
|
||||
wnoutrefresh(win);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
curses_alert_main_borders(boolean onoff)
|
||||
{
|
||||
curses_alert_win_border(MAP_WIN, onoff);
|
||||
curses_alert_win_border(MESSAGE_WIN, onoff);
|
||||
curses_alert_win_border(STATUS_WIN, onoff);
|
||||
curses_alert_win_border(INV_WIN, onoff);
|
||||
}
|
||||
|
||||
/* Return true if given wid is a main NetHack window */
|
||||
|
||||
static boolean
|
||||
is_main_window(winid wid)
|
||||
{
|
||||
if ((wid == MESSAGE_WIN) || (wid == MAP_WIN) || (wid == STATUS_WIN) || wid == INV_WIN) {
|
||||
return TRUE;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Unconditionally write a single character to a window at the given
|
||||
coordinates without a refresh. Currently only used for the map. */
|
||||
|
||||
static void
|
||||
write_char(WINDOW * win, int x, int y, nethack_char nch)
|
||||
{
|
||||
curses_toggle_color_attr(win, nch.color, nch.attr, ON);
|
||||
#ifdef PDCURSES
|
||||
mvwaddrawch(win, y, x, nch.ch);
|
||||
#else
|
||||
mvwaddch(win, y, x, nch.ch);
|
||||
#endif
|
||||
curses_toggle_color_attr(win, nch.color, nch.attr, OFF);
|
||||
}
|
||||
|
||||
|
||||
/* Draw the entire visible map onto the screen given the visible map
|
||||
boundaries */
|
||||
|
||||
void
|
||||
curses_draw_map(int sx, int sy, int ex, int ey)
|
||||
{
|
||||
int curx, cury;
|
||||
int bspace = 0;
|
||||
|
||||
#ifdef MAP_SCROLLBARS
|
||||
int sbsx, sbsy, sbex, sbey, count;
|
||||
nethack_char hsb_back, hsb_bar, vsb_back, vsb_bar;
|
||||
#endif
|
||||
|
||||
if (curses_window_has_border(MAP_WIN)) {
|
||||
bspace++;
|
||||
}
|
||||
#ifdef MAP_SCROLLBARS
|
||||
hsb_back.ch = '-';
|
||||
hsb_back.color = SCROLLBAR_BACK_COLOR;
|
||||
hsb_back.attr = A_NORMAL;
|
||||
hsb_bar.ch = '*';
|
||||
hsb_bar.color = SCROLLBAR_COLOR;
|
||||
hsb_bar.attr = A_NORMAL;
|
||||
vsb_back.ch = '|';
|
||||
vsb_back.color = SCROLLBAR_BACK_COLOR;
|
||||
vsb_back.attr = A_NORMAL;
|
||||
vsb_bar.ch = '*';
|
||||
vsb_bar.color = SCROLLBAR_COLOR;
|
||||
vsb_bar.attr = A_NORMAL;
|
||||
|
||||
/* Horizontal scrollbar */
|
||||
if ((sx > 0) || (ex < (COLNO - 1))) {
|
||||
sbsx = (sx * ((long) (ex - sx + 1) / COLNO));
|
||||
sbex = (ex * ((long) (ex - sx + 1) / COLNO));
|
||||
|
||||
for (count = 0; count < sbsx; count++) {
|
||||
write_char(mapwin, count + bspace, ey - sy + 1 + bspace, hsb_back);
|
||||
}
|
||||
|
||||
for (count = sbsx; count <= sbex; count++) {
|
||||
write_char(mapwin, count + bspace, ey - sy + 1 + bspace, hsb_bar);
|
||||
}
|
||||
|
||||
for (count = sbex + 1; count <= (ex - sx); count++) {
|
||||
write_char(mapwin, count + bspace, ey - sy + 1 + bspace, hsb_back);
|
||||
}
|
||||
}
|
||||
|
||||
/* Vertical scrollbar */
|
||||
if ((sy > 0) || (ey < (ROWNO - 1))) {
|
||||
sbsy = (sy * ((long) (ey - sy + 1) / ROWNO));
|
||||
sbey = (ey * ((long) (ey - sy + 1) / ROWNO));
|
||||
|
||||
for (count = 0; count < sbsy; count++) {
|
||||
write_char(mapwin, ex - sx + 1 + bspace, count + bspace, vsb_back);
|
||||
}
|
||||
|
||||
for (count = sbsy; count <= sbey; count++) {
|
||||
write_char(mapwin, ex - sx + 1 + bspace, count + bspace, vsb_bar);
|
||||
}
|
||||
|
||||
for (count = sbey + 1; count <= (ey - sy); count++) {
|
||||
write_char(mapwin, ex - sx + 1 + bspace, count + bspace, vsb_back);
|
||||
}
|
||||
}
|
||||
#endif /* MAP_SCROLLBARS */
|
||||
|
||||
for (curx = sx; curx <= ex; curx++) {
|
||||
for (cury = sy; cury <= ey; cury++) {
|
||||
write_char(mapwin, curx - sx + bspace, cury - sy + bspace,
|
||||
map[cury][curx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Init map array to blanks */
|
||||
|
||||
static void
|
||||
clear_map()
|
||||
{
|
||||
int x, y;
|
||||
|
||||
for (x = 0; x < COLNO; x++) {
|
||||
for (y = 0; y < ROWNO; y++) {
|
||||
map[y][x].ch = ' ';
|
||||
map[y][x].color = NO_COLOR;
|
||||
map[y][x].attr = A_NORMAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Determine visible boundaries of map, and determine if it needs to be
|
||||
based on the location of the player. */
|
||||
|
||||
boolean
|
||||
curses_map_borders(int *sx, int *sy, int *ex, int *ey, int ux, int uy)
|
||||
{
|
||||
static int width = 0;
|
||||
static int height = 0;
|
||||
static int osx = 0;
|
||||
static int osy = 0;
|
||||
static int oex = 0;
|
||||
static int oey = 0;
|
||||
static int oux = -1;
|
||||
static int ouy = -1;
|
||||
|
||||
if ((oux == -1) || (ouy == -1)) {
|
||||
oux = u.ux;
|
||||
ouy = u.uy;
|
||||
}
|
||||
|
||||
if (ux == -1) {
|
||||
ux = oux;
|
||||
} else {
|
||||
oux = ux;
|
||||
}
|
||||
|
||||
if (uy == -1) {
|
||||
uy = ouy;
|
||||
} else {
|
||||
ouy = uy;
|
||||
}
|
||||
|
||||
curses_get_window_size(MAP_WIN, &height, &width);
|
||||
|
||||
#ifdef MAP_SCROLLBARS
|
||||
if (width < COLNO) {
|
||||
height--; /* room for horizontal scrollbar */
|
||||
}
|
||||
|
||||
if (height < ROWNO) {
|
||||
width--; /* room for vertical scrollbar */
|
||||
|
||||
if (width == COLNO) {
|
||||
height--;
|
||||
}
|
||||
}
|
||||
#endif /* MAP_SCROLLBARS */
|
||||
|
||||
if (width >= COLNO) {
|
||||
*sx = 0;
|
||||
*ex = COLNO - 1;
|
||||
} else {
|
||||
*ex = (width / 2) + ux;
|
||||
*sx = *ex - (width - 1);
|
||||
|
||||
if (*ex >= COLNO) {
|
||||
*sx = COLNO - width;
|
||||
*ex = COLNO - 1;
|
||||
} else if (*sx < 0) {
|
||||
*sx = 0;
|
||||
*ex = width - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (height >= ROWNO) {
|
||||
*sy = 0;
|
||||
*ey = ROWNO - 1;
|
||||
} else {
|
||||
*ey = (height / 2) + uy;
|
||||
*sy = *ey - (height - 1);
|
||||
|
||||
if (*ey >= ROWNO) {
|
||||
*sy = ROWNO - height;
|
||||
*ey = ROWNO - 1;
|
||||
} else if (*sy < 0) {
|
||||
*sy = 0;
|
||||
*ey = height - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((*sx != osx) || (*sy != osy) || (*ex != oex) || (*ey != oey) ||
|
||||
map_clipped) {
|
||||
osx = *sx;
|
||||
osy = *sy;
|
||||
oex = *ex;
|
||||
oey = *ey;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
33
win/curses/curswins.h
Normal file
33
win/curses/curswins.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
|
||||
|
||||
#ifndef CURSWIN_H
|
||||
# define CURSWIN_H
|
||||
|
||||
|
||||
/* Global declarations */
|
||||
|
||||
WINDOW *curses_create_window(int width, int height, orient orientation);
|
||||
|
||||
void curses_destroy_win(WINDOW * win);
|
||||
void curses_refresh_nethack_windows(void);
|
||||
WINDOW *curses_get_nhwin(winid wid);
|
||||
void curses_add_nhwin(winid wid, int height, int width, int y, int x,
|
||||
orient orientation, boolean border);
|
||||
void curses_add_wid(winid wid);
|
||||
void curses_refresh_nhwin(winid wid);
|
||||
void curses_del_nhwin(winid wid);
|
||||
void curses_del_wid(winid wid);
|
||||
void curses_putch(winid wid, int x, int y, int ch, int color, int attrs);
|
||||
void curses_get_window_xy(winid wid, int *x, int *y);
|
||||
boolean curses_window_has_border(winid wid);
|
||||
boolean curses_window_exists(winid wid);
|
||||
int curses_get_window_orientation(winid wid);
|
||||
void curses_puts(winid wid, int attr, const char *text);
|
||||
void curses_clear_nhwin(winid wid);
|
||||
void curses_alert_win_border(winid wid, boolean onoff);
|
||||
void curses_alert_main_borders(boolean onoff);
|
||||
void curses_draw_map(int sx, int sy, int ex, int ey);
|
||||
boolean curses_map_borders(int *sx, int *sy, int *ex, int *ey, int ux, int uy);
|
||||
|
||||
|
||||
#endif /* CURSWIN_H */
|
||||
Reference in New Issue
Block a user