more quieting of Qt6 build warnings
[NetHack.git] / win / curses / cursmain.c
blob5dee9900bc1e8e8bae5b4181c34d334e6b5c8a61
1 /* 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" -*-*/
2 /* NetHack 3.7 cursmain.c */
3 /* Copyright (c) Karl Garrison, 2010. */
4 /* NetHack may be freely redistributed. See license for details. */
6 #include "curses.h"
7 #include "hack.h"
8 #include "color.h"
9 #include "wincurs.h"
10 #ifdef CURSES_UNICODE
11 #include <locale.h>
12 #endif
14 /* define this if not linking with <foo>tty.o|.obj for some reason */
15 #ifdef CURSES_DEFINE_ERASE_CHAR
16 char erase_char, kill_char;
17 #else
18 /* defined in sys/<foo>/<foo>tty.o|.obj which gets linked into
19 tty-only, tty+curses, and curses-only binaries */
20 extern char erase_char, kill_char;
21 #endif
23 extern long curs_mesg_suppress_seq; /* from cursmesg.c */
24 extern boolean curs_mesg_no_suppress; /* ditto */
25 extern int mesg_mixed;
26 extern glyph_info mesg_gi;
28 #ifndef CURSES_GENL_PUTMIXED
29 #if defined(PDC_WIDE) || defined(NCURSES_WIDECHAR)
30 #define USE_CURSES_PUTMIXED
31 #else /* WIDE */
32 #ifdef NH_PRAGMA_MESSAGE
33 #ifdef _MSC_VER
34 #pragma message ("Curses wide support not defined so NetHack curses message window functionality reduced")
35 #else
36 #pragma message "Curses wide support not defined so NetHack curses message window functionality reduced"
37 #endif /* _MSC_VER */
38 #endif /* NH_PRAGMA_MESSAGE */
39 #endif /* WIDE */
40 #endif /* CURSES_GENL_PUTMIXED */
42 /* stubs for curses_procs{} */
43 #ifdef POSITIONBAR
44 static void dummy_update_position_bar(char *);
45 #endif
46 #ifdef CHANGE_COLOR
47 static void curses_change_color(int, long, int);
48 static char *curses_get_color_string(void);
49 #endif
51 /* Public functions for curses NetHack interface */
53 /* Interface definition, for windows.c */
54 struct window_procs curses_procs = {
55 WPID(curses),
56 (WC_ALIGN_MESSAGE | WC_ALIGN_STATUS | WC_COLOR | WC_INVERSE
57 | WC_HILITE_PET | WC_WINDOWCOLORS
58 #ifdef NCURSES_MOUSE_VERSION /* (this macro name works for PDCURSES too) */
59 | WC_MOUSE_SUPPORT
60 #endif
61 | WC_PERM_INVENT | WC_POPUP_DIALOG | WC_SPLASH_SCREEN),
62 (WC2_DARKGRAY | WC2_HITPOINTBAR
63 #ifdef CURSES_UNICODE
64 | WC2_U_UTF8STR
65 #endif
66 | WC2_EXTRACOLORS
67 #ifdef SELECTSAVED
68 | WC2_SELECTSAVED
69 #endif
70 #if defined(STATUS_HILITES)
71 | WC2_HILITE_STATUS
72 #endif
73 | WC2_FLUSH_STATUS | WC2_TERM_SIZE
74 | WC2_STATUSLINES | WC2_WINDOWBORDERS | WC2_PETATTR | WC2_GUICOLOR
75 | WC2_SUPPRESS_HIST | WC2_URGENT_MESG | WC2_MENU_SHIFT),
76 {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */
77 curses_init_nhwindows,
78 curses_player_selection,
79 curses_askname,
80 curses_get_nh_event,
81 curses_exit_nhwindows,
82 curses_suspend_nhwindows,
83 curses_resume_nhwindows,
84 curses_create_nhwindow,
85 curses_clear_nhwindow,
86 curses_display_nhwindow,
87 curses_destroy_nhwindow,
88 curses_curs,
89 curses_putstr,
90 #ifdef USE_CURSES_PUTMIXED
91 curses_putmixed,
92 #else
93 genl_putmixed,
94 #endif
95 curses_display_file,
96 curses_start_menu,
97 curses_add_menu,
98 curses_end_menu,
99 curses_select_menu,
100 genl_message_menu,
101 curses_mark_synch,
102 curses_wait_synch,
103 #ifdef CLIPPING
104 curses_cliparound,
105 #endif
106 #ifdef POSITIONBAR
107 dummy_update_position_bar,
108 #endif
109 curses_print_glyph,
110 curses_raw_print,
111 curses_raw_print_bold,
112 curses_nhgetch,
113 curses_nh_poskey,
114 curses_nhbell,
115 curses_doprev_message,
116 curses_yn_function,
117 curses_getlin,
118 curses_get_ext_cmd,
119 curses_number_pad,
120 curses_delay_output,
121 #ifdef CHANGE_COLOR
122 curses_change_color,
123 #ifdef MAC /* old OS 9, not OSX */
124 (void (*)(int)) 0,
125 (short (*)(winid, char *)) 0,
126 #endif
127 curses_get_color_string,
128 #endif
129 curses_start_screen,
130 curses_end_screen,
131 genl_outrip,
132 curses_preference_update,
133 curses_getmsghistory,
134 curses_putmsghistory,
135 curses_status_init,
136 curses_status_finish,
137 genl_status_enablefield,
138 curses_status_update,
139 genl_can_suspend_yes,
140 curses_update_inventory,
141 curses_ctrl_nhwindow,
145 * Global variables for curses interface
148 int term_rows, term_cols; /* size of underlying terminal */
149 int orig_cursor; /* Preserve initial cursor state */
150 WINDOW *base_term; /* underlying terminal window */
151 boolean counting; /* Count window is active */
152 WINDOW *mapwin, *statuswin, *messagewin; /* Main windows */
153 color_attr curses_menu_promptstyle = { NO_COLOR, ATR_NONE };
155 /* Track if we're performing an update to the permanent window.
156 Needed since we aren't using the normal menu functions to handle
157 the inventory window. */
158 static int inv_update = 0;
161 init_nhwindows(int *argcp, char **argv)
162 -- Initialize the windows used by NetHack. This can also
163 create the standard windows listed at the top, but does
164 not display them.
165 -- Any commandline arguments relevant to the windowport
166 should be interpreted, and *argcp and *argv should
167 be changed to remove those arguments.
168 -- When the message window is created, the variable
169 iflags.window_inited needs to be set to TRUE. Otherwise
170 all plines() will be done via raw_print().
171 ** Why not have init_nhwindows() create all of the "standard"
172 ** windows? Or at least all but WIN_INFO? -dean
174 void
175 curses_init_nhwindows(
176 int *argcp UNUSED,
177 char **argv UNUSED)
179 #ifdef PDCURSES
180 char window_title[BUFSZ];
181 #endif
182 #ifdef CURSES_UNICODE
183 #ifdef PDCURSES
184 static char pdc_font[BUFSZ] = "";
185 #endif
186 #endif
188 #ifdef CURSES_UNICODE
189 setlocale(LC_CTYPE, "");
190 #ifdef PDCURSES
191 /* Assume the DOSVGA port of PDCursesMod, or the SDL1 or SDL2 port of
192 either PDCurses or PDCursesMod. Honor the font_map option to set
193 a font.
194 On MS-DOS, if no font_map is set, use ter-u16v.psf if it is present.
195 PDC_FONT has no effect on other PDCurses or PDCursesMod ports. */
196 if (iflags.wc_font_map && iflags.wc_font_map[0]) {
197 Snprintf(pdc_font, sizeof(pdc_font), "PDC_FONT=%s",
198 iflags.wc_font_map);
199 #ifdef MSDOS
200 } else if (access("ter-u16v.psf", R_OK) >= 0) {
201 Snprintf(pdc_font, sizeof(pdc_font), "PDC_FONT=ter-u16v.psf");
202 #endif
204 if (pdc_font[0] != '\0') {
205 putenv(pdc_font);
207 #endif
208 #endif
210 /* if anything has already been output by nethack (for instance, warnings
211 about RC file issues), let the player acknowlege it before initscr()
212 erases the screen */
213 if (iflags.raw_printed)
214 curses_wait_synch();
216 #ifdef XCURSES
217 base_term = Xinitscr(*argcp, argv);
218 #else
219 base_term = initscr();
220 #endif
221 if (has_colors()) {
222 start_color();
223 curses_init_nhcolors();
224 } else {
225 iflags.use_color = FALSE;
226 set_option_mod_status("color", set_in_config);
227 iflags.wc2_guicolor = FALSE;
228 set_wc2_option_mod_status(WC2_GUICOLOR, set_in_config);
230 noecho();
231 raw();
232 nonl(); /* don't force ^M into newline (^J); input accepts them both
233 * but as a command, accidental <enter> won't run South */
234 meta(stdscr, TRUE);
235 orig_cursor = curs_set(0);
236 keypad(stdscr, TRUE);
237 #ifdef NCURSES_VERSION
238 # ifdef __APPLE__
239 ESCDELAY = 25;
240 # else
241 set_escdelay(25);
242 # endif/* __APPLE__ */
243 #endif /* NCURSES_VERSION */
244 #ifdef PDCURSES
245 # ifdef DEF_GAME_NAME
246 # ifdef VERSION_STRING
247 Snprintf(window_title, sizeof window_title, "%s %s",
248 DEF_GAME_NAME, VERSION_STRING);
249 # else
250 if (nomakedefs.version_string)
251 Snprintf(window_title, sizeof window_title, "%s %s",
252 DEF_GAME_NAME, nomakedefs.version_string);
253 else
254 Snprintf(window_title, sizeof window_title, "%s", DEF_GAME_NAME);
255 # endif
256 /* VERSION_STRING */
257 # else
258 # ifdef VERSION_STRING
259 Snprintf(window_title, sizeof window_title, "%s %s",
260 "NetHack", VERSION_STRING);
261 # else
262 if (nomakedefs.version_string)
263 Snprintf(window_title, sizeof window_title, "%s %s",
264 "NetHack", nomakedefs.version_string);
265 else
266 Snprintf(window_title, sizeof window_title, "%s", "NetHack");
267 # endif
268 /* VERSION_STRING */
269 # endif/* DEF_GAME_NAME */
270 PDC_set_title(window_title);
271 PDC_set_blink(TRUE); /* Only if the user asks for it! */
272 timeout(1);
273 (void) getch();
274 timeout(-1);
275 #endif /* PDCURSES */
276 getmaxyx(base_term, term_rows, term_cols);
277 counting = FALSE;
278 curses_init_options();
279 if (term_rows < 15 || term_cols < 40) {
280 panic("Terminal is too small; must have at least %s%s%s.",
281 (term_rows < 15) ? "15 rows" : "",
282 (term_rows < 15 && term_cols < 40) ? " and " : "",
283 (term_cols < 40) ? "40 columns" : "");
285 /* during line input, deletes the most recently typed character */
286 erase_char = erasechar(); /* <delete>/<rubout> or possibly <backspace> */
287 /* during line input, deletes all typed characters */
288 kill_char = killchar(); /* ^U (back in prehistoric times, '@') */
290 curses_create_main_windows();
291 curses_init_mesg_history();
292 curses_display_splash_window();
295 /* Use the general role/race/&c selection originally implemented for tty. */
296 void
297 curses_player_selection(void)
299 #if 1
300 if (genl_player_setup(0))
301 return; /* success */
303 /* quit/cancel */
304 curses_bail((const char *) NULL);
305 /*NOTREACHED*/
306 #else
307 /* still present cursinit.c but no longer used */
308 curses_choose_character();
309 #endif
313 /* Ask the user for a player name. */
314 void
315 curses_askname(void)
317 #ifdef SELECTSAVED
318 if (iflags.wc2_selectsaved && !iflags.renameinprogress)
319 switch (restore_menu(MAP_WIN)) {
320 case -1: /* quit */
321 goto bail;
322 case 0: /* new game */
323 break;
324 case 1: /* picked a save file to restore and set plname[] for it */
325 return;
327 #endif /* SELECTSAVED */
329 curses_line_input_dialog("Who are you?", svp.plname, PL_NSIZ);
330 (void) mungspaces(svp.plname);
331 if (!svp.plname[0] || svp.plname[0] == '\033')
332 goto bail;
334 iflags.renameallowed = TRUE; /* tty uses this, we don't [yet?] */
335 return;
337 bail:
338 /* message is delivered via raw_print() */
339 curses_bail("\nUntil next time then...\n");
340 /*NOTREACHED*/
344 /* Does window event processing (e.g. exposure events).
345 A noop for the tty and X window-ports.
347 void
348 curses_get_nh_event(void)
350 boolean do_reset = FALSE;
352 #ifdef PDCURSES
353 if (is_termresized()) {
354 resize_term(0, 0);
355 do_reset = TRUE;
357 #endif
358 #ifdef NCURSES_VERSION /* Is there a better way to detect ncurses? */
359 if (is_term_resized(term_rows, term_cols)) {
360 if (!isendwin()) {
361 endwin();
363 refresh();
364 do_reset = TRUE;
366 #endif
368 if (do_reset) {
369 getmaxyx(base_term, term_rows, term_cols);
370 curses_got_input(); /* reset More>> */
371 /* status_initialize, create_main_windows, last_messages, doredraw */
372 curs_reset_windows(TRUE, TRUE);
376 /* restore terminal state; extracted from curses_exit_nhwindows() */
377 void
378 curses_uncurse_terminal(void)
380 /* also called by panictrace_handler(), a signal handler, so somewhat
381 iffy in that situation; but without this, newlines behave as raw
382 line feeds so subsequent backtrace gets scrawled all over the screen
383 and is nearly useless */
384 curses_cleanup();
385 curs_set(orig_cursor);
386 endwin();
389 /* Exits the window system. This should dismiss all windows,
390 except the "window" used for raw_print(). str is printed if possible.
392 void
393 curses_exit_nhwindows(const char *str)
395 curses_destroy_nhwindow(INV_WIN);
396 curses_destroy_nhwindow(MAP_WIN);
397 curses_destroy_nhwindow(STATUS_WIN);
398 curses_destroy_nhwindow(MESSAGE_WIN);
399 curs_destroy_all_wins();
401 curses_uncurse_terminal();
403 iflags.window_inited = 0;
404 if (str != NULL) {
405 raw_print(str);
409 /* Prepare the window to be suspended. */
410 void
411 curses_suspend_nhwindows(const char *str UNUSED)
413 endwin();
417 /* Restore the windows after being suspended. */
418 void
419 curses_resume_nhwindows(void)
421 curses_refresh_nethack_windows();
424 /* Create a window of type "type" which can be
425 NHW_MESSAGE (top line)
426 NHW_STATUS (bottom lines)
427 NHW_MAP (main dungeon)
428 NHW_MENU (inventory or other "corner" windows)
429 NHW_TEXT (help/text, full screen paged window)
431 winid
432 curses_create_nhwindow(int type)
434 winid wid = curses_get_wid(type);
436 if (curses_is_menu(wid))
437 curses_parse_wid_colors(MENU_WIN, iflags.wcolors[wcolor_menu].fg,
438 iflags.wcolors[wcolor_menu].bg);
439 else if (curses_is_text(wid))
440 curses_parse_wid_colors(TEXT_WIN, iflags.wcolors[wcolor_text].fg,
441 iflags.wcolors[wcolor_text].bg);
442 if (curses_is_menu(wid) || curses_is_text(wid)) {
443 curses_start_menu(wid, MENU_BEHAVE_STANDARD);
444 curses_add_wid(wid);
447 return wid;
451 /* Clear the given window, when asked to. */
452 void
453 curses_clear_nhwindow(winid wid)
455 if (wid != NHW_MESSAGE) {
456 curses_clear_nhwin(wid);
457 } else {
458 /* scroll the message window one line if it's full */
459 curses_count_window("");
460 /* remove 'countwin', leaving last message line blank */
461 curses_count_window((char *) 0);
465 /* -- Display the window on the screen. If there is data
466 pending for output in that window, it should be sent.
467 If blocking is TRUE, display_nhwindow() will not
468 return until the data has been displayed on the screen,
469 and acknowledged by the user where appropriate.
470 -- All calls are blocking in the tty window-port.
471 -- Calling display_nhwindow(WIN_MESSAGE,???) will do a
472 --more--, if necessary, in the tty window-port.
474 void
475 curses_display_nhwindow(winid wid, boolean block)
477 menu_item *selected = NULL;
478 int border = curses_window_has_border(wid) ? 1 : 0;
480 if (wid == WIN_ERR)
481 return;
482 if (curses_is_menu(wid) || curses_is_text(wid)) {
483 curses_end_menu(wid, "");
484 (void) curses_select_menu(wid, PICK_NONE, &selected);
485 return;
488 /* don't overwrite the splash screen first time through */
489 if (!iflags.window_inited && wid == MAP_WIN) {
490 iflags.window_inited = TRUE;
491 } else {
492 WINDOW *win = curses_get_nhwin(wid);
494 /* actually display the window */
495 wnoutrefresh(win);
496 if (border)
497 box(win, 0, 0);
498 /* flush pending writes from other windows too */
499 doupdate();
501 if ((wid == MAP_WIN) && block) {
502 (void) curses_more();
505 if ((wid == MESSAGE_WIN) && block) {
506 (void) curses_block(TRUE);
511 /* Destroy will dismiss the window if the window has not
512 * already been dismissed.
514 void
515 curses_destroy_nhwindow(winid wid)
517 switch (wid) {
518 case MESSAGE_WIN:
519 curses_teardown_messages(); /* discard ^P message history data */
520 break;
521 case STATUS_WIN:
522 if (VIA_WINDOWPORT())
523 curses_status_finish(); /* discard cached status data */
524 break;
525 case INV_WIN:
526 curs_purge_perminv_data(TRUE);
527 iflags.perm_invent = 0; /* avoid unexpected update_inventory() */
528 break;
529 case MAP_WIN:
530 break;
531 default:
532 break;
534 curses_del_nhwin(wid);
537 /* Next output to window will start at (x,y), also moves
538 displayable cursor to (x,y). For backward compatibility,
539 1 <= x < cols, 0 <= y < rows, where cols and rows are
540 the size of window.
542 void
543 curses_curs(winid wid, int x, int y)
545 curses_move_cursor(wid, x, y);
549 putstr(window, attr, str)
550 -- Print str on the window with the given attribute. Only
551 printable ASCII characters (040-0126) must be supported.
552 Multiple putstr()s are output on separate lines.
553 Attributes can be one of
554 ATR_NONE (or 0)
555 ATR_ULINE
556 ATR_BOLD
557 ATR_BLINK
558 ATR_INVERSE
559 If a window-port does not support all of these, it may map
560 unsupported attributes to a supported one (e.g. map them
561 all to ATR_INVERSE). putstr() may compress spaces out of
562 str, break str, or truncate str, if necessary for the
563 display. Where putstr() breaks a line, it has to clear
564 to end-of-line.
565 -- putstr should be implemented such that if two putstr()s
566 are done consecutively the user will see the first and
567 then the second. In the tty port, pline() achieves this
568 by calling more() or displaying both on the same line.
570 void
571 curses_putstr(winid wid, int attr, const char *text)
573 int mesgflags, curses_attr;
575 mesgflags = attr & (ATR_URGENT | ATR_NOHISTORY);
576 attr &= ~mesgflags;
578 /* this is comparable to tty's cw->flags &= ~WIN_STOP; if messages are
579 being suppressed after >>ESC, override that and resume showing them */
580 if ((mesgflags & ATR_URGENT) != 0) {
581 curs_mesg_suppress_seq = -1L;
582 curs_mesg_no_suppress = TRUE;
585 if (wid == WIN_MESSAGE && (mesgflags & ATR_NOHISTORY) != 0) {
586 /* display message without saving it in recall history */
587 curses_count_window(text);
588 } else {
589 /* We need to convert NetHack attributes to curses attributes */
590 curses_attr = curses_convert_attr(attr);
591 curses_puts(wid, curses_attr, text);
594 /* urgent message handling is a one-shot operation; we're done */
595 curs_mesg_no_suppress = FALSE;
598 void
599 curses_putmixed(winid window, int attr, const char *str)
601 const char *substr = 0;
602 char buf[BUFSZ];
603 boolean done_output = FALSE;
604 #ifdef ENHANCED_SYMBOLS
605 int utf8flag = 0;
606 #endif
608 if (window == WIN_MESSAGE) {
609 str = mixed_to_glyphinfo(str, &mesg_gi);
610 mesg_mixed = 1;
611 } else {
612 if ((substr = strstri(str, "\\G")) != 0) {
613 #ifdef ENHANCED_SYMBOLS
614 if ((windowprocs.wincap2 & WC2_U_UTF8STR) && SYMHANDLING(H_UTF8)) {
615 mixed_to_utf8(buf, sizeof buf, str, &utf8flag);
616 } else {
617 #endif
618 decode_mixed(buf, str);
619 #ifdef ENHANCED_SYMBOLS
621 #endif
622 /* now send buf to the normal putstr */
623 curses_putstr(window, attr, buf);
624 done_output = TRUE;
628 if (!done_output) {
629 /* just send str to the normal putstr */
630 curses_putstr(window, attr, str);
632 if (window == WIN_MESSAGE)
633 mesg_mixed = 0;
636 /* Display the file named str. Complain about missing files
637 iff complain is TRUE.
639 void
640 curses_display_file(const char *filename, boolean must_exist)
642 curses_view_file(filename, must_exist);
645 /* Start using window as a menu. You must call start_menu()
646 before add_menu(). After calling start_menu() you may not
647 putstr() to the window. Only windows of type NHW_MENU may
648 be used for menus.
650 void
651 curses_start_menu(winid wid, unsigned long mbehavior)
653 if (inv_update)
654 return;
656 curses_create_nhmenu(wid, mbehavior);
660 add_menu(winid wid, const glyph_info *glyphinfo,
661 const anything identifier,
662 char accelerator, char groupacc,
663 int attr, int color,
664 char *str, unsigned int itemflags)
665 -- Add a text line str to the given menu window. If identifier
666 is 0, then the line cannot be selected (e.g. a title).
667 Otherwise, identifier is the value returned if the line is
668 selected. Accelerator is a keyboard key that can be used
669 to select the line. If the accelerator of a selectable
670 item is 0, the window system is free to select its own
671 accelerator. It is up to the window-port to make the
672 accelerator visible to the user (e.g. put "a - " in front
673 of str). The value attr is the same as in putstr().
674 -- Glyph is an optional glyph to accompany the line and its
675 modifiers (if any) can be found in glyphinfo. If
676 window port cannot or does not want to display it, this
677 is OK. If there is no glyph applicable, then this
678 value will be NO_GLYPH.
679 -- All accelerators should be in the range [A-Za-z].
680 -- It is expected that callers do not mix accelerator
681 choices. Either all selectable items have an accelerator
682 or let the window system pick them. Don't do both.
683 -- Groupacc is a group accelerator. It may be any character
684 outside of the standard accelerator (see above) or a
685 number. If 0, the item is unaffected by any group
686 accelerator. If this accelerator conflicts with
687 the menu command (or their user defined alises), it loses.
688 The menu commands and aliases take care not to interfere
689 with the default object class symbols.
690 -- If you want this choice to be preselected when the
691 menu is displayed, set bit MENU_ITEMFLAGS_SELECTED.
693 void
694 curses_add_menu(winid wid, const glyph_info *glyphinfo,
695 const ANY_P *identifier,
696 char accelerator, char group_accel, int attr,
697 int clr, const char *str, unsigned itemflags)
699 int curses_attr;
701 attr &= ~(ATR_URGENT | ATR_NOHISTORY);
702 curses_attr = curses_convert_attr(attr);
704 /* 'inv_update': 0 for normal menus, 1 and up for perminv window */
705 if (inv_update) {
706 /* persistent inventory window; nothing is selectable;
707 omit glyphinfo because perm_invent is to the side of
708 the map so usually cramped for horizontal space */
709 curs_add_invt(inv_update, accelerator, curses_attr, clr, str);
710 inv_update++;
711 return;
714 curses_add_nhmenu_item(wid, glyphinfo, identifier,
715 accelerator, group_accel,
716 curses_attr, clr, str, itemflags);
720 end_menu(window, prompt)
721 -- Stop adding entries to the menu and flushes the window
722 to the screen (brings to front?). Prompt is a prompt
723 to give the user. If prompt is NULL, no prompt will
724 be printed.
725 ** This probably shouldn't flush the window any more (if
726 ** it ever did). That should be select_menu's job. -dean
728 void
729 curses_end_menu(winid wid, const char *prompt)
731 if (inv_update)
732 return;
734 curses_finalize_nhmenu(wid, prompt);
738 int select_menu(winid window, int how, menu_item **selected)
739 -- Return the number of items selected; 0 if none were chosen,
740 -1 when explicitly cancelled. If items were selected, then
741 selected is filled in with an allocated array of menu_item
742 structures, one for each selected line. The caller must
743 free this array when done with it. The "count" field
744 of selected is a user supplied count. If the user did
745 not supply a count, then the count field is filled with
746 -1 (meaning all). A count of zero is equivalent to not
747 being selected and should not be in the list. If no items
748 were selected, then selected is NULL'ed out. How is the
749 mode of the menu. Three valid values are PICK_NONE,
750 PICK_ONE, and PICK_N, meaning: nothing is selectable,
751 only one thing is selectable, and any number valid items
752 may selected. If how is PICK_NONE, this function should
753 never return anything but 0 or -1.
754 -- You may call select_menu() on a window multiple times --
755 the menu is saved until start_menu() or destroy_nhwindow()
756 is called on the window.
757 -- Note that NHW_MENU windows need not have select_menu()
758 called for them. There is no way of knowing whether
759 select_menu() will be called for the window at
760 create_nhwindow() time.
763 curses_select_menu(winid wid, int how, MENU_ITEM_P ** selected)
765 if (inv_update)
766 return 0;
768 return curses_display_nhmenu(wid, how, selected);
771 void
772 curses_update_inventory(int arg)
774 /* Don't do anything if perm_invent is off unless it was on and
775 player just changed the option. */
776 if (!iflags.perm_invent) {
777 if (curses_get_nhwin(INV_WIN)) {
778 curs_reset_windows(TRUE, FALSE);
779 curs_purge_perminv_data(FALSE);
781 return;
784 /* skip inventory updating during character initialization */
785 if (!program_state.in_moveloop && !program_state.gameover)
786 return;
788 if (!arg) {
789 /* if perm_invent is just being toggled on, we need to run the
790 update twice; the first time creates the window and organizes
791 the screen to fit it in, the second time populates it;
792 needed if we're called from docrt() because the "organizes
793 the screen" part calls docrt() and that skips recursive calls */
794 boolean no_inv_win_yet = !curses_get_nhwin(INV_WIN);
796 /* Update inventory sidebar. NetHack uses normal menu functions
797 when gathering the inventory, and we don't want to change the
798 underlying code. So instead, track if an inventory update is
799 being performed with a static variable. */
800 inv_update = 1;
801 curs_update_invt(0);
802 if (no_inv_win_yet)
803 curs_update_invt(0);
804 inv_update = 0;
805 } else {
806 /* perform scrolling operations on persistent inventory window */
807 curs_update_invt(arg);
811 win_request_info *
812 curses_ctrl_nhwindow(
813 winid window UNUSED,
814 int request,
815 win_request_info *wri)
817 int attr;
819 if (!wri)
820 return (win_request_info *) 0;
822 switch (request) {
823 case set_mode:
824 case request_settings:
825 break;
826 case set_menu_promptstyle:
827 curses_menu_promptstyle.color = wri->fromcore.menu_promptstyle.color;
828 if (curses_menu_promptstyle.color == NO_COLOR)
829 curses_menu_promptstyle.color = NONE;
830 attr = wri->fromcore.menu_promptstyle.attr;
831 curses_menu_promptstyle.attr = curses_convert_attr(attr);;
832 break;
833 default:
834 break;
836 return wri;
840 mark_synch() -- Don't go beyond this point in I/O on any channel until
841 all channels are caught up to here.
843 void
844 curses_mark_synch(void)
846 /* full refresh has unintended side-effect of making a menu window
847 that has called core's get_count() to vanish; do a basic screen
848 refresh instead */
849 /*curses_refresh_nethack_windows();*/
850 refresh();
854 wait_synch() -- Wait until all pending output is complete (*flush*() for
855 streams goes here).
856 -- May also deal with exposure events etc. so that the
857 display is OK when return from wait_synch().
859 void
860 curses_wait_synch(void)
862 if (iflags.raw_printed) {
863 #ifndef PDCURSES
864 int chr;
866 * If any message has been issued via raw_print(), make the user
867 * acknowledge it. This might take place before initscr() so
868 * access to curses is limited. [Despite that, there's probably
869 * a more curses-specific way to handle this. FIXME?]
872 (void) fprintf(stdout, "\nPress <return> to continue: ");
873 (void) fflush(stdout);
874 do {
875 chr = fgetc(stdin);
876 } while (chr > 0 && chr != C('j') && chr != C('m') && chr != '\033');
877 #endif
878 iflags.raw_printed = 0;
881 if (iflags.window_inited) {
882 if (curses_got_output())
883 (void) curses_more();
884 curses_mark_synch();
886 /* [do we need 'if (counting) curses_count_window((char *)0);' here?] */
890 cliparound(x, y)-- Make sure that the user is more-or-less centered on the
891 screen if the playing area is larger than the screen.
892 -- This function is only defined if CLIPPING is defined.
894 void
895 curses_cliparound(int x, int y)
897 int sx, sy, ex, ey;
898 boolean redraw = curses_map_borders(&sx, &sy, &ex, &ey, x, y);
900 if (redraw) {
901 curses_draw_map(sx, sy, ex, ey);
906 print_glyph(window, x, y, glyphinfo, bkglyphinfo)
907 -- Print glyph at (x,y) on the given window. Glyphs are
908 integers within the glyph_info struct that is passed
909 at the interface, mapped to whatever the window-
910 port wants (symbol, font, color, attributes, ...there's
911 a 1-1 map between glyphs and distinct things on the map).
912 bkglyphinfo is to render the background behind the glyph.
913 It's not used here.
914 -- bkglyphinfo contains a background glyph for potential use
915 by some graphical or tiled environments to allow the
916 depiction to fall against a background consistent with
917 the grid around x,y. If bkglyphinfo->glyph is NO_GLYPH,
918 then the parameter should be ignored (do nothing with it).
919 -- glyph_info struct fields:
920 int glyph; the display entity
921 int color; color for window ports not using a tile
922 int ttychar; the character mapping for the original tty
923 interface. Most or all window ports wanted
924 and used this for various things so it is
925 provided in 3.7+
926 short int symidx; offset into syms array
927 unsigned glyphflags; more detail about the entity
931 void
932 curses_print_glyph(
933 winid wid,
934 coordxy x, coordxy y,
935 const glyph_info *glyphinfo,
936 const glyph_info *bkglyphinfo)
938 int glyph;
939 int ch;
940 int color;
941 int nhcolor = 0;
942 unsigned int special;
943 int attr = -1;
945 glyph = glyphinfo->glyph;
946 special = glyphinfo->gm.glyphflags;
947 ch = glyphinfo->ttychar;
948 color = glyphinfo->gm.sym.color;
949 /* Extra color handling
950 * FIQ: The curses library does not support truecolor, only the more limited 256
951 * color mode. On top of this, the windowport only supports 16 color mode.
952 * Thus, we only allow users to customize glyph colors to the basic NetHack
953 * colors. */
954 if (glyphinfo->gm.customcolor != 0
955 && (curses_procs.wincap2 & WC2_EXTRACOLORS) != 0) {
956 if ((glyphinfo->gm.customcolor & NH_BASIC_COLOR) != 0) {
957 color = COLORVAL(glyphinfo->gm.customcolor);
958 #if 0
959 } else {
960 /* 24-bit color, NH_BASIC_COLOR == 0 */
961 nhcolor = COLORVAL(glyphinfo->gm.customcolor);
962 #endif
965 if ((special & MG_PET) && iflags.hilite_pet) {
966 attr = curses_convert_attr(iflags.wc2_petattr);
968 if ((special & MG_DETECT) && iflags.use_inverse) {
969 attr = A_REVERSE;
971 if (SYMHANDLING(H_DEC))
972 ch = curses_convert_glyph(ch, glyph);
974 if (wid == NHW_MAP) {
975 /* hilite stairs not in 3.6, yet
976 if ((special & MG_STAIRS) && iflags.hilite_hidden_stairs) {
977 color = 16 + (color * 2);
978 } else
980 if ((special & MG_OBJPILE) && iflags.hilite_pile) {
981 if (iflags.wc_color)
982 color = get_framecolor(color, CLR_BLUE);
983 else /* if (iflags.use_inverse) */
984 attr = A_REVERSE;
986 /* water and lava look the same except for color; when color is off
987 (checked by core), render lava in inverse video so that it looks
988 different from water; similar for floor vs ice, fountain vs sink,
989 and corridor vs engranving-in-corridor */
990 if ((special & (MG_BW_LAVA | MG_BW_ICE | MG_BW_SINK | MG_BW_ENGR))
991 != 0 && iflags.use_inverse) {
992 /* reset_glyphmap() only sets MG_BW_foo if color is off */
993 attr = A_REVERSE;
995 /* highlight female monsters (wizard mode option) */
996 if ((special & MG_FEMALE) && wizard && iflags.wizmgender) {
997 attr = A_REVERSE;
1001 curses_putch(wid, x, y, ch,
1002 #ifdef ENHANCED_SYMBOLS
1003 (SYMHANDLING(H_UTF8)
1004 && glyphinfo->gm.u && glyphinfo->gm.u->utf8str)
1005 ? glyphinfo->gm.u : NULL,
1006 #endif
1007 (nhcolor != 0) ? nhcolor : color,
1008 bkglyphinfo->framecolor, attr);
1013 raw_print(str) -- Print directly to a screen, or otherwise guarantee that
1014 the user sees str. raw_print() appends a newline to str.
1015 It need not recognize ASCII control characters. This is
1016 used during startup (before windowing system initialization
1017 -- maybe this means only error startup messages are raw),
1018 for error messages, and maybe other "msg" uses. E.g.
1019 updating status for micros (i.e, "saving").
1021 void
1022 curses_raw_print(const char *str)
1024 #ifdef PDCURSES
1025 /* WINDOW *win = curses_get_nhwin(MESSAGE_WIN); */
1027 if (iflags.window_inited) {
1028 curses_message_win_puts(str, FALSE);
1029 return;
1031 #endif
1032 puts(str);
1033 iflags.raw_printed++;
1037 raw_print_bold(str)
1038 -- Like raw_print(), but prints in bold/standout (if possible).
1040 void
1041 curses_raw_print_bold(const char *str)
1043 curses_raw_print(str);
1047 int nhgetch() -- Returns a single character input from the user.
1048 -- In the tty window-port, nhgetch() assumes that tgetch()
1049 will be the routine the OS provides to read a character.
1050 Returned character _must_ be non-zero.
1053 curses_nhgetch(void)
1055 int ch;
1057 /* curses_prehousekeeping() assumes that the map window is active;
1058 avoid it when a menu is active */
1059 if (!activemenu)
1060 curses_prehousekeeping();
1062 ch = curses_read_char();
1064 if (!activemenu)
1065 curses_posthousekeeping();
1067 return ch;
1071 int nh_poskey(coordxy *x, coordxy *y, int *mod)
1072 -- Returns a single character input from the user or a
1073 a positioning event (perhaps from a mouse). If the
1074 return value is non-zero, a character was typed, else,
1075 a position in the MAP window is returned in x, y and mod.
1076 mod may be one of
1078 CLICK_1 -- mouse click type 1
1079 CLICK_2 -- mouse click type 2
1081 The different click types can map to whatever the
1082 hardware supports. If no mouse is supported, this
1083 routine always returns a non-zero character.
1086 curses_nh_poskey(coordxy *x, coordxy *y, int *mod)
1088 int key = curses_nhgetch();
1090 #ifdef NCURSES_MOUSE_VERSION
1091 /* Mouse event if mouse_support is true */
1092 if (key == KEY_MOUSE) {
1093 key = curses_get_mouse(x, y, mod);
1095 #else
1096 nhUse(x);
1097 nhUse(y);
1098 nhUse(mod);
1099 #endif
1101 return key;
1105 nhbell() -- Beep at user. [This will exist at least until sounds are
1106 redone, since sounds aren't attributable to windows anyway.]
1108 void
1109 curses_nhbell(void)
1111 if (!flags.silent)
1112 beep();
1116 doprev_message()
1117 -- Display previous messages. Used by the ^P command.
1118 -- On the tty-port this scrolls WIN_MESSAGE back one line.
1121 curses_doprev_message(void)
1123 curses_prev_mesg();
1124 return 0;
1128 char yn_function(const char *ques, const char *choices, char default)
1129 -- Print a prompt made up of ques, choices and default.
1130 Read a single character response that is contained in
1131 choices or default. If choices is NULL, all possible
1132 inputs are accepted and returned. This overrides
1133 everything else. The choices are expected to be in
1134 lower case. Entering ESC always maps to 'q', or 'n',
1135 in that order, if present in choices, otherwise it maps
1136 to default. Entering any other quit character (SPACE,
1137 RETURN, NEWLINE) maps to default.
1138 -- If the choices string contains ESC, then anything after
1139 it is an acceptable response, but the ESC and whatever
1140 follows is not included in the prompt.
1141 -- If the choices string contains a '#' then accept a count.
1142 Place this value in the global "yn_number" and return '#'.
1143 -- This uses the top line in the tty window-port, other
1144 ports might use a popup.
1146 char
1147 curses_yn_function(const char *question, const char *choices, char def)
1149 return (char) curses_character_input_dialog(question, choices, def);
1153 getlin(const char *ques, char *input)
1154 -- Prints ques as a prompt and reads a single line of text,
1155 up to a newline. The string entered is returned without the
1156 newline. ESC is used to cancel, in which case the string
1157 "\033\000" is returned.
1158 -- getlin() must call flush_screen(1) before doing anything.
1159 -- This uses the top line in the tty window-port, other
1160 ports might use a popup.
1162 void
1163 curses_getlin(const char *question, char *input)
1165 curses_line_input_dialog(question, input, BUFSZ);
1169 int get_ext_cmd(void)
1170 -- Get an extended command in a window-port specific way.
1171 An index into extcmdlist[] is returned on a successful
1172 selection, -1 otherwise.
1175 curses_get_ext_cmd(void)
1177 return curses_ext_cmd();
1182 number_pad(state)
1183 -- Initialize the number pad to the given state.
1185 void
1186 curses_number_pad(int state UNUSED)
1188 return;
1192 delay_output() -- Causes a visible delay of 50ms in the output.
1193 Conceptually, this is similar to wait_synch() followed
1194 by a nap(50ms), but allows asynchronous operation.
1196 void
1197 curses_delay_output(void)
1199 #ifdef TIMED_DELAY
1200 if (flags.nap && !iflags.debug_fuzzer) {
1201 /* refreshing the whole display is a waste of time,
1202 * but that's why we're here */
1203 curses_update_stdscr_cursor();
1204 refresh();
1205 napms(50);
1207 #endif
1211 start_screen() -- Only used on Unix tty ports, but must be declared for
1212 completeness. Sets up the tty to work in full-screen
1213 graphics mode. Look at win/tty/termcap.c for an
1214 example. If your window-port does not need this function
1215 just declare an empty function.
1217 void
1218 curses_start_screen(void)
1223 end_screen() -- Only used on Unix tty ports, but must be declared for
1224 completeness. The complement of start_screen().
1226 void
1227 curses_end_screen(void)
1232 outrip(winid, int)
1233 -- The tombstone code. We use genl_outrip() from rip.c
1234 instead of rolling our own.
1236 void
1237 curses_outrip(winid wid UNUSED,
1238 int how UNUSED,
1239 time_t when UNUSED)
1241 return;
1245 preference_update(preference)
1246 -- The player has just changed one of the wincap preference
1247 settings, and the NetHack core is notifying your window
1248 port of that change. If your window-port is capable of
1249 dynamically adjusting to the change then it should do so.
1250 Your window-port will only be notified of a particular
1251 change if it indicated that it wants to be by setting the
1252 corresponding bit in the wincap mask.
1254 void
1255 curses_preference_update(const char *pref)
1257 boolean redo_main = FALSE, redo_status = FALSE;
1259 if (!strcmp(pref, "align_status")
1260 || !strcmp(pref, "statuslines")
1261 || !strcmp(pref, "windowborders"))
1262 redo_main = redo_status = TRUE;
1263 else if (!strcmp(pref, "hilite_status"))
1264 redo_status = TRUE;
1265 else if (!strcmp(pref, "align_message"))
1266 redo_main = TRUE;
1267 else if (!strcmp(pref, "mouse_support"))
1268 curses_mouse_support(iflags.wc_mouse_support);
1270 if (redo_main || redo_status)
1271 curs_reset_windows(redo_main, redo_status);
1274 void
1275 curs_reset_windows(boolean redo_main, boolean redo_status)
1277 boolean need_redraw = FALSE;
1279 if (redo_status) {
1280 status_initialize(REASSESS_ONLY);
1281 need_redraw = TRUE;
1283 if (redo_main) {
1284 curses_create_main_windows();
1285 need_redraw = TRUE;
1287 if (need_redraw) {
1288 curses_last_messages();
1289 docrt();
1293 /* stubs for curses_procs{} */
1295 #ifdef POSITIONBAR
1296 static void
1297 dummy_update_position_bar(char *arg UNUSED)
1299 return;
1301 #endif
1303 #ifdef CHANGE_COLOR
1304 static void
1305 curses_change_color(int color, long rgb, int reverse UNUSED)
1307 short r, g, b;
1309 if (!can_change_color())
1310 return;
1312 r = (rgb >> 16) & 0xFF;
1313 g = (rgb >> 8) & 0xFF;
1314 b = rgb & 0xFF;
1315 init_color(color % 16, r * 4, g * 4, b * 4);
1318 static char *
1319 curses_get_color_string(void)
1321 return (char *) 0;
1323 #endif
1325 /*cursmain.c*/