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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ('#') */
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
155
src/cmd.c
155
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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user