refactor(action): 重新设计 action 实现方式
This commit is contained in:
@@ -8,50 +8,65 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef union zdwm_action_arg_t {
|
||||
bool b;
|
||||
int32_t i;
|
||||
uint32_t ui;
|
||||
const char *str;
|
||||
const void *ptr;
|
||||
} zdwm_action_arg_t;
|
||||
typedef enum zdwm_action_type_t {
|
||||
ZDWM_ACTION_SPAWN,
|
||||
ZDWM_ACTION_QUIT,
|
||||
ZDWM_ACTION_RAISE_OR_RUN,
|
||||
|
||||
typedef struct zdwm_runtime_t zdwm_runtime_t;
|
||||
ZDWM_ACTION_OUTPUT_CYCLE,
|
||||
ZDWM_ACTION_WORKSPACE_SWITCH_SAME_OUTPUT_BY_INDEX,
|
||||
ZDWM_ACTION_LAYOUT_CYCLE,
|
||||
ZDWM_ACTION_BINDING_MODE_CYCLE,
|
||||
|
||||
typedef struct zdwm_action_api_t {
|
||||
void (*spawn)(const char *command);
|
||||
void (*quit)(zdwm_runtime_t *runtime, bool restart);
|
||||
void (*raise_or_run)(
|
||||
zdwm_runtime_t *runtime,
|
||||
const char *class_name,
|
||||
const char *command
|
||||
);
|
||||
void (*toggle_fullscreen)(zdwm_runtime_t *runtime);
|
||||
void (*toggle_maximize)(zdwm_runtime_t *runtime);
|
||||
void (*toggle_floating)(zdwm_runtime_t *runtime);
|
||||
void (*toggle_sticky)(zdwm_runtime_t *runtime);
|
||||
void (*switch_workspace)(
|
||||
zdwm_runtime_t *runtime,
|
||||
zdwm_workspace_id_t workspace_id
|
||||
);
|
||||
void (*switch_workspace_in_current_output)(
|
||||
zdwm_runtime_t *runtime,
|
||||
zdwm_workspace_id_t workspace_id
|
||||
);
|
||||
void (*switch_workspace_by_index_in_current_output)(
|
||||
zdwm_runtime_t *runtime,
|
||||
uint32_t index
|
||||
);
|
||||
void (*cycle_layout)(zdwm_runtime_t *runtime, int32_t delta);
|
||||
void (*cycle_current_output)(zdwm_runtime_t *runtime, int32_t delta);
|
||||
void (*focus_window)(zdwm_runtime_t *runtime, int32_t delta);
|
||||
} zdwm_action_api_t;
|
||||
ZDWM_ACTION_WINDOW_TOGGLE_FULLSCREEN,
|
||||
ZDWM_ACTION_WINDOW_TOGGLE_MAXIMIZE,
|
||||
ZDWM_ACTION_WINDOW_TOGGLE_MINIMIZE,
|
||||
ZDWM_ACTION_WINDOW_TOGGLE_FLOATING,
|
||||
ZDWM_ACTION_WINDOW_TOGGLE_STICKY,
|
||||
|
||||
typedef void zdwm_action_fn(
|
||||
zdwm_runtime_t *runtime,
|
||||
const zdwm_action_api_t *ctx,
|
||||
const zdwm_action_arg_t *arg
|
||||
);
|
||||
ZDWM_ACTION_WINDOW_FOCUS_CYCLE,
|
||||
ZDWM_ACTION_WINDOW_KILL,
|
||||
|
||||
ZDWM_ACTION_WINDOW_SEND_TO_WORKSPACE_SAME_OUTPUT_BY_INDEX,
|
||||
ZDWM_ACTION_WINDOW_CYCLE_OUTPUT,
|
||||
} zdwm_action_type_t;
|
||||
|
||||
typedef struct zdwm_action_data_spawn_t {
|
||||
const char *command;
|
||||
} zdwm_action_data_spawn_t;
|
||||
|
||||
typedef struct zdwm_action_data_quit_t {
|
||||
bool restart;
|
||||
} zdwm_action_data_quit_t;
|
||||
|
||||
typedef struct zdwm_action_data_raise_or_run_t {
|
||||
const char *class_name;
|
||||
const char *command;
|
||||
} zdwm_action_data_raise_or_run_t;
|
||||
|
||||
typedef struct zdwm_action_data_delta_only_t {
|
||||
int32_t delta;
|
||||
} zdwm_action_data_delta_only_t;
|
||||
|
||||
typedef struct zdwm_action_data_index_only_t {
|
||||
uint32_t index;
|
||||
} zdwm_action_data_index_only_t;
|
||||
|
||||
typedef struct zdwm_action_t {
|
||||
zdwm_action_type_t type;
|
||||
union {
|
||||
zdwm_action_data_spawn_t spawn;
|
||||
zdwm_action_data_quit_t quit;
|
||||
zdwm_action_data_raise_or_run_t raise_or_run;
|
||||
zdwm_action_data_delta_only_t output_cycle;
|
||||
zdwm_action_data_index_only_t workspace_switch_same_output_by_index;
|
||||
zdwm_action_data_delta_only_t layout_cycle;
|
||||
zdwm_action_data_delta_only_t binding_mode_cycle;
|
||||
zdwm_action_data_delta_only_t window_focus_cycle;
|
||||
zdwm_action_data_index_only_t window_send_to_workspace_same_output_by_index;
|
||||
zdwm_action_data_delta_only_t window_cycle_output;
|
||||
} as;
|
||||
} zdwm_action_t;
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
|
||||
@@ -112,8 +112,7 @@ typedef struct zdwm_api_t {
|
||||
zdwm_config_builder_t *builder,
|
||||
zdwm_binding_mode_id_t bind_mode,
|
||||
const char *key_sequence,
|
||||
zdwm_action_fn action_fn,
|
||||
zdwm_action_arg_t action_arg
|
||||
zdwm_action_t action
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,80 +13,12 @@
|
||||
static constexpr char launcher[] =
|
||||
"rofi -show combi -modes combi -combi-modes window,drun,run,ssh,windowcd";
|
||||
|
||||
typedef struct raise_or_run_arg_t {
|
||||
const char *class_name;
|
||||
const char *command;
|
||||
} raise_or_run_arg_t;
|
||||
typedef struct zdwm_action_data_raise_or_run_t raise_or_run_data_t;
|
||||
|
||||
static raise_or_run_arg_t terminal = {"XTerm", "xterm"};
|
||||
static raise_or_run_arg_t editor = {"Emacs", "emacsclient -a '' -r -n"};
|
||||
static raise_or_run_arg_t browser = {"firefox", "firefox-bin"};
|
||||
static raise_or_run_arg_t chrome = {"Google-chrome", "google-chrome-stable"};
|
||||
|
||||
static void spawn(
|
||||
zdwm_runtime_t *runtime,
|
||||
const zdwm_action_api_t *api,
|
||||
const zdwm_action_arg_t *arg
|
||||
) {
|
||||
api->spawn(arg->str);
|
||||
}
|
||||
|
||||
static void quit(
|
||||
zdwm_runtime_t *runtime,
|
||||
const zdwm_action_api_t *api,
|
||||
const zdwm_action_arg_t *arg
|
||||
) {
|
||||
api->quit(runtime, arg->b);
|
||||
}
|
||||
|
||||
static void raise_or_run(
|
||||
zdwm_runtime_t *runtime,
|
||||
const zdwm_action_api_t *api,
|
||||
const zdwm_action_arg_t *arg
|
||||
) {
|
||||
auto args = (raise_or_run_arg_t *)arg->ptr;
|
||||
api->raise_or_run(runtime, args->class_name, args->command);
|
||||
}
|
||||
|
||||
static void toggle_fullscreen(
|
||||
zdwm_runtime_t *runtime,
|
||||
const zdwm_action_api_t *api,
|
||||
const zdwm_action_arg_t *arg
|
||||
) {
|
||||
api->toggle_fullscreen(runtime);
|
||||
}
|
||||
|
||||
static void toggle_maximize(
|
||||
zdwm_runtime_t *runtime,
|
||||
const zdwm_action_api_t *api,
|
||||
const zdwm_action_arg_t *arg
|
||||
) {
|
||||
api->toggle_maximize(runtime);
|
||||
}
|
||||
|
||||
static void toggle_floating(
|
||||
zdwm_runtime_t *runtime,
|
||||
const zdwm_action_api_t *api,
|
||||
const zdwm_action_arg_t *arg
|
||||
) {
|
||||
api->toggle_floating(runtime);
|
||||
}
|
||||
|
||||
static void switch_workspace(
|
||||
zdwm_runtime_t *runtime,
|
||||
const zdwm_action_api_t *api,
|
||||
const zdwm_action_arg_t *arg
|
||||
) {
|
||||
api->switch_workspace_by_index_in_current_output(runtime, arg->ui);
|
||||
}
|
||||
|
||||
static void focus_window(
|
||||
zdwm_runtime_t *runtime,
|
||||
const zdwm_action_api_t *api,
|
||||
const zdwm_action_arg_t *arg
|
||||
) {
|
||||
api->focus_window(runtime, arg->i);
|
||||
}
|
||||
static raise_or_run_data_t terminal = {"XTerm", "xterm"};
|
||||
static raise_or_run_data_t editor = {"Emacs", "emacsclient -a '' -r -n"};
|
||||
static raise_or_run_data_t browser = {"firefox", "firefox-bin"};
|
||||
static raise_or_run_data_t chrome = {"Google-chrome", "google-chrome-stable"};
|
||||
|
||||
bool config_defaults_build(
|
||||
const zdwm_api_t *api,
|
||||
@@ -156,32 +88,63 @@ bool config_defaults_build(
|
||||
|
||||
auto default_mode = api->add_mode(builder, "default");
|
||||
|
||||
#define Alt "Mod1"
|
||||
#define Super "Mod4"
|
||||
#define BIND(MODE, KEY, FN, ARG) \
|
||||
api->bind(builder, MODE, KEY, FN, (zdwm_action_arg_t)ARG)
|
||||
#define Alt_Key "Mod1"
|
||||
#define Super_key "Mod4"
|
||||
#define Alt(K) Alt_Key "+" K
|
||||
#define Super(K) Super_key "+" K
|
||||
|
||||
BIND(default_mode, Super "+r", spawn, {.str = launcher});
|
||||
BIND(default_mode, Super "+Shift+q", quit, {.b = false});
|
||||
BIND(default_mode, Super "+Control+r", quit, {.b = true});
|
||||
#define BIND(MODE, KEY, ...) \
|
||||
api->bind(builder, MODE, KEY, (zdwm_action_t)__VA_ARGS__)
|
||||
#define SPAWN(MODE, KEY, COMMAND) \
|
||||
BIND(MODE, KEY, {.type = ZDWM_ACTION_SPAWN, .as.spawn.command = (COMMAND)})
|
||||
#define QUIT(MODE, KEY, RESTART) \
|
||||
BIND(MODE, KEY, {.type = ZDWM_ACTION_QUIT, .as.quit.restart = (RESTART)})
|
||||
#define RAISE_OR_RUN(MODE, KEY, ...) \
|
||||
BIND( \
|
||||
MODE, \
|
||||
KEY, \
|
||||
{ \
|
||||
.type = ZDWM_ACTION_RAISE_OR_RUN, \
|
||||
.as.raise_or_run = (zdwm_action_data_raise_or_run_t)__VA_ARGS__, \
|
||||
} \
|
||||
)
|
||||
#define BIND_DEFAULT(KEY, ...) BIND(default_mode, KEY, __VA_ARGS__)
|
||||
|
||||
BIND(default_mode, Super "+Return", spawn, {.str = terminal.command});
|
||||
BIND(default_mode, Alt "+Control+r", raise_or_run, {.ptr = &terminal});
|
||||
BIND(default_mode, Super "+e", raise_or_run, {.ptr = &editor});
|
||||
BIND(default_mode, Super "+q", raise_or_run, {.ptr = &browser});
|
||||
BIND(default_mode, Super "+a", raise_or_run, {.ptr = &chrome});
|
||||
BIND(default_mode, Super "+f", toggle_fullscreen, {0});
|
||||
BIND(default_mode, Super "+m", toggle_maximize, {0});
|
||||
BIND(default_mode, Super "+Control+space", toggle_floating, {0});
|
||||
BIND(default_mode, Alt "+j", focus_window, {.i = 1});
|
||||
BIND(default_mode, Alt "+k", focus_window, {.i = -1});
|
||||
SPAWN(default_mode, Super("r"), launcher);
|
||||
QUIT(default_mode, Super("Shift+q"), false);
|
||||
QUIT(default_mode, Super("Control+r"), true);
|
||||
|
||||
SPAWN(default_mode, Super("Return"), terminal.command);
|
||||
RAISE_OR_RUN(default_mode, Alt("Control+r"), terminal);
|
||||
RAISE_OR_RUN(default_mode, Super("e"), editor);
|
||||
RAISE_OR_RUN(default_mode, Super("q"), browser);
|
||||
RAISE_OR_RUN(default_mode, Super("a"), chrome);
|
||||
BIND_DEFAULT(Super("f"), {.type = ZDWM_ACTION_WINDOW_TOGGLE_FLOATING});
|
||||
BIND_DEFAULT(Super("m"), {.type = ZDWM_ACTION_WINDOW_TOGGLE_MAXIMIZE});
|
||||
BIND_DEFAULT(
|
||||
Super("Control+space"),
|
||||
{.type = ZDWM_ACTION_WINDOW_TOGGLE_FLOATING}
|
||||
);
|
||||
BIND_DEFAULT(
|
||||
Alt("j"),
|
||||
{.type = ZDWM_ACTION_WINDOW_FOCUS_CYCLE, .as.window_focus_cycle.delta = 1}
|
||||
);
|
||||
BIND_DEFAULT(
|
||||
Alt("k"),
|
||||
{.type = ZDWM_ACTION_WINDOW_FOCUS_CYCLE, .as.window_focus_cycle.delta = -1}
|
||||
);
|
||||
|
||||
char buffer[64] = {0};
|
||||
for (uint32_t i = 0; i < 9; ++i) {
|
||||
snprintf(buffer, sizeof(buffer), "%s+%d", Super, i + 1);
|
||||
BIND(default_mode, buffer, switch_workspace, {.ui = i});
|
||||
snprintf(buffer, sizeof(buffer), "%s+%d", Super_key, i + 1);
|
||||
zdwm_action_t action = {
|
||||
.type = ZDWM_ACTION_WINDOW_SEND_TO_WORKSPACE_SAME_OUTPUT_BY_INDEX,
|
||||
.as.workspace_switch_same_output_by_index.index = i,
|
||||
};
|
||||
BIND_DEFAULT(buffer, action);
|
||||
}
|
||||
|
||||
#undef BIND_DEFAULT
|
||||
#undef BIND
|
||||
|
||||
api->set_default_mode(builder, default_mode);
|
||||
|
||||
@@ -167,10 +167,9 @@ static bool runtime_config_bind(
|
||||
zdwm_config_builder_t *builder,
|
||||
zdwm_binding_mode_id_t mode,
|
||||
const char *key,
|
||||
zdwm_action_fn fn,
|
||||
zdwm_action_arg_t arg
|
||||
zdwm_action_t action
|
||||
) {
|
||||
return binding_table_add_bind(builder->binding_table, mode, key, fn, arg);
|
||||
return binding_table_add_bind(builder->binding_table, mode, key, action);
|
||||
}
|
||||
|
||||
static bool runtime_config_set_default_mode(
|
||||
|
||||
@@ -3,30 +3,29 @@
|
||||
#include <stdint.h>
|
||||
#include <zdwm/action.h>
|
||||
|
||||
typedef struct runtime_t runtime_t;
|
||||
|
||||
void spawn(const char *command);
|
||||
void quit(zdwm_runtime_t *runtime, bool restart);
|
||||
void quit(runtime_t *runtime, bool restart);
|
||||
void raise_or_run(
|
||||
zdwm_runtime_t *runtime,
|
||||
runtime_t *runtime,
|
||||
const char *class_name,
|
||||
const char *command
|
||||
);
|
||||
|
||||
void toggle_fullscreen(zdwm_runtime_t *runtime);
|
||||
void toggle_maximize(zdwm_runtime_t *runtime);
|
||||
void toggle_floating(zdwm_runtime_t *runtime);
|
||||
void toggle_sticky(zdwm_runtime_t *runtime);
|
||||
void switch_workspace(
|
||||
zdwm_runtime_t *runtime,
|
||||
zdwm_workspace_id_t workspace_id
|
||||
);
|
||||
void toggle_fullscreen(runtime_t *runtime);
|
||||
void toggle_maximize(runtime_t *runtime);
|
||||
void toggle_floating(runtime_t *runtime);
|
||||
void toggle_sticky(runtime_t *runtime);
|
||||
void switch_workspace(runtime_t *runtime, zdwm_workspace_id_t workspace_id);
|
||||
void switch_workspace_in_current_output(
|
||||
zdwm_runtime_t *runtime,
|
||||
runtime_t *runtime,
|
||||
zdwm_workspace_id_t workspace_id
|
||||
);
|
||||
void switch_workspace_by_index_in_current_output(
|
||||
zdwm_runtime_t *runtime,
|
||||
runtime_t *runtime,
|
||||
uint32_t index
|
||||
);
|
||||
void cycle_layout(zdwm_runtime_t *runtime, int32_t delta);
|
||||
void cycle_current_output(zdwm_runtime_t *runtime, int32_t delta);
|
||||
void focus_window(zdwm_runtime_t *runtime, int32_t delta);
|
||||
void cycle_layout(runtime_t *runtime, int32_t delta);
|
||||
void cycle_current_output(runtime_t *runtime, int32_t delta);
|
||||
void focus_window(runtime_t *runtime, int32_t delta);
|
||||
|
||||
@@ -43,8 +43,7 @@ binding_table_add_mode(binding_table_t *table, const char *mode_name) {
|
||||
}
|
||||
|
||||
zdwm_binding_mode_id_t id = (zdwm_binding_mode_id_t)table->count;
|
||||
binding_mode_t *new_mode =
|
||||
array_push(table->modes, table->count, table->capacity);
|
||||
auto new_mode = array_push(table->modes, table->count, table->capacity);
|
||||
new_mode->name = mode_name;
|
||||
new_mode->id = id;
|
||||
|
||||
@@ -166,8 +165,7 @@ bool binding_table_add_bind(
|
||||
binding_table_t *table,
|
||||
zdwm_binding_mode_id_t mode_id,
|
||||
const char *key_sequence,
|
||||
zdwm_action_fn fn,
|
||||
zdwm_action_arg_t arg
|
||||
zdwm_action_t action
|
||||
) {
|
||||
binding_mode_t *mode = binding_table_get_mode(table, mode_id);
|
||||
if (!mode) return false;
|
||||
@@ -176,12 +174,11 @@ bool binding_table_add_bind(
|
||||
xkb_keysym_t keysym = XKB_KEY_NoSymbol;
|
||||
if (!parse_key_sequence(key_sequence, &modifiers, &keysym)) return false;
|
||||
|
||||
key_binding_t *item = array_push(mode->items, mode->count, mode->capacity);
|
||||
item->key_str = key_sequence;
|
||||
item->modifiers = modifiers;
|
||||
item->keysym = keysym;
|
||||
item->fn = fn;
|
||||
item->arg = arg;
|
||||
auto item = array_push(mode->items, mode->count, mode->capacity);
|
||||
item->key_str = key_sequence;
|
||||
item->modifiers = modifiers;
|
||||
item->keysym = keysym;
|
||||
item->action = action;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -12,8 +12,7 @@ typedef struct key_binding_t {
|
||||
const char *key_str;
|
||||
modifier_mask_t modifiers;
|
||||
keysym_t keysym;
|
||||
zdwm_action_fn *fn;
|
||||
zdwm_action_arg_t arg;
|
||||
zdwm_action_t action;
|
||||
} key_binding_t;
|
||||
|
||||
/**
|
||||
@@ -37,8 +36,7 @@ binding_table_add_mode(binding_table_t *table, const char *mode_name);
|
||||
* @param table 按键绑定表
|
||||
* @param mode_id 模式 ID
|
||||
* @param key_sequence 按键序列的字符串表达
|
||||
* @param fn 用户行为函数
|
||||
* @param arg 用户行为函数所需的参数
|
||||
* @param action 用户行为描述
|
||||
*
|
||||
* @return 添加成功返回 true 否则返回 false
|
||||
*/
|
||||
@@ -46,8 +44,7 @@ bool binding_table_add_bind(
|
||||
binding_table_t *table,
|
||||
zdwm_binding_mode_id_t mode_id,
|
||||
const char *key_sequence,
|
||||
zdwm_action_fn fn,
|
||||
zdwm_action_arg_t arg
|
||||
zdwm_action_t action
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,8 +18,17 @@
|
||||
#include "core/window.h"
|
||||
#include "core/wm_desc.h"
|
||||
|
||||
static void
|
||||
route_key_press(const policy_context_t *ctx, const key_press_event_t *e) {
|
||||
static void policy_resolve_action(
|
||||
const state_t *state,
|
||||
const zdwm_action_t *action,
|
||||
command_buffer_t *out
|
||||
) {}
|
||||
|
||||
static void route_key_press(
|
||||
const policy_context_t *ctx,
|
||||
const key_press_event_t *e,
|
||||
command_buffer_t *out
|
||||
) {
|
||||
auto binding_table = ctx->bind_table;
|
||||
|
||||
size_t count = 0;
|
||||
@@ -29,7 +38,8 @@ route_key_press(const policy_context_t *ctx, const key_press_event_t *e) {
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
auto binding = &bindings[i];
|
||||
if (binding->modifiers == e->modifiers && binding->keysym == e->keysym) {
|
||||
binding->fn(ctx->runtime, &ctx->action_api, &binding->arg);
|
||||
policy_resolve_action(ctx->state, &binding->action, out);
|
||||
/* binding->fn(ctx->runtime, &ctx->action_api, &binding->arg); */
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -264,7 +274,7 @@ void policy_route_event(
|
||||
auto state = ctx->state;
|
||||
switch (event->type) {
|
||||
case ZDWM_EVENT_KEY_PRESS:
|
||||
route_key_press(ctx, &event->as.key_press);
|
||||
route_key_press(ctx, &event->as.key_press, out);
|
||||
break;
|
||||
case ZDWM_EVENT_POINTER_ENTER:
|
||||
route_pointer_enter(state, event->as.pointer_enter.window, out);
|
||||
|
||||
@@ -17,8 +17,6 @@ typedef struct policy_context_t {
|
||||
const rules_t *rules;
|
||||
const border_config_t *border;
|
||||
const layout_registry_t *layouts;
|
||||
const zdwm_action_api_t action_api;
|
||||
zdwm_runtime_t *runtime;
|
||||
} policy_context_t;
|
||||
|
||||
/**
|
||||
|
||||
@@ -266,25 +266,6 @@ static policy_context_t policy_context_init(runtime_t *runtime) {
|
||||
.rules = &(runtime)->rules,
|
||||
.border = &(runtime)->border,
|
||||
.layouts = &(runtime)->layouts,
|
||||
.runtime = (runtime),
|
||||
.action_api = {
|
||||
.spawn = spawn,
|
||||
.quit = quit,
|
||||
.raise_or_run = raise_or_run,
|
||||
.toggle_fullscreen = toggle_fullscreen,
|
||||
.toggle_maximize = toggle_maximize,
|
||||
.toggle_floating = toggle_floating,
|
||||
.toggle_sticky = toggle_sticky,
|
||||
|
||||
.switch_workspace = switch_workspace,
|
||||
.switch_workspace_in_current_output = switch_workspace_in_current_output,
|
||||
.switch_workspace_by_index_in_current_output =
|
||||
switch_workspace_by_index_in_current_output,
|
||||
|
||||
.cycle_layout = cycle_layout,
|
||||
.cycle_current_output = cycle_current_output,
|
||||
.focus_window = focus_window,
|
||||
},
|
||||
};
|
||||
return ctx;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ typedef struct runtime_init_desc_t {
|
||||
listeners_t listeners;
|
||||
} runtime_init_desc_t;
|
||||
|
||||
typedef struct zdwm_runtime_t {
|
||||
typedef struct runtime_t {
|
||||
bool running;
|
||||
bool will_restart;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user