Add key rebinding
This is a modified version of Jason Dorje Short's key rebinding patch, and allows also binding special keys, such as the ones used in getloc and getpos. One of the ways to play NetHack on nethack.alt.org is via a HTML terminal in browser. Unfortunately this means several ctrl-key combinations cannot be entered, because the browser intercepts those. Similar thing applies to some international keyboard layouts on Windows. With this patch, the user can just rebind the command to a key that works best for them. I've tested this on Linux TTY, X11, and Windows TTY and GUI.
This commit is contained in:
@@ -34,9 +34,6 @@ boolean resuming;
|
||||
monstr_init(); /* monster strengths */
|
||||
objects_init();
|
||||
|
||||
if (wizard)
|
||||
add_debug_extended_commands();
|
||||
|
||||
/* if a save file created in normal mode is now being restored in
|
||||
explore mode, treat it as normal restore followed by 'X' command
|
||||
to use up the save file and require confirmation for explore mode */
|
||||
|
||||
150
src/do_name.c
150
src/do_name.c
@@ -59,45 +59,84 @@ const char *goal;
|
||||
putstr(tmpwin, 0, sbuf);
|
||||
putstr(tmpwin, 0, "Use 'H', 'J', 'K', 'L' to move the cursor 8 units at a time.");
|
||||
putstr(tmpwin, 0, "Or enter a background symbol (ex. '<').");
|
||||
putstr(tmpwin, 0, "Use '@' to move the cursor on yourself.");
|
||||
if (!iflags.terrainmode || (iflags.terrainmode & TER_MON) != 0)
|
||||
putstr(tmpwin, 0, "Use 'm' or 'M' to move the cursor to next monster.");
|
||||
if (!iflags.terrainmode || (iflags.terrainmode & TER_OBJ) != 0)
|
||||
putstr(tmpwin, 0, "Use 'o' or 'O' to move the cursor to next object.");
|
||||
Sprintf(sbuf, "Use '%s' to move the cursor on yourself.",
|
||||
visctrl(Cmd.spkeys[NHKF_GETPOS_SELF]));
|
||||
putstr(tmpwin, 0, sbuf);
|
||||
if (!iflags.terrainmode || (iflags.terrainmode & TER_MON) != 0) {
|
||||
Sprintf(sbuf, "Use '%s' or '%s' to move the cursor to next monster.",
|
||||
visctrl(Cmd.spkeys[NHKF_GETPOS_MON_NEXT]),
|
||||
visctrl(Cmd.spkeys[NHKF_GETPOS_MON_PREV]));
|
||||
putstr(tmpwin, 0, sbuf);
|
||||
}
|
||||
if (!iflags.terrainmode || (iflags.terrainmode & TER_OBJ) != 0) {
|
||||
Sprintf(sbuf, "Use '%s' or '%s' to move the cursor to next object.",
|
||||
visctrl(Cmd.spkeys[NHKF_GETPOS_OBJ_NEXT]),
|
||||
visctrl(Cmd.spkeys[NHKF_GETPOS_OBJ_PREV]));
|
||||
putstr(tmpwin, 0, sbuf);
|
||||
}
|
||||
if (!iflags.terrainmode || (iflags.terrainmode & TER_MAP) != 0) {
|
||||
/* both of these are primarily useful when choosing a travel
|
||||
destination for the '_' command */
|
||||
putstr(tmpwin, 0,
|
||||
"Use 'd' or 'D' to move the cursor to next door or doorway.");
|
||||
putstr(tmpwin, 0,
|
||||
"Use 'x' or 'X' to move the cursor to unexplored location.");
|
||||
Sprintf(sbuf,
|
||||
"Use '%s' or '%s' to move the cursor to next door or doorway.",
|
||||
visctrl(Cmd.spkeys[NHKF_GETPOS_DOOR_NEXT]),
|
||||
visctrl(Cmd.spkeys[NHKF_GETPOS_DOOR_PREV]));
|
||||
putstr(tmpwin, 0, sbuf);
|
||||
Sprintf(sbuf,
|
||||
"Use '%s' or '%s' to move the cursor to unexplored location.",
|
||||
visctrl(Cmd.spkeys[NHKF_GETPOS_UNEX_NEXT]),
|
||||
visctrl(Cmd.spkeys[NHKF_GETPOS_UNEX_PREV]));
|
||||
putstr(tmpwin, 0, sbuf);
|
||||
}
|
||||
if (!iflags.terrainmode) {
|
||||
if (getpos_hilitefunc)
|
||||
putstr(tmpwin, 0, "Use '$' to display valid locations.");
|
||||
putstr(tmpwin, 0, "Use '#' to toggle automatic description.");
|
||||
if (iflags.cmdassist) /* assisting the '/' command, I suppose... */
|
||||
putstr(tmpwin, 0,
|
||||
(iflags.getpos_coords == GPCOORDS_NONE)
|
||||
? "(Set 'whatis_coord' option to include coordinates with '#' text.)"
|
||||
: "(Reset 'whatis_coord' option to omit coordinates from '#' text.)");
|
||||
char kbuf[BUFSZ];
|
||||
if (getpos_hilitefunc) {
|
||||
Sprintf(sbuf, "Use '%s' to display valid locations.",
|
||||
visctrl(Cmd.spkeys[NHKF_GETPOS_SHOWVALID]));
|
||||
putstr(tmpwin, 0, sbuf);
|
||||
}
|
||||
Sprintf(sbuf, "Use '%s' to toggle automatic description.",
|
||||
visctrl(Cmd.spkeys[NHKF_GETPOS_AUTODESC]));
|
||||
putstr(tmpwin, 0, sbuf);
|
||||
if (iflags.cmdassist) { /* assisting the '/' command, I suppose... */
|
||||
Sprintf(sbuf,
|
||||
(iflags.getpos_coords == GPCOORDS_NONE)
|
||||
? "(Set 'whatis_coord' option to include coordinates with '%s' text.)"
|
||||
: "(Reset 'whatis_coord' option to omit coordinates from '%s' text.)",
|
||||
visctrl(Cmd.spkeys[NHKF_GETPOS_AUTODESC]));
|
||||
}
|
||||
/* disgusting hack; the alternate selection characters work for any
|
||||
getpos call, but only matter for dowhatis (and doquickwhatis) */
|
||||
doing_what_is = (goal == what_is_an_unknown_object);
|
||||
Sprintf(sbuf, "Type a '.'%s when you are at the right place.",
|
||||
doing_what_is ? " or ',' or ';' or ':'" : "");
|
||||
doing_what_is = (goal == what_is_an_unknown_object);
|
||||
if (doing_what_is) {
|
||||
Sprintf(kbuf, "'%s' or '%s' or '%s' or '%s'",
|
||||
visctrl(Cmd.spkeys[NHKF_GETPOS_PICK]),
|
||||
visctrl(Cmd.spkeys[NHKF_GETPOS_PICK_Q]),
|
||||
visctrl(Cmd.spkeys[NHKF_GETPOS_PICK_O]),
|
||||
visctrl(Cmd.spkeys[NHKF_GETPOS_PICK_V]));
|
||||
} else {
|
||||
Sprintf(kbuf, "'%s'", visctrl(Cmd.spkeys[NHKF_GETPOS_PICK]));
|
||||
}
|
||||
Sprintf(sbuf, "Type a %s when you are at the right place.", kbuf);
|
||||
putstr(tmpwin, 0, sbuf);
|
||||
if (doing_what_is) {
|
||||
putstr(tmpwin, 0,
|
||||
" ':' describe current spot, show 'more info', move to another spot.");
|
||||
Sprintf(sbuf,
|
||||
" '.' describe current spot,%s move to another spot;",
|
||||
" '%s' describe current spot, show 'more info', move to another spot.",
|
||||
visctrl(Cmd.spkeys[NHKF_GETPOS_PICK_V]));
|
||||
putstr(tmpwin, 0, sbuf);
|
||||
Sprintf(sbuf,
|
||||
" '%s' describe current spot,%s move to another spot;",
|
||||
visctrl(Cmd.spkeys[NHKF_GETPOS_PICK]),
|
||||
flags.help ? " prompt if 'more info'," : "");
|
||||
putstr(tmpwin, 0, sbuf);
|
||||
putstr(tmpwin, 0,
|
||||
" ',' describe current spot, move to another spot;");
|
||||
putstr(tmpwin, 0,
|
||||
" ';' describe current spot, stop looking at things;");
|
||||
Sprintf(sbuf,
|
||||
" '%s' describe current spot, move to another spot;",
|
||||
visctrl(Cmd.spkeys[NHKF_GETPOS_PICK_Q]));
|
||||
putstr(tmpwin, 0, sbuf);
|
||||
Sprintf(sbuf,
|
||||
" '%s' describe current spot, stop looking at things;",
|
||||
visctrl(Cmd.spkeys[NHKF_GETPOS_PICK_O]));
|
||||
putstr(tmpwin, 0, sbuf);
|
||||
}
|
||||
}
|
||||
if (!force)
|
||||
@@ -337,9 +376,27 @@ coord *ccp;
|
||||
boolean force;
|
||||
const char *goal;
|
||||
{
|
||||
static const char pick_chars[] = ".,;:",
|
||||
mMoOdDxX[] = "mMoOdDxX";
|
||||
const char *cp;
|
||||
struct {
|
||||
int nhkf, ret;
|
||||
} const pick_chars_def[] = {
|
||||
{ NHKF_GETPOS_PICK, LOOK_TRADITIONAL },
|
||||
{ NHKF_GETPOS_PICK_Q, LOOK_QUICK },
|
||||
{ NHKF_GETPOS_PICK_O, LOOK_ONCE },
|
||||
{ NHKF_GETPOS_PICK_V, LOOK_VERBOSE }
|
||||
};
|
||||
const int mMoOdDxX_def[] = {
|
||||
NHKF_GETPOS_MON_NEXT,
|
||||
NHKF_GETPOS_MON_PREV,
|
||||
NHKF_GETPOS_OBJ_NEXT,
|
||||
NHKF_GETPOS_OBJ_PREV,
|
||||
NHKF_GETPOS_DOOR_NEXT,
|
||||
NHKF_GETPOS_DOOR_PREV,
|
||||
NHKF_GETPOS_UNEX_NEXT,
|
||||
NHKF_GETPOS_UNEX_PREV
|
||||
};
|
||||
char pick_chars[6];
|
||||
char mMoOdDxX[9];
|
||||
int result = 0;
|
||||
int cx, cy, i, c;
|
||||
int sidx, tx, ty;
|
||||
@@ -350,10 +407,19 @@ const char *goal;
|
||||
int gcount[NUM_GLOCS] = DUMMY;
|
||||
int gidx[NUM_GLOCS] = DUMMY;
|
||||
|
||||
for (i = 0; i < SIZE(pick_chars_def); i++)
|
||||
pick_chars[i] = Cmd.spkeys[pick_chars_def[i].nhkf];
|
||||
pick_chars[SIZE(pick_chars_def)] = '\0';
|
||||
|
||||
for (i = 0; i < SIZE(mMoOdDxX_def); i++)
|
||||
mMoOdDxX[i] = Cmd.spkeys[mMoOdDxX_def[i]];
|
||||
mMoOdDxX[SIZE(mMoOdDxX_def)] = '\0';
|
||||
|
||||
if (!goal)
|
||||
goal = "desired location";
|
||||
if (flags.verbose) {
|
||||
pline("(For instructions type a '?')");
|
||||
pline("(For instructions type a '%s')",
|
||||
visctrl(Cmd.spkeys[NHKF_GETPOS_HELP]));
|
||||
msg_given = TRUE;
|
||||
}
|
||||
cx = ccp->x;
|
||||
@@ -388,7 +454,7 @@ const char *goal;
|
||||
if (iflags.autodescribe)
|
||||
msg_given = FALSE;
|
||||
|
||||
if (c == '\033') {
|
||||
if (c == Cmd.spkeys[NHKF_ESC]) {
|
||||
cx = cy = -10;
|
||||
msg_given = TRUE; /* force clear */
|
||||
result = -1;
|
||||
@@ -404,7 +470,7 @@ const char *goal;
|
||||
}
|
||||
if ((cp = index(pick_chars, c)) != 0) {
|
||||
/* '.' => 0, ',' => 1, ';' => 2, ':' => 3 */
|
||||
result = (int) (cp - pick_chars);
|
||||
result = pick_chars_def[(int) (cp - pick_chars)].ret;
|
||||
break;
|
||||
}
|
||||
for (i = 0; i < 8; i++) {
|
||||
@@ -442,26 +508,23 @@ const char *goal;
|
||||
goto nxtc;
|
||||
}
|
||||
|
||||
if (c == '?' || redraw_cmd(c)) {
|
||||
if (c == '?')
|
||||
if (c == Cmd.spkeys[NHKF_GETPOS_HELP] || redraw_cmd(c)) {
|
||||
if (c == Cmd.spkeys[NHKF_GETPOS_HELP])
|
||||
getpos_help(force, goal);
|
||||
else /* ^R */
|
||||
docrt(); /* redraw */
|
||||
/* update message window to reflect that we're still targetting */
|
||||
show_goal_msg = TRUE;
|
||||
msg_given = TRUE;
|
||||
} else if (c == '$' && getpos_hilitefunc) {
|
||||
} else if (c == Cmd.spkeys[NHKF_GETPOS_SHOWVALID]
|
||||
&& getpos_hilitefunc) {
|
||||
if (!hilite_state) {
|
||||
(*getpos_hilitefunc)(0);
|
||||
(*getpos_hilitefunc)(1);
|
||||
hilite_state = TRUE;
|
||||
}
|
||||
goto nxtc;
|
||||
} else if (c == '#') {
|
||||
/* unfortunately, using '#' as a command means we can't move
|
||||
cursor to sinks, iron bars, and poison clouds; perhaps
|
||||
when autodescribe is already on, next '#' should try to
|
||||
move to '#' rather than to toggle off? (or ask; ick...) */
|
||||
} else if (c == Cmd.spkeys[NHKF_GETPOS_AUTODESC]) {
|
||||
iflags.autodescribe = !iflags.autodescribe;
|
||||
pline("Automatic description %sis %s.",
|
||||
flags.verbose ? "of features under cursor " : "",
|
||||
@@ -470,7 +533,7 @@ const char *goal;
|
||||
show_goal_msg = TRUE;
|
||||
msg_given = TRUE;
|
||||
goto nxtc;
|
||||
} else if (c == '@') { /* return to hero's spot */
|
||||
} else if (c == Cmd.spkeys[NHKF_GETPOS_SELF]) {
|
||||
/* reset 'm&M', 'o&O', &c; otherwise, there's no way for player
|
||||
to achieve that except by manually cycling through all spots */
|
||||
for (i = 0; i < NUM_GLOCS; i++)
|
||||
@@ -568,9 +631,10 @@ const char *goal;
|
||||
if (!force)
|
||||
Strcpy(note, "aborted");
|
||||
else
|
||||
Sprintf(note, "use '%c', '%c', '%c', '%c' or '.'", /* hjkl */
|
||||
Sprintf(note, "use '%c', '%c', '%c', '%c' or '%s'", /* hjkl */
|
||||
Cmd.move_W, Cmd.move_S, Cmd.move_N,
|
||||
Cmd.move_E);
|
||||
Cmd.move_E,
|
||||
visctrl(Cmd.spkeys[NHKF_GETPOS_PICK]));
|
||||
pline("Unknown direction: '%s' (%s).", visctrl((char) c),
|
||||
note);
|
||||
msg_given = TRUE;
|
||||
|
||||
@@ -2152,6 +2152,10 @@ int src;
|
||||
parseoptions(bufp, TRUE, TRUE);
|
||||
} else if (match_varname(buf, "AUTOPICKUP_EXCEPTION", 5)) {
|
||||
add_autopickup_exception(bufp);
|
||||
} else if (match_varname(buf, "BINDINGS", 4)) {
|
||||
parsebindings(bufp);
|
||||
} else if (match_varname(buf, "AUTOCOMPLETE", 5)) {
|
||||
parseautocomplete(bufp, TRUE);
|
||||
} else if (match_varname(buf, "MSGTYPE", 7)) {
|
||||
(void) msgtype_parse_add(bufp);
|
||||
#ifdef NOCWD_ASSUMPTIONS
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
char * ucase (char *)
|
||||
char * upstart (char *)
|
||||
char * mungspaces (char *)
|
||||
char * trimspaces (char *)
|
||||
char * strip_newline (char *)
|
||||
char * eos (char *)
|
||||
boolean str_end_is (const char *, const char *)
|
||||
@@ -159,6 +160,22 @@ char *bp;
|
||||
return bp;
|
||||
}
|
||||
|
||||
/* remove leading and trailing whitespace, in place */
|
||||
char*
|
||||
trimspaces(txt)
|
||||
char* txt;
|
||||
{
|
||||
char* end;
|
||||
|
||||
while (*txt == ' ' || *txt == '\t')
|
||||
txt++;
|
||||
end = eos(txt);
|
||||
while (--end >= txt && (*end == ' ' || *end == '\t'))
|
||||
*end = '\0';
|
||||
|
||||
return txt;
|
||||
}
|
||||
|
||||
/* remove \n from end of line; remove \r too if one is there */
|
||||
char *
|
||||
strip_newline(str)
|
||||
@@ -385,13 +402,17 @@ char *sbuf;
|
||||
return strcpy(sbuf, buf);
|
||||
}
|
||||
|
||||
#define VISCTRL_NBUF 5
|
||||
/* make a displayable string from a character */
|
||||
char *
|
||||
visctrl(c)
|
||||
char c;
|
||||
{
|
||||
Static char ccc[5];
|
||||
Static char visctrl_bufs[VISCTRL_NBUF][5];
|
||||
static int nbuf = 0;
|
||||
register int i = 0;
|
||||
char *ccc = visctrl_bufs[nbuf];
|
||||
nbuf = (nbuf + 1) % VISCTRL_NBUF;
|
||||
|
||||
if ((uchar) c & 0200) {
|
||||
ccc[i++] = 'M';
|
||||
|
||||
@@ -833,6 +833,10 @@ int maxlen;
|
||||
* has the effect of 'meta'-ing the value which follows (so that the
|
||||
* alternate character set will be enabled).
|
||||
*
|
||||
* X normal key X
|
||||
* ^X control-X
|
||||
* \mX meta-X
|
||||
*
|
||||
* For 3.4.3 and earlier, input ending with "\M", backslash, or caret
|
||||
* prior to terminating '\0' would pull that '\0' into the output and then
|
||||
* keep processing past it, potentially overflowing the output buffer.
|
||||
@@ -3241,7 +3245,6 @@ boolean tinitial, tfrom_file;
|
||||
if (negated) {
|
||||
bad_negation(fullname, FALSE);
|
||||
} else if ((op = string_for_opt(opts, FALSE)) != 0) {
|
||||
int j;
|
||||
char c, op_buf[BUFSZ];
|
||||
|
||||
escapes(op, op_buf);
|
||||
@@ -3475,6 +3478,56 @@ boolean tinitial, tfrom_file;
|
||||
badoption(opts);
|
||||
}
|
||||
|
||||
/* parse key:command */
|
||||
void
|
||||
parsebindings(bindings)
|
||||
char* bindings;
|
||||
{
|
||||
char *bind;
|
||||
char key;
|
||||
int i;
|
||||
|
||||
/* break off first binding from the rest; parse the rest */
|
||||
if ((bind = index(bindings, ',')) != 0) {
|
||||
*bind++ = 0;
|
||||
parsebindings(bind);
|
||||
}
|
||||
|
||||
/* parse a single binding: first split around : */
|
||||
if (! (bind = index(bindings, ':'))) return; /* it's not a binding */
|
||||
*bind++ = 0;
|
||||
|
||||
/* read the key to be bound */
|
||||
key = txt2key(bindings);
|
||||
if (!key) {
|
||||
raw_printf("Bad binding %s.", bindings);
|
||||
wait_synch();
|
||||
return;
|
||||
}
|
||||
|
||||
bind = trimspaces(bind);
|
||||
|
||||
/* is it a special key? */
|
||||
if (bind_specialkey(key, bind))
|
||||
return;
|
||||
|
||||
/* is it a menu command? */
|
||||
for (i = 0; i < NUM_MENU_CMDS; i++) {
|
||||
if (!strcmp(default_menu_cmd_info[i].name, bind)) {
|
||||
if (illegal_menu_cmd_key(key)) {
|
||||
char tmp[BUFSZ];
|
||||
Sprintf(tmp, "Bad menu key %s:%s", visctrl(key), bind);
|
||||
badoption(tmp);
|
||||
} else
|
||||
add_menu_cmd_alias(key, default_menu_cmd_info[i].cmd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* extended command? */
|
||||
bind_key(key, bind);
|
||||
}
|
||||
|
||||
static NEARDATA const char *menutype[] = { "traditional", "combination",
|
||||
"full", "partial" };
|
||||
|
||||
|
||||
18
src/pager.c
18
src/pager.c
@@ -1371,6 +1371,7 @@ whatdoes_help()
|
||||
destroy_nhwindow(tmpwin);
|
||||
}
|
||||
|
||||
#if 0
|
||||
#define WD_STACKLIMIT 5
|
||||
struct wd_stack_frame {
|
||||
Bitfield(active, 1);
|
||||
@@ -1501,18 +1502,31 @@ int *depth, lnum;
|
||||
}
|
||||
return stack[*depth].active ? TRUE : FALSE;
|
||||
}
|
||||
#endif /* 0 */
|
||||
|
||||
char *
|
||||
dowhatdoes_core(q, cbuf)
|
||||
char q;
|
||||
char *cbuf;
|
||||
{
|
||||
dlb *fp;
|
||||
char buf[BUFSZ];
|
||||
#if 0
|
||||
dlb *fp;
|
||||
struct wd_stack_frame stack[WD_STACKLIMIT];
|
||||
boolean cond;
|
||||
int ctrl, meta, depth = 0, lnum = 0;
|
||||
#endif /* 0 */
|
||||
const char *ec_desc;
|
||||
|
||||
if ((ec_desc = key2extcmddesc(q)) != NULL) {
|
||||
char keybuf[QBUFSZ];
|
||||
|
||||
Sprintf(buf, "%-8s%s.", key2txt(q, keybuf), ec_desc);
|
||||
Strcpy(cbuf, buf);
|
||||
return cbuf;
|
||||
}
|
||||
return 0;
|
||||
#if 0
|
||||
fp = dlb_fopen(CMDHELPFILE, "r");
|
||||
if (!fp) {
|
||||
pline("Cannot open \"%s\" data file!", CMDHELPFILE);
|
||||
@@ -1568,6 +1582,7 @@ char *cbuf;
|
||||
if (depth != 0)
|
||||
impossible("cmdhelp: mismatched &? &: &. conditionals.");
|
||||
return (char *) 0;
|
||||
#endif /* 0 */
|
||||
}
|
||||
|
||||
int
|
||||
@@ -1718,6 +1733,7 @@ static struct {
|
||||
{ hmenu_dowhatdoes, "Info on what a given key does." },
|
||||
{ option_help, "List of game options." },
|
||||
{ dispfile_optionfile, "Longer explanation of game options." },
|
||||
{ dokeylist, "Full list of keyboard commands" },
|
||||
{ hmenu_doextlist, "List of extended commands." },
|
||||
{ dispfile_license, "The NetHack license." },
|
||||
{ docontact, "Support information." },
|
||||
|
||||
Reference in New Issue
Block a user