diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 641185da9..c26796aad 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -3483,8 +3483,8 @@ Set exceptions to the option. See the \(lqConfiguring Autopickup Exceptions\(rq section. .lp BINDINGS -Change the key bindings of some special keys, menu accelerators, or -extended commands. +Change the key bindings of some special keys, menu accelerators, +extended commands, or mouse buttons. You can specify multiple bindings. Format is key followed by the command, separated by a colon. See the \(lqChanging Key Bindings\(rq section for more information. @@ -3753,9 +3753,6 @@ for more details. .lp checkpoint Save game state after each level change, for possible recovery after program crash (default on). Persistent. -.lp clicklook -Allows looking at things on the screen by navigating the mouse -over them and clicking the right mouse button (default off). .lp cmdassist Have the game provide some additional command assistance for new players if it detects some anticipated mistakes (default on). @@ -4873,7 +4870,8 @@ menu accelerator keys, and extended commands, by using BIND stanzas in the configuration file. Format is key, followed by the command to bind to, separated by a colon. The key can be a single character (\(lqx\(rq), a control key (\(lq\(haX\(rq, -\(lqC-x\(rq), a meta key (\(lqM-x\(rq), or a three-digit decimal ASCII code. +\(lqC-x\(rq), a meta key (\(lqM-x\(rq), a mouse button, +or a three-digit decimal ASCII code. .pg For example: .sd @@ -4893,6 +4891,9 @@ The menu control or accelerator keys can also be rebound via OPTIONS lines in the configuration file. You cannot bind object symbols or selection letters into menu accelerators. Some interfaces only support some of the menu accelerators. +.lp "Mouse buttons" +You can bind \(lqmouse1\(rq or \(lqmouse2\(rq to \(lqnothing\(rq, +\(lqtherecmdmenu\(rq, \(lqclicklook\(rq, or \(lqmouseaction\(rq. .lp "Special command keys" Below are the special commands you can rebind. Some of them can be bound to diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 60cac1496..086b24712 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -3779,9 +3779,9 @@ Set exceptions to the {{\it pickup\_types\/}} option. See the ``Configuring Autopickup Exceptions'' section. %.lp \item[\bb{BINDINGS}] -Change the key bindings of some special keys, menu accelerators, or -extended commands. You can specify multiple bindings. Format is key -followed by the command, separated by a colon. +Change the key bindings of some special keys, menu accelerators, +extended commands, or mouse buttons. You can specify multiple bindings. +Format is key followed by the command, separated by a colon. See the ``Changing Key Bindings`` section for more information. %.lp "" @@ -4087,10 +4087,6 @@ Synonym for ``{\tt role}'' to pick the type of your character Save game state after each level change, for possible recovery after program crash (default on). Persistent. %.lp -\item[\ib{clicklook}] -Allows looking at things on the screen by navigating the mouse -over them and clicking the right mouse button (default off). -%.lp \item[\ib{cmdassist}] Have the game provide some additional command assistance for new players if it detects some anticipated mistakes (default on). @@ -5379,11 +5375,11 @@ autopickup. %.pg It is possible to change the default key bindings of some special commands, -menu accelerator keys, and extended commands, by using BIND stanzas in the +menu accelerator keys, extended commands, by using BIND stanzas in the configuration file. Format is key, followed by the command to bind to, separated by a colon. The key can be a single character (``{\tt x}''), a control key (``{\tt \^{}X}'', ``{\tt C-x}''), a meta key (``{\tt M-x}''), -or a three-digit decimal ASCII code. +a mouse button, or a three-digit decimal ASCII code. %.pg For example: @@ -5408,6 +5404,11 @@ in the configuration file. You cannot bind object symbols or selection letters into menu accelerators. Some interfaces only support some of the menu accelerators. +%.lp "Mouse buttons" +\item[\tb{Mouse buttons}] +You can bind ``mouse1'' or ``mouse2'' to ``{\tt nothing}'', +``{\tt therecmdmenu}'', ``{\tt clicklook}'', or ``{\tt mouseaction}''. + %.lp "Special command keys" \item[\tb{Special command keys}] Below are the special commands you can rebind. Some of them can be bound to diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 5683261ad..7edb08bd0 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1803,6 +1803,7 @@ eliminate scimitar skill and have scimitars use saber skill simplified configuration options menu rudimentary key rebinding in game options experimental #saveoptions command to allow saving configuration settings +mouse buttons can be bound to extended commands Platform- and/or Interface-Specific New Features diff --git a/include/decl.h b/include/decl.h index b1d63c8dd..d8abd1103 100644 --- a/include/decl.h +++ b/include/decl.h @@ -506,6 +506,7 @@ struct cmd { const char *dirchars; /* current movement/direction characters */ const char *alphadirchars; /* same as dirchars if !numpad */ const struct ext_func_tab *commands[256]; /* indexed by input character */ + const struct ext_func_tab *mousebtn[NUM_MOUSE_BUTTONS]; char spkeys[NUM_NHKF]; char extcmd_char; /* key that starts an extended command ('#') */ }; diff --git a/include/extern.h b/include/extern.h index 9898f9d51..c9ae83c0e 100644 --- a/include/extern.h +++ b/include/extern.h @@ -279,6 +279,7 @@ extern void rhack(char *); extern int doextlist(void); extern int extcmd_via_menu(void); extern int enter_explore_mode(void); +extern boolean bind_mousebtn(int, const char *); extern boolean bind_key(uchar, const char *); extern void dokeylist(void); extern coordxy xytod(coordxy, coordxy); @@ -291,7 +292,7 @@ extern const char *directionname(int); extern int isok(coordxy, coordxy); extern int get_adjacent_loc(const char *, const char *, coordxy, coordxy, coord *); -extern const char *click_to_cmd(coordxy, coordxy, int); +extern void click_to_cmd(coordxy, coordxy, int); extern char get_count(const char *, char, long, cmdcount_nht *, unsigned); #ifdef HANGUPHANDLING extern void hangup(int); diff --git a/include/flag.h b/include/flag.h index 551cd004e..f4006d118 100644 --- a/include/flag.h +++ b/include/flag.h @@ -318,7 +318,6 @@ struct instance_flags { #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 */ boolean fireassist; /* autowield launcher when using fire-command */ boolean time_botl; /* context.botl for 'time' (moves) only */ diff --git a/include/func_tab.h b/include/func_tab.h index 08016f518..764b9ff33 100644 --- a/include/func_tab.h +++ b/include/func_tab.h @@ -19,6 +19,7 @@ #define CMD_MOVE_PREFIXES (CMD_M_PREFIX | CMD_gGF_PREFIX) #define PREFIXCMD 0x0200 /* prefix command, requires another one after it */ #define MOVEMENTCMD 0x0400 /* used to move hero/cursor */ +#define MOUSECMD 0x0800 /* cmd allowed to be bound to mouse button */ /* flags for extcmds_match() */ #define ECM_NOFLAGS 0 diff --git a/include/optlist.h b/include/optlist.h index f46b3fcc2..dcad974e4 100644 --- a/include/optlist.h +++ b/include/optlist.h @@ -162,8 +162,6 @@ static int optfn_##a(int, int, boolean, char *, char *); NHOPTB(checkpoint, Advanced, 0, opt_out, set_in_config, Off, No, No, No, NoAlias, (boolean *) 0) #endif - NHOPTB(clicklook, Advanced, 0, opt_in, set_in_game, - Off, Yes, No, No, NoAlias, &iflags.clicklook) NHOPTB(cmdassist, Behavior, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias, &iflags.cmdassist) NHOPTB(color, Map, 0, opt_in, set_in_game, diff --git a/include/wintype.h b/include/wintype.h index dd69b9446..420917dcf 100644 --- a/include/wintype.h +++ b/include/wintype.h @@ -135,6 +135,7 @@ typedef struct gi { /* nh_poskey() modifier types */ #define CLICK_1 1 #define CLICK_2 2 +#define NUM_MOUSE_BUTTONS 2 /* invalid winid */ #define WIN_ERR ((winid) -1) diff --git a/src/cmd.c b/src/cmd.c index 3eb7d9c37..acb2bf311 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -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); diff --git a/src/options.c b/src/options.c index 9ea9130e4..27772d8c9 100644 --- a/src/options.c +++ b/src/options.c @@ -6632,6 +6632,9 @@ parsebindings(char *bindings) uchar key; int i; boolean ret = TRUE; /* assume success */ + static const char *const mousebtn_names[NUM_MOUSE_BUTTONS] = { + "mouse1", "mouse2" + }; /* look for first comma, then decide whether it is the key being bound or a list element separator; if it's a key, find separator beyond it */ @@ -6660,6 +6663,17 @@ parsebindings(char *bindings) return FALSE; /* it's not a binding */ *bind++ = 0; + bind = trimspaces(bind); + + for (i = 0; i < SIZE(mousebtn_names); i++) + if (!strcmp(bindings, mousebtn_names[i])) { + if (!bind_mousebtn(i + 1, bind)) { + config_error_add("Error binding mouse button %i", i + 1); + } else { + return ret; + } + } + /* read the key to be bound */ key = txt2key(bindings); if (!key) { @@ -6667,8 +6681,6 @@ parsebindings(char *bindings) return FALSE; } - bind = trimspaces(bind); - /* is it a special key? */ if (bind_specialkey(key, bind)) return ret;