From 0caf84ae8ea372f4e148b22d6ed9a1570684e3d8 Mon Sep 17 00:00:00 2001 From: Zuki Air Date: Fri, 1 Aug 2025 22:41:48 +0100 Subject: [PATCH] riverctl patch: add keybind modes --- patches/riverctl/README.md | 17 ++- patches/riverctl/riverctl.patch | 238 ++++++++++++++++++++++++-------- 2 files changed, 193 insertions(+), 62 deletions(-) diff --git a/patches/riverctl/README.md b/patches/riverctl/README.md index 44eb094..0b664cf 100644 --- a/patches/riverctl/README.md +++ b/patches/riverctl/README.md @@ -7,13 +7,26 @@ Most of this patch is stored in river-control.h, It contains a list of functions This patches main intended use case is to have a startup script that calls dwlctl a bunch to add all the binds/rules you want, without the need of restarting dwl if you make any changes to the list of binds/rules. +This patch also adds keybind modes which allow switching between a diffrent sets of keybinds on the fly. +Also you can set a keybind mode as oneshot(meaning as soon as a keybind is activated the mode is switch) by using... +`dwlctl oneshot-mode _layout_you_want_to_make_oneshot_here_ _layout_you_want_to_switch_to_after_keybind_pressed_` +Just make sure to set a mode as oneshot after creating a bind under it otherwise it won't work. + + ### dwlctl syntax example ``` dwlctl clear-binds -dwlctl bind super,shift Return spawn kitty tmux +dwlctl bind normal super,shift Return spawn kitty tmux -dwlctl bind supershift q killclient +dwlctl bind normal supershift q killclient + +dwlctl bind normal super l enter-mode layoutpick + +dwlctl bind layoutpick none t layout 0 +dwlctl bind layoutpick none f layout 1 +dwlctl bind layoutpick none m layout 2 +dwlctl oneshot-mode layoutpick normal dwlctl clear-rules diff --git a/patches/riverctl/riverctl.patch b/patches/riverctl/riverctl.patch index 9ecd78d..52f2e96 100644 --- a/patches/riverctl/riverctl.patch +++ b/patches/riverctl/riverctl.patch @@ -1,4 +1,4 @@ -From de574aa854d6da09a886f946b557d349c04e749d Mon Sep 17 00:00:00 2001 +From da6c19ca2bf476426ee598072a12b6d6e1679c54 Mon Sep 17 00:00:00 2001 From: Zuki Air Date: Sun, 27 Jul 2025 11:30:32 +0100 Subject: [PATCH] river-ctl patch @@ -12,11 +12,11 @@ fix bug .gitignore | 1 + Makefile | 22 +- config.def.h | 36 +- - dwl.c | 20 +- + dwl.c | 47 +- dwlctl.c | 132 +++++ protocols/river-control-unstable-v1.xml | 85 +++ - river-control.h | 669 ++++++++++++++++++++++++ - 7 files changed, 949 insertions(+), 16 deletions(-) + river-control.h | 753 ++++++++++++++++++++++++ + 7 files changed, 1054 insertions(+), 22 deletions(-) create mode 100644 dwlctl.c create mode 100644 protocols/river-control-unstable-v1.xml create mode 100644 river-control.h @@ -148,7 +148,7 @@ index 95c2afa..72afbd6 100644 CHVT(1), CHVT(2), CHVT(3), CHVT(4), CHVT(5), CHVT(6), CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12), diff --git a/dwl.c b/dwl.c -index 12f441e..cf89d9a 100644 +index 12f441e..2cbb1b0 100644 --- a/dwl.c +++ b/dwl.c @@ -145,7 +145,7 @@ typedef struct { @@ -187,20 +187,54 @@ index 12f441e..cf89d9a 100644 if ((!r->title || strstr(title, r->title)) && (!r->id || strstr(appid, r->id))) { c->isfloating = r->isfloating; -@@ -1613,8 +1618,10 @@ keybinding(uint32_t mods, xkb_keysym_t sym) +@@ -1605,6 +1610,16 @@ inputdevice(struct wl_listener *listener, void *data) + wlr_seat_set_capabilities(seat, caps); + } + ++inline bool ++keybinding_key(uint32_t mods, xkb_keysym_t sym,const Key *k) { ++ if (CLEANMASK(mods) == CLEANMASK(k->mod) ++ && sym == k->keysym && k->func) { ++ k->func(&k->arg); ++ return true; ++ } ++ return false; ++} ++ + int + keybinding(uint32_t mods, xkb_keysym_t sym) + { +@@ -1613,14 +1628,21 @@ keybinding(uint32_t mods, xkb_keysym_t sym) * processing keys, rather than passing them on to the client for its own * processing. */ - const Key *k; - for (k = keys; k < END(keys); k++) { +- if (CLEANMASK(mods) == CLEANMASK(k->mod) +- && sym == k->keysym && k->func) { +- k->func(&k->arg); +- return 1; +- } +- } + const Key_linked *kl; + const Key *k; -+ wl_list_for_each(kl,&keys_list,link) { ++ ++ wl_list_for_each(kl,&active_mode->linked_keys,link) { + k = kl->key; - if (CLEANMASK(mods) == CLEANMASK(k->mod) - && sym == k->keysym && k->func) { - k->func(&k->arg); -@@ -2645,6 +2652,9 @@ setup(void) ++ if (keybinding_key(mods,sym,k) == true) { ++ if (active_mode->oneshot_mode != NULL) { ++ active_mode = active_mode->oneshot_mode; ++ } ++ return 1; ++ } ++ } ++ for (k = keys_always; k < END(keys_always); k++) { ++ if (keybinding_key(mods,sym,k) == true) {return 1;} ++ } + return 0; + } + +@@ -2645,6 +2667,9 @@ setup(void) wl_signal_add(&output_mgr->events.apply, &output_mgr_apply); wl_signal_add(&output_mgr->events.test, &output_mgr_test); @@ -210,7 +244,7 @@ index 12f441e..cf89d9a 100644 /* Make sure XWayland clients don't connect to the parent X server, * e.g when running in the x11 backend or the wayland backend and the * compositor has Xwayland support */ -@@ -3187,6 +3197,8 @@ main(int argc, char *argv[]) +@@ -3187,6 +3212,8 @@ main(int argc, char *argv[]) { char *startup_cmd = NULL; int c; @@ -450,23 +484,25 @@ index 0000000..aa5fc4d + diff --git a/river-control.h b/river-control.h new file mode 100644 -index 0000000..b8c7a8e +index 0000000..ad76520 --- /dev/null +++ b/river-control.h -@@ -0,0 +1,669 @@ +@@ -0,0 +1,753 @@ +#include "river-control-unstable-v1-private-protocol.c" +#include "river-control-unstable-v1-protocol.h" +#ifdef KEYS_USED -+void default_binds(void); ++void default_binds(struct wl_list*); +#endif ++void enter_mode(const Arg*); ++void create_mode_user(const Arg*); ++void oneshot_mode(const Arg*); +void clear_rules(const Arg*); +void clear_binds(const Arg*); +void setborderpx(const Arg*); -+// TODO add dynamic rules then its fully complete! +struct wl_list arg_str_store; +struct wl_list rule_str_store; +struct wl_list rules_list; -+struct wl_list keys_list; ++struct wl_list modes_list; +typedef struct { + Key *key; + struct wl_list link; @@ -480,6 +516,15 @@ index 0000000..b8c7a8e + bool no_remove; + struct Str_link *str_link; +} Rule_linked; ++typedef struct Mode Mode; ++struct Mode { ++ struct wl_list link; ++ struct wl_list linked_keys; ++ struct Mode *oneshot_mode; ++ char* name; ++}; ++Mode *active_mode; ++Mode *normal_mode; +struct Keysym_str_pair { + xkb_keysym_t keysym; + const char * keysym_str; @@ -508,10 +553,9 @@ index 0000000..b8c7a8e +struct Func_str_type_pair Func_str_type_pair_list[] = { + { clear_binds, FUNC_STR_ARG_TYPE_NONE, "clear-binds" }, + { clear_rules, FUNC_STR_ARG_TYPE_NONE, "clear-rules" }, -+ STR(togglefakefullscreen,FUNC_STR_ARG_TYPE_NONE), -+ STR(togglegaps,FUNC_STR_ARG_TYPE_NONE), -+ STR(pushup,FUNC_STR_ARG_TYPE_NONE), -+ STR(pushdown,FUNC_STR_ARG_TYPE_NONE), ++ { enter_mode, FUNC_STR_ARG_TYPE_STRING_ARRAY, "enter-mode"}, ++ { oneshot_mode, FUNC_STR_ARG_TYPE_STRING_ARRAY, "oneshot-mode"}, ++ { create_mode_user, FUNC_STR_ARG_TYPE_STRING_ARRAY, "create-mode"}, + STR(setborderpx,FUNC_STR_ARG_TYPE_UINT), + STR(setlayout,FUNC_STR_ARG_TYPE_LAYOUT), + STR(spawn,FUNC_STR_ARG_TYPE_STRING_ARRAY), @@ -593,6 +637,7 @@ index 0000000..b8c7a8e + Func_str_arg_type key_arg_type; + Rule_match_type_next rule_match_type; + Rule_type rule_type; ++ Mode *key_mode; + bool rule_valid; + bool error; + const char* error_msg; @@ -656,7 +701,6 @@ index 0000000..b8c7a8e + return str_link; +} +struct Str_link* add_arg_str_store(const char* string) { -+// char** add_arg_str_store(const char* string) { + struct Str_link *str_link = calloc(1,sizeof(struct Str_link)); + int i; + int string_len = strlen(string) + 1; @@ -710,31 +754,77 @@ index 0000000..b8c7a8e + free(rl); + } + } ++ clear_str_store(&rule_str_store); +} -+void setup_binds(void) { -+ Key *k; -+ Key_linked *kl; -+ if (keys_list.next == NULL) { -+ wl_list_init(&keys_list); -+ } -+ for (k = keys_always; k < END(keys_always); k++) { -+ kl = calloc(1,sizeof(Key_linked)); -+ if (kl != NULL) { -+ kl->key = k; -+ kl->no_free_key = true; -+ kl->no_remove = true; -+ wl_list_insert(&keys_list,&kl->link); ++Mode* create_mode(const char *name) { ++ Mode *mode = calloc(1,sizeof(Mode)); ++ if (mode == NULL) {return NULL;} ++ wl_list_init(&mode->linked_keys); ++ wl_list_insert(&modes_list, &mode->link); ++ if (name != NULL) { ++ int string_len = strlen(name) + 1; ++ mode->name = malloc(sizeof(char) * string_len); ++ if (mode->name == NULL) { ++ free(mode); ++ return NULL; + } -+ } -+#ifdef KEYS_USED -+ default_binds(); -+#endif ++ memcpy(mode->name,name,string_len); ++ } ++ return mode; ++} ++Mode* get_mode(char* mode_name) { ++ Mode *mode; ++ wl_list_for_each(mode,&modes_list,link) { ++ if (strcmp(mode_name,mode->name) == 0) { ++ return mode; ++ } ++ } ++ return NULL; ++} ++void oneshot_mode(const Arg *arg) { ++ char * oneshot_mode_name = *(char **)arg->v; ++ char * return_mode_name = *((char **)arg->v+1); ++ if (oneshot_mode_name != NULL && return_mode_name != NULL) { ++ Mode *oneshot_mode = get_mode(oneshot_mode_name); ++ if (oneshot_mode_name != NULL) { ++ oneshot_mode->oneshot_mode = get_mode(return_mode_name); ++ } ++ } ++} ++void create_mode_user(const Arg *arg) { ++ char * mode_name = *(char **)arg->v; ++ if (mode_name != NULL) { ++ Mode *mode_exists = get_mode(mode_name); ++ if (mode_exists == NULL) { ++ create_mode(mode_name); ++ } ++ } ++} ++void enter_mode(const Arg *arg) { ++ char * mode_name = *(char **)arg->v; ++ Mode *mode = get_mode(mode_name); ++ if (mode != NULL) { ++ active_mode = mode; ++ } ++} ++char * zriver_default_mode_name = "normal"; ++void setup_binds(void) { ++ if (modes_list.next == NULL) { ++ Mode *normal; ++ wl_list_init(&modes_list); ++ normal = create_mode(NULL); ++ if (normal == NULL) { die("out of memory!"); } ++ normal->name = zriver_default_mode_name; ++ normal_mode = normal; ++ active_mode = normal; ++ default_binds(&normal->linked_keys); ++ } + if (arg_str_store.next == NULL) { + wl_list_init(&arg_str_store); + } +} +#ifdef KEYS_USED -+void default_binds(void) { ++void default_binds(struct wl_list *keys_list) { + Key *k; + Key_linked *kl; + for (k = keys; k < END(keys); k++) { @@ -742,23 +832,33 @@ index 0000000..b8c7a8e + if (kl != NULL) { + kl->key = k; + kl->no_free_key = true; -+ wl_list_insert(&keys_list,&kl->link); ++ wl_list_insert(keys_list,&kl->link); + } + } +} +#endif +void clear_binds(const Arg* arg) { + Key_linked *kl,*tmp_kl; ++ Mode *mode,*tmp_mode; ++ active_mode = normal_mode; + -+ wl_list_for_each_safe(kl,tmp_kl,&keys_list,link) { -+ if (kl->no_remove == false) { -+ wl_list_remove(&kl->link); -+ if (kl->no_free_key == false) { -+ free(kl->key); ++ wl_list_for_each_safe(mode,tmp_mode,&modes_list,link) { ++ wl_list_for_each_safe(kl,tmp_kl,&mode->linked_keys,link) { ++ if (kl->no_remove == false) { ++ wl_list_remove(&kl->link); ++ if (kl->no_free_key == false) { ++ free(kl->key); ++ } ++ free(kl); + } -+ free(kl); ++ } ++ if (normal_mode != mode) { ++ wl_list_remove(&mode->link); ++ free(mode->name); ++ free(mode); + } + } ++ clear_str_store(&arg_str_store); +} + +void zriver_control_add_argument(struct wl_client *client, @@ -781,7 +881,6 @@ index 0000000..b8c7a8e + args->str_link = add_rule_str_store(); + args->p.rl->rule->monitor = -1; + } else { -+ // free(args->p.rl); + args->error = true; + args->error_msg = zriver_error_alloc; + } @@ -790,7 +889,6 @@ index 0000000..b8c7a8e + args->error_msg = zriver_error_alloc; + } + } else if (strcmp("map",argument) == 0 || strcmp("bind",argument) == 0) { -+ // printf("add bind!\n"); + args->type = ZRIVER_ARG_TYPE_KEY; + args->p.kl = calloc(1,sizeof(Key_linked)); + if (args->p.kl != NULL) { @@ -895,7 +993,7 @@ index 0000000..b8c7a8e + append_str_store((char**)args->p.fa->arg.v,argument,args->argc-1); + } + } else if (args->type == ZRIVER_ARG_TYPE_KEY) { -+ if (args->argc == 1) { ++ if (args->argc == 2) { + for (ms = Mod_str_pair_list; ms < END(Mod_str_pair_list); ms++) { + if (strstr(argument,ms->mod_str)) { + if (args->p.kl->key->mod != 0) { @@ -903,11 +1001,33 @@ index 0000000..b8c7a8e + } else { + args->p.kl->key->mod = ms->mod; + } -+ // printf("mod %s %i after %i detected\n",ms->mod_str,ms->mod,args->p.kl->key->mod); -+ // printf("mod %i\n",WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT); + } + } -+ } else if (args->argc == 2) { ++ } else if (args->argc == 1) { ++ int arg_len = strlen(argument) + 1; ++ if (arg_len > 1) { ++ bool found_mode = false; ++ Mode *mode; ++ wl_list_for_each(mode,&modes_list,link) { ++ printf("mode name: %s, argument %s \n",mode->name,argument); ++ if (strcmp(argument,mode->name) == 0) { ++ found_mode = true; ++ args->key_mode = mode; ++ break; ++ } ++ } ++ if (found_mode == false) { ++ args->key_mode = create_mode(argument); ++ if (args->key_mode == NULL) { ++ args->error = true; ++ args->error_msg = zriver_error_alloc; ++ } ++ } ++ } else { ++ args->error = true; ++ args->error_msg = zriver_error_too_few_args; ++ } ++ } else if (args->argc == 3) { + if (strcmp(argument,"none") == 0 ) { + args->p.kl->key->keysym = XKB_KEY_NoSymbol; + } else { @@ -917,7 +1037,7 @@ index 0000000..b8c7a8e + args->error_msg = zriver_error_invalid_keysym; + } + } -+ } else if (args->argc == 3) { ++ } else if (args->argc == 4) { + for (fst = Func_str_type_pair_list; fst < END(Func_str_type_pair_list); fst++) { + if (strcmp(argument,fst->func_str) == 0) { + args->p.kl->key->func = fst->func; @@ -925,11 +1045,11 @@ index 0000000..b8c7a8e + break; + } + } -+ } else if (args->argc == 4) { ++ } else if (args->argc == 5) { + arg_filter = true; + arg = &args->p.kl->key->arg; -+ } else if (args->argc > 4 && args->argc < STR_LINK_ARRAY_SIZE && args->key_arg_type == FUNC_STR_ARG_TYPE_STRING_ARRAY && args->p.kl->key->arg.v != NULL) { -+ append_str_store((char**)args->p.kl->key->arg.v,argument,args->argc-4); ++ } else if (args->argc > 5 && args->argc < STR_LINK_ARRAY_SIZE && args->key_arg_type == FUNC_STR_ARG_TYPE_STRING_ARRAY && args->p.kl->key->arg.v != NULL) { ++ append_str_store((char**)args->p.kl->key->arg.v,argument,args->argc-5); + } + } + if (arg_filter == true && arg != NULL) { @@ -937,7 +1057,6 @@ index 0000000..b8c7a8e + case(FUNC_STR_ARG_TYPE_NONE): + break; + case(FUNC_STR_ARG_TYPE_UINT): -+ // TODO make overflow check + arg->i = strtol(argument,NULL,10); + if (arg->i >= 0) { + arg->ui = arg->i; @@ -959,7 +1078,6 @@ index 0000000..b8c7a8e + } else { + arg->v = args->str_link->string; + } -+ // printf("string arg %s \n",*(char**)arg->v); + + break; + case(FUNC_STR_ARG_TYPE_WLR_DIRECTION): @@ -1033,7 +1151,7 @@ index 0000000..b8c7a8e + } else if (args->error == false) { + if (args->type == ZRIVER_ARG_TYPE_KEY) { + if (args->p.kl->key != NULL && args->p.kl->key->func != NULL) { -+ wl_list_insert(&keys_list,&args->p.kl->link); ++ wl_list_insert(&args->key_mode->linked_keys,&args->p.kl->link); + zriver_command_callback_v1_send_success(callback_interface,"bind success!"); + } else { + if (args->str_link != NULL) {