diff --git a/dat/opthelp b/dat/opthelp index e2170af78..942788a91 100644 --- a/dat/opthelp +++ b/dat/opthelp @@ -114,6 +114,9 @@ preload_tiles control whether tiles get pre-loaded into RAM at [TRUE] Boolean option if TTY_TILES_ESCCODES was set at compile time (tty only): vt_tiledata insert extra data escape code markers into output [FALSE] +Boolean option if TTY_SOUND_ESCCODES was set at compile time (tty only): +vt_sounddata insert sound data escape code markers into output [FALSE] + Any Boolean option can be negated by prefixing it with a '!' or 'no'. diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 9800bb832..071227b3d 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -35,7 +35,7 @@ .ds vr "NetHack 3.7 .ds f0 "\*(vr .ds f1 -.ds f2 "June 20, 2020 +.ds f2 "July 2, 2020 . .\" A note on some special characters: .\" \(lq = left double quote @@ -4662,6 +4662,8 @@ the pattern to match; the sound file to play; .PL volume the volume to be set while playing the sound file. +.PL "vt_sounddata index" +optional - the index corresponding to a sound file; .PE .lp "" The pattern should be a POSIX extended regular expression. diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 8fa1bf2ef..13e9dded7 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -45,7 +45,7 @@ %.au \author{Original version - Eric S. Raymond\\ (Edited and expanded for 3.7 by Mike Stephenson and others)} -\date{June 20, 2020} +\date{July 2, 2020} \maketitle @@ -5110,6 +5110,7 @@ Each SOUND entry is broken down into the following parts: {\tt pattern } --- the pattern to match;\\ {\tt sound file} --- the sound file to play;\\ {\tt volume } --- the volume to be set while playing the sound file. +{\tt sound index } --- optional - the index corresponding to a sound file. %.ei %.ed \elist diff --git a/doc/fixes37.0 b/doc/fixes37.0 index 494d75429..c59d45dd6 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -298,6 +298,10 @@ Unix: when user name is used as default character name, keep hyphenated value intact instead stripping off dash and whatever follows as if that specified role/race/&c (worked once upon a time; broken since 3.3.0) Unix: add "ec2-user" to the list of user names 'sysconf' classifies as generic +user_sounds: move the message hook from inside individual window display ports + to the core where it allows MSGTYP_NOSHOW msgtyp's to still trigger + sounds to correct a reported github issue; also fixes a past reported + issue that the curses port on Windows not working with user_sounds Windows: update for new status condition fields Windows: include commented-out 'PORTABLE_DEVICE_PATHS' in sysconf.template X11: substantial overhaul of status display, both 'fancy' and 'tty-style' @@ -384,6 +388,11 @@ record number of wishes and artifact wishes in xlogfile Platform- and/or Interface-Specific New Features ------------------------------------------------ +user_sounds: provide an experimental mechanism for terminal-side sounds similar + to the method used for vt_tiledata; new option vt_sounddata that also + requires compile-time definition of TTY_SOUND_ESCCODES (also requires + terminal-side code external to NetHack to recognize the sequence and + act on it NetHack Community Patches (or Variation) Included diff --git a/include/config.h b/include/config.h index f1af270ef..7bcb6bbc9 100644 --- a/include/config.h +++ b/include/config.h @@ -474,7 +474,10 @@ typedef unsigned char uchar; */ /* TTY_TILES_ESCCODES: Enable output of special console escape codes - * which act as hints for external programs such as EbonHack. + * which act as hints for external programs such as EbonHack, or hterm. + * + * TTY_SOUND_ESCCODES: Enable output of special console escape codes + * which act as hints for theoretical external programs to play sound effect. * * Only for TTY_GRAPHICS. * @@ -482,12 +485,14 @@ typedef unsigned char uchar; * one or more positive integer values, separated by semicolons. * For example ESC [ 1 ; 0 ; 120 z * - * Possible codes are: + * Possible TTY_TILES_ESCCODES codes are: * ESC [ 1 ; 0 ; n ; m z Start a glyph (aka a tile) number n, with flags m * ESC [ 1 ; 1 z End a glyph. * ESC [ 1 ; 2 ; n z Select a window n to output to. * ESC [ 1 ; 3 z End of data. NetHack has finished sending data, * and is waiting for input. + * Possible TTY_SOUND_ESCCODES codes are: + * ESC [ 1 ; 4 ; n ; m z Play specified sound n, volume m * * Whenever NetHack outputs anything, it will first output the "select window" * code. Whenever NetHack outputs a tile, it will first output the "start @@ -496,9 +501,10 @@ typedef unsigned char uchar; * * To compile NetHack with this, add tile.c to WINSRC and tile.o to WINOBJ * in the hints file or Makefile. - * Set boolean option vt_tiledata in your config file to turn this on. + * Set boolean option vt_xdata in your config file to turn either of these on. * Note that gnome-terminal at least doesn't work with this. */ /* #define TTY_TILES_ESCCODES */ +/* #define TTY_SOUND_ESCCODES */ /* NetHack will execute an external program whenever a new message-window * message is shown. The program to execute is given in environment variable diff --git a/include/flag.h b/include/flag.h index 5ba296d46..f75f66e30 100644 --- a/include/flag.h +++ b/include/flag.h @@ -326,6 +326,9 @@ struct instance_flags { #endif #ifdef TTY_TILES_ESCCODES boolean vt_tiledata; /* output console codes for tile support in TTY */ +#endif +#ifdef TTY_SOUND_ESCCODES + boolean vt_sounddata; /* output console codes for sound support in TTY*/ #endif boolean clicklook; /* allow right-clicking for look */ boolean cmdassist; /* provide detailed assistance for some comnds */ diff --git a/include/ntconf.h b/include/ntconf.h index ae86e93d4..7b7edb407 100644 --- a/include/ntconf.h +++ b/include/ntconf.h @@ -29,6 +29,7 @@ #define DUMPLOG_MSG_COUNT 50 #define USER_SOUNDS +#define TTY_SOUND_ESCCODES /*#define CHANGE_COLOR*/ /* allow palette changes */ #define SELECTSAVED /* Provide menu of saved games to choose from at start */ diff --git a/include/optlist.h b/include/optlist.h index e741351eb..8955b078c 100644 --- a/include/optlist.h +++ b/include/optlist.h @@ -516,6 +516,13 @@ pfx_##a, #else NHOPTB(vt_tiledata, 0, opt_in, set_in_config, Off, Yes, No, No, NoAlias, (boolean *) 0) +#endif +#ifdef TTY_SOUND_ESCCODES + NHOPTB(vt_sounddata, 0, opt_in, set_in_config, Off, Yes, No, No, NoAlias, + &iflags.vt_sounddata) +#else + NHOPTB(vt_sounddata, 0, opt_in, set_in_config, Off, Yes, No, No, NoAlias, + (boolean *) 0) #endif NHOPTC(warnings, 10, opt_in, set_in_config, No, Yes, No, No, NoAlias, "display characters for warnings") diff --git a/src/pline.c b/src/pline.c index 28d5dd4be..16594654f 100644 --- a/src/pline.c +++ b/src/pline.c @@ -15,7 +15,9 @@ static char *FDECL(You_buf, (int)); #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__)) static void FDECL(execplinehandler, (const char *)); #endif - +#ifdef USER_SOUNDS +extern void FDECL(maybe_play_sound, (const char *)); +#endif #ifdef DUMPLOG /* keep the most recent DUMPLOG_MSG_COUNT messages */ @@ -163,6 +165,7 @@ VA_DECL(const char *, line) pbuf[BUFSZ - 1] = '\0'; line = pbuf; } + msgtyp = MSGTYP_NORMAL; #ifdef DUMPLOG /* We hook here early to have options-agnostic output. @@ -182,10 +185,13 @@ VA_DECL(const char *, line) goto pline_done; } - msgtyp = MSGTYP_NORMAL; no_repeat = (g.pline_flags & PLINE_NOREPEAT) ? TRUE : FALSE; if ((g.pline_flags & OVERRIDE_MSGTYPE) == 0) { msgtyp = msgtype_type(line, no_repeat); +#ifdef USER_SOUNDS + if (msgtyp == MSGTYP_NORMAL || msgtyp == MSGTYP_NOSHOW) + maybe_play_sound(line); +#endif if ((g.pline_flags & URGENT_MESSAGE) == 0 && (msgtyp == MSGTYP_NOSHOW || (msgtyp == MSGTYP_NOREP && !strcmp(line, g.prevmsg)))) diff --git a/src/sounds.c b/src/sounds.c index 28c3369fd..667ecef9c 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -1258,16 +1258,23 @@ tiphat() #ifdef USER_SOUNDS +#if defined(WIN32) || defined(QT_GRAPHICS) extern void FDECL(play_usersound, (const char *, int)); +#endif +#if defined(TTY_SOUND_ESCCODES) +extern void FDECL(play_usersound_via_idx, (int, int)); +#endif typedef struct audio_mapping_rec { struct nhregex *regex; char *filename; int volume; + int idx; struct audio_mapping_rec *next; } audio_mapping; static audio_mapping *soundmap = 0; +static audio_mapping *FDECL(sound_matches_message, (const char *)); char *sounddir = 0; /* set in files.c */ @@ -1279,26 +1286,29 @@ const char *mapping; char text[256]; char filename[256]; char filespec[256]; - int volume; + int volume, idx = -1; + boolean toolong = FALSE; - if (sscanf(mapping, "MESG \"%255[^\"]\"%*[\t ]\"%255[^\"]\" %d", text, + if (sscanf(mapping, "MESG \"%255[^\"]\"%*[\t ]\"%255[^\"]\" %d %d", text, + filename, &volume, &idx) == 4 + || sscanf(mapping, "MESG \"%255[^\"]\"%*[\t ]\"%255[^\"]\" %d", text, filename, &volume) == 3) { audio_mapping *new_map; if (!sounddir) sounddir = dupstr("."); - if (strlen(sounddir) + 1 + strlen(filename) >= sizeof filespec) { raw_print("sound file name too long"); return 0; - } + } Sprintf(filespec, "%s/%s", sounddir, filename); - if (can_read_file(filespec)) { + if (idx >= 0 || can_read_file(filespec)) { new_map = (audio_mapping *) alloc(sizeof *new_map); new_map->regex = regex_init(); new_map->filename = dupstr(filespec); new_map->volume = volume; + new_map->idx = idx; new_map->next = soundmap; if (!regex_compile(text, new_map->regex)) { @@ -1325,18 +1335,56 @@ const char *mapping; return 1; } +static audio_mapping * +sound_matches_message(msg) +const char *msg; +{ + audio_mapping *snd = soundmap; + + while (snd) { + if (regex_match(msg, snd->regex)) + return snd; + snd = snd->next; + } + return (audio_mapping *) 0; +} + void play_sound_for_message(msg) const char *msg; { - audio_mapping *cursor = soundmap; + audio_mapping *snd = sound_matches_message(msg); - while (cursor) { - if (regex_match(msg, cursor->regex)) { - play_usersound(cursor->filename, cursor->volume); - } - cursor = cursor->next; - } + if (snd) + play_usersound(snd->filename, snd->volume); +} + +void +maybe_play_sound(msg) +const char *msg; +{ +#if defined(WIN32) || defined(QT_GRAPHICS) || defined(TTY_SOUND_ESCCODES) + audio_mapping *snd = sound_matches_message(msg); + + if (snd +#if defined(WIN32) || defined(QT_GRAPHICS) +#ifdef TTY_SOUND_ESCCODES + && !iflags.vt_sounddata +#endif +#if defined(QT_GRAPHICS) + && WINDOWPORT("Qt") +#endif +#if defined(WIN32) + && (WINDOWPORT("tty") || WINDOWPORT("mswin") || WINDOWPORT("curses")) +#endif +#endif /* WIN32 || QT_GRAPHICS */ + ) + play_usersound(snd->filename, snd->volume); +#if defined(TTY_GRAPHICS) && defined(TTY_SOUND_ESCCODES) + else if (snd && iflags.vt_sounddata && snd->idx >= 0 && WINDOWPORT("tty")) + play_usersound_via_idx(snd->idx, snd->volume); +#endif /* TTY_GRAPHICS && TTY_SOUND_ESCCODES */ +#endif /* WIN32 || QT_GRAPHICS || TTY_SOUND_ESCCODES */ } void diff --git a/sys/winnt/stubs.c b/sys/winnt/stubs.c index 447b0dabb..805298305 100644 --- a/sys/winnt/stubs.c +++ b/sys/winnt/stubs.c @@ -189,4 +189,13 @@ set_altkeyhandler(const char *inName) { return; } + +#if defined(USER_SOUNDS) && defined(TTY_SOUND_ESCCODES) +void +play_usersound_via_idx(idx, volume) +int idx, volume; +{ +} +#endif /* USER_SOUNDS && TTY_SOUND_ESCCODES */ + #endif /* TTYSTUBS */ diff --git a/win/Qt/qt_msg.cpp b/win/Qt/qt_msg.cpp index 6236f9817..516e992df 100644 --- a/win/Qt/qt_msg.cpp +++ b/win/Qt/qt_msg.cpp @@ -103,10 +103,6 @@ const char * NetHackQtMessageWindow::GetStr(bool init) void NetHackQtMessageWindow::PutStr(int attr, const QString& text) { -#ifdef USER_SOUNDS - play_sound_for_message(text.toLatin1().constData()); -#endif - changed=true; // If the line is output from the "/" command, map the first character diff --git a/win/tty/wintty.c b/win/tty/wintty.c index bb993aa54..bf63421ed 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 wintty.c $NHDT-Date: 1587110794 2020/04/17 08:06:34 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.256 $ */ + /* NetHack 3.6 wintty.c $NHDT-Date: 1587110794 2020/04/17 08:06:34 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.256 $ */ /* Copyright (c) David Cohrs, 1991 */ /* NetHack may be freely redistributed. See license for details. */ @@ -36,14 +36,19 @@ extern void msmsg(const char *, ...); #endif #endif +#if defined(TTY_TILES_ESCCODES) || defined(TTY_SOUND_ESCCODES) +#define VT_ANSI_COMMAND 'z' +#endif #ifdef TTY_TILES_ESCCODES extern short glyph2tile[]; -#define TILE_ANSI_COMMAND 'z' #define AVTC_GLYPH_START 0 #define AVTC_GLYPH_END 1 #define AVTC_SELECT_WINDOW 2 #define AVTC_INLINE_SYNC 3 #endif +#ifdef TTY_SOUND_ESCCODES +#define AVTC_SOUND_PLAY 4 +#endif #ifdef HANGUP_HANDLING /* @@ -244,11 +249,11 @@ int i, c, d; vt_tile_current_window = c; } if (d >= 0) - printf("\033[1;%d;%d;%d%c", i, c, d, TILE_ANSI_COMMAND); + printf("\033[1;%d;%d;%d%c", i, c, d, VT_ANSI_COMMAND); else - printf("\033[1;%d;%d%c", i, c, TILE_ANSI_COMMAND); + printf("\033[1;%d;%d%c", i, c, VT_ANSI_COMMAND); } else { - printf("\033[1;%d%c", i, TILE_ANSI_COMMAND); + printf("\033[1;%d%c", i, VT_ANSI_COMMAND); } } } @@ -259,6 +264,24 @@ int i, c, d; #define print_vt_code2(i,c) print_vt_code((i), (c), -1) #define print_vt_code3(i,c,d) print_vt_code((i), (c), (d)) +#ifdef TTY_SOUND_ESCCODES +void +print_vt_soundcode_idx(idx, v) +int idx, v; +{ + HUPSKIP(); + if (iflags.vt_sounddata) { + if (v >= 0) + printf("\033[1;%d;%d;%d%c", AVTC_SOUND_PLAY, + idx, v, VT_ANSI_COMMAND); + else + printf("\033[1;%d;%d%c", AVTC_SOUND_PLAY, + idx, VT_ANSI_COMMAND); + } +} +#else +# define print_vt_soundcode_idx(idx, v) ; +#endif /* !TTY_SOUND_ESCCODES */ /* clean up and quit */ static void @@ -2678,10 +2701,6 @@ const char *str; messages, clear flag mask leaving only display attr */ /*attr &= ~(ATR_URGENT | ATR_NOHISTORY);*/ - /* really do this later */ -#if defined(USER_SOUNDS) && defined(WIN32CON) - play_sound_for_message(str); -#endif if (!suppress_history) { /* normal output; add to current top line if room, else flush whatever is there to history and then write this */ @@ -4634,6 +4653,15 @@ render_status(VOID_ARGS) #endif /* STATUS_HILITES */ +#if defined(USER_SOUNDS) && defined(TTY_SOUND_ESCCODES) +void +play_usersound_via_idx(idx, volume) +int idx, volume; +{ + print_vt_soundcode_idx(idx, volume); +} +#endif /* USER_SOUNDS && TTY_SOUND_ESCCODES */ + #endif /* TTY_GRAPHICS */ /*wintty.c*/