diff --git a/docs/bindings/keys.md b/docs/bindings/keys.md index 6c8a8c58..d1eaa67e 100644 --- a/docs/bindings/keys.md +++ b/docs/bindings/keys.md @@ -89,6 +89,7 @@ bindr=Super,Super_L,spawn,rofi -show run | Command | Param | Description | | :--- | :--- | :--- | | `killclient` | `force` | Close the focused window. If `force` is specified, sends `SIGKILL`. | +| `smartkillclient` | - | Remove the focused window from the current tag(s) if it is on multiple tags; otherwise close it. | | `togglefloating` | - | Toggle floating state. | | `toggle_all_floating` | - | Toggle all visible clients floating state. | | `togglefullscreen` | - | Toggle fullscreen. | diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 61af421b..e31a040d 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -1061,6 +1061,8 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value, } else if (strcmp(func_name, "killclient") == 0) { func = killclient; (*arg).i = parse_force(arg_value); + } else if (strcmp(func_name, "smartkillclient") == 0) { + func = smartkillclient; } else if (strcmp(func_name, "centerwin") == 0) { func = centerwin; } else if (strcmp(func_name, "focuslast") == 0) { diff --git a/src/dispatch/bind_declare.h b/src/dispatch/bind_declare.h index 6960b001..1de2d78b 100644 --- a/src/dispatch/bind_declare.h +++ b/src/dispatch/bind_declare.h @@ -34,6 +34,7 @@ int32_t moveresize(const Arg *arg); int32_t exchange_client(const Arg *arg); int32_t exchange_stack_client(const Arg *arg); int32_t killclient(const Arg *arg); +int32_t smartkillclient(const Arg *arg); int32_t toggleglobal(const Arg *arg); int32_t incnmaster(const Arg *arg); int32_t focusmon(const Arg *arg); diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index e7cfd020..34731b9f 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -360,6 +360,28 @@ int32_t killclient(const Arg *arg) { return 0; } +int32_t smartkillclient(const Arg *arg) { + Client *c = arg->tc ? arg->tc : (selmon ? selmon->sel : NULL); + if (!c) + return 0; + + uint32_t newtags = c->tags & ~(c->mon->tagset[c->mon->seltags] & TAGMASK); + // Kill the client if it's single-tag, or if removing the tag(s) would leave + // it on none + if (__builtin_popcount(c->tags & TAGMASK) <= 1 || !newtags) { + pending_kill_client(c); + return 0; + } + + // Remove the client from the currently viewed tag(s) on its own monitor + // (the one that changed) while keeping focus on the user's active monitor. + c->tags = newtags; + focusclient(focustop(selmon), 1); + arrange(c->mon, false, false); + printstatus(IPC_WATCH_ARRANGGE); + return 0; +} + int32_t moveresize(const Arg *arg) { const char *cursors[] = {"nw-resize", "ne-resize", "sw-resize", "se-resize"};