Allow binding mouse buttons

Instead of hardcoding mouse button actions, allow the user to
bind mouse buttons to extended commands.  For example the new
defaults are:

BIND=mouse1:therecmdmenu
BIND=mouse2:clicklook

Currently a bit rudimentary; the defaults should be OK, but
documentation is bit lacking, and in-game binding and option
saving are missing.

Allowed commands to bind are "nothing", "therecmdmenu", "clicklook",
and "mouseaction". Clicklook replaces the "clicklook" boolean option,
and mouseaction does what mouse 1 button used to do - a context sensitive
action.
This commit is contained in:
Pasi Kallinen
2022-08-23 23:06:26 +03:00
parent 3a255e86c4
commit c42e73fd9c
11 changed files with 135 additions and 78 deletions

155
src/cmd.c
View File

@@ -105,6 +105,7 @@ static boolean can_do_extcmd(const struct ext_func_tab *);
static int dotravel(void);
static int dotravel_target(void);
static int doclicklook(void);
static int domouseaction(void);
static int doterrain(void);
static int wiz_wish(void);
static int wiz_identify(void);
@@ -2692,7 +2693,7 @@ struct ext_func_tab extcmdlist[] = {
doterrain, IFBURIED | AUTOCOMPLETE, NULL },
{ '\0', "therecmdmenu",
"menu of commands you can do from here to adjacent spot",
dotherecmdmenu, AUTOCOMPLETE | GENERALCMD, NULL },
dotherecmdmenu, AUTOCOMPLETE | GENERALCMD | MOUSECMD, NULL },
{ 't', "throw", "throw something",
dothrow, 0, NULL },
{ '\0', "timeout", "look at timeout queue and hero's timed intrinsics",
@@ -2831,7 +2832,8 @@ struct ext_func_tab extcmdlist[] = {
do_run_southwest, MOVEMENTCMD | CMD_M_PREFIX, NULL },
/* internal commands: only used by game core, not available for user */
{ '\0', "clicklook", NULL, doclicklook, INTERNALCMD, NULL },
{ '\0', "clicklook", NULL, doclicklook, INTERNALCMD | MOUSECMD, NULL },
{ '\0', "mouseaction", NULL, domouseaction, INTERNALCMD | MOUSECMD, NULL },
{ '\0', "altdip", NULL, dip_into, INTERNALCMD, NULL },
{ '\0', "altadjust", NULL, adjust_split, INTERNALCMD, NULL },
{ '\0', "altunwield", NULL, remarm_swapwep, INTERNALCMD, NULL },
@@ -3169,6 +3171,43 @@ key2extcmddesc(uchar key)
return (char *) 0;
}
boolean
bind_mousebtn(int btn, const char *command)
{
struct ext_func_tab *extcmd;
if (btn < 1 || btn > NUM_MOUSE_BUTTONS) {
config_error_add("Wrong mouse button, valid are 1-%i", NUM_MOUSE_BUTTONS);
return FALSE;
}
btn--;
/* special case: "nothing" is reserved for unbinding */
if (!strcmpi(command, "nothing")) {
g.Cmd.mousebtn[btn] = (struct ext_func_tab *) 0;
return TRUE;
}
for (extcmd = extcmdlist; extcmd->ef_txt; extcmd++) {
if (strcmpi(command, extcmd->ef_txt))
continue;
if (!(extcmd->flags & MOUSECMD))
continue;
g.Cmd.mousebtn[btn] = extcmd;
#if 0 /* silently accept key binding for unavailable command (!SHELL,&c) */
if ((extcmd->flags & CMD_NOT_AVAILABLE) != 0) {
char buf[BUFSZ];
Sprintf(buf, cmdnotavail, extcmd->ef_txt);
config_error_add("%s", buf);
}
#endif
return TRUE;
}
return FALSE;
}
boolean
bind_key(uchar key, const char *command)
{
@@ -3228,6 +3267,9 @@ commands_init(void)
if (extcmd->key)
g.Cmd.commands[extcmd->key] = extcmd;
(void) bind_mousebtn(1, "therecmdmenu");
(void) bind_mousebtn(2, "clicklook");
/* number_pad */
(void) bind_key(C('l'), "redraw");
(void) bind_key('h', "help");
@@ -5203,8 +5245,20 @@ dotherecmdmenu(void)
{
char ch;
int dir, click;
coordxy x = g.clicklook_cc.x;
coordxy y = g.clicklook_cc.y;
iflags.getdir_click = CLICK_1 | CLICK_2; /* allow 'far' click */
if (isok(x, y)) {
if (x == u.ux && y == u.uy)
ch = here_cmd_menu();
else
ch = there_cmd_menu(x, y, iflags.getdir_click);
g.clicklook_cc.x = g.clicklook_cc.y = -1;
return (ch && ch != '\033') ? ECMD_TIME : ECMD_OK;
}
dir = getdir((const char *) 0);
click = iflags.getdir_click;
iflags.getdir_click = 0;
@@ -5731,30 +5785,25 @@ here_cmd_menu(void)
return '\0';
}
/*
* convert a MAP window position into a movement key usable with movecmd()
*/
const char *
void
click_to_cmd(coordxy x, coordxy y, int mod)
{
static char cmd[4];
g.clicklook_cc.x = x;
g.clicklook_cc.y = y;
if (g.Cmd.mousebtn[mod-1])
cmdq_add_ec(CQ_CANNED, g.Cmd.mousebtn[mod-1]->ef_funct);
}
static int
domouseaction(void)
{
coordxy x, y;
struct obj *o;
int dir;
cmd[0] = cmd[1] = '\0';
if (!iflags.herecmd_menu && iflags.clicklook && mod == CLICK_2) {
g.clicklook_cc.x = x;
g.clicklook_cc.y = y;
cmdq_add_ec(CQ_CANNED, doclicklook);
return cmd;
}
if (iflags.herecmd_menu && isok(x, y)) {
(void) there_cmd_menu(x, y, mod);
return cmd;
}
x -= u.ux;
y -= u.uy;
x = g.clicklook_cc.x - u.ux;
y = g.clicklook_cc.y - u.uy;
if (flags.travelcmd) {
if (abs(x) <= 1 && abs(y) <= 1) {
@@ -5763,30 +5812,30 @@ click_to_cmd(coordxy x, coordxy y, int mod)
iflags.travelcc.x = u.tx = u.ux + x;
iflags.travelcc.y = u.ty = u.uy + y;
cmdq_add_ec(CQ_CANNED, dotravel_target);
return cmd;
return ECMD_OK;
}
if (x == 0 && y == 0) {
/* here */
if (IS_FOUNTAIN(levl[u.ux][u.uy].typ)
|| IS_SINK(levl[u.ux][u.uy].typ)) {
cmd[0] = cmd_from_func(mod == CLICK_1 ? dodrink : dodip);
return cmd;
cmdq_add_ec(CQ_CANNED, dodrink);
return ECMD_OK;
} else if (IS_THRONE(levl[u.ux][u.uy].typ)) {
cmd[0] = cmd_from_func(dosit);
return cmd;
cmdq_add_ec(CQ_CANNED, dosit);
return ECMD_OK;
} else if (On_stairs_up(u.ux, u.uy)) {
cmd[0] = cmd_from_func(doup);
return cmd;
cmdq_add_ec(CQ_CANNED, doup);
return ECMD_OK;
} else if (On_stairs_dn(u.ux, u.uy)) {
cmd[0] = cmd_from_func(dodown);
return cmd;
cmdq_add_ec(CQ_CANNED, dodown);
return ECMD_OK;
} else if ((o = vobj_at(u.ux, u.uy)) != 0) {
cmd[0] = cmd_from_func(Is_container(o) ? doloot : dopickup);
return cmd;
cmdq_add_ec(CQ_CANNED, Is_container(o) ? doloot : dopickup);
return ECMD_OK;
} else {
cmd[0] = cmd_from_func(donull); /* just rest */
return cmd;
cmdq_add_ec(CQ_CANNED, donull); /* just rest */
return ECMD_OK;
}
}
@@ -5795,25 +5844,23 @@ click_to_cmd(coordxy x, coordxy y, int mod)
dir = xytod(x, y);
if (!m_at(u.ux + x, u.uy + y)
&& !test_move(u.ux, u.uy, x, y, TEST_MOVE)) {
cmd[1] = cmd_from_func(move_funcs[dir][MV_WALK]);
cmd[2] = '\0';
if (IS_DOOR(levl[u.ux + x][u.uy + y].typ)) {
/* slight assistance to player: choose kick/open for them */
if (levl[u.ux + x][u.uy + y].doormask & D_LOCKED) {
cmd[0] = cmd_from_func(dokick);
return cmd;
cmdq_add_ec(CQ_CANNED, dokick);
return ECMD_OK;
}
if (levl[u.ux + x][u.uy + y].doormask & D_CLOSED) {
cmd[0] = cmd_from_func(doopen);
return cmd;
cmdq_add_ec(CQ_CANNED, doopen);
return ECMD_OK;
}
}
if (levl[u.ux + x][u.uy + y].typ <= SCORR) {
cmd[0] = cmd_from_func(dosearch);
cmd[1] = 0;
return cmd;
cmdq_add_ec(CQ_CANNED, dosearch);
return ECMD_OK;
}
cmdq_add_ec(CQ_CANNED, move_funcs[dir][MV_WALK]);
return ECMD_OK;
}
} else {
/* convert without using floating point, allowing sloppy clicking */
@@ -5830,21 +5877,15 @@ click_to_cmd(coordxy x, coordxy y, int mod)
if (x == 0 && y == 0) {
/* map click on player to "rest" command */
cmd[0] = cmd_from_func(donull);
return cmd;
cmdq_add_ec(CQ_CANNED, donull);
return ECMD_OK;
}
dir = xytod(x, y);
}
/* move, attack, etc. */
cmd[1] = 0;
if (mod == CLICK_1) {
cmd[0] = cmd_from_func(move_funcs[dir][MV_WALK]);
} else {
cmd[0] = cmd_from_func(move_funcs[dir][MV_RUN]);
}
return cmd;
cmdq_add_ec(CQ_CANNED, move_funcs[dir][MV_WALK]);
return ECMD_OK;
}
/* gather typed digits into a number in *count; return the next non-digit */
@@ -6077,8 +6118,8 @@ readchar_core(coordxy *x, coordxy *y, int *mod)
#endif /*ALTMETA*/
} else if (sym == 0) {
/* click event */
readchar_queue = click_to_cmd(*x, *y, *mod);
sym = *readchar_queue++;
g.clicklook_cc.x = g.clicklook_cc.y = -1;
click_to_cmd(*x, *y, *mod);
}
g.program_state.getting_a_command = 0; /* next readchar() will be for an
* ordinary char unless parse()
@@ -6183,7 +6224,7 @@ static int
doclicklook(void)
{
if (!isok(g.clicklook_cc.x, g.clicklook_cc.y))
return 0;
return ECMD_OK;
g.context.move = FALSE;
(void) do_look(2, &g.clicklook_cc);