Compare commits

...

3 Commits

Author SHA1 Message Date
Zuki Air
b608e58da3 riverctl patch: fix bugs with keybind mode oneshot option 2025-08-01 23:19:34 +01:00
Zuki Air
0caf84ae8e riverctl patch: add keybind modes 2025-08-01 22:59:38 +01:00
Zuki Air
c7b78634a0 riverctl patch: better handling of invalid keysyms 2025-08-01 20:10:18 +01:00
2 changed files with 203 additions and 58 deletions

View File

@ -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

View File

@ -1,4 +1,4 @@
From 5b6f945beea355708aa8d9f98feb089510d5f1ef Mon Sep 17 00:00:00 2001
From 692cf030859e2ab663a2883e005835f8424dfdec Mon Sep 17 00:00:00 2001
From: Zuki Air <zukirust@gmail.com>
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 | 48 +-
dwlctl.c | 132 +++++
protocols/river-control-unstable-v1.xml | 85 +++
river-control.h | 656 ++++++++++++++++++++++++
7 files changed, 936 insertions(+), 16 deletions(-)
river-control.h | 753 ++++++++++++++++++++++++
7 files changed, 1055 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..366b8bf 100644
--- a/dwl.c
+++ b/dwl.c
@@ -145,7 +145,7 @@ typedef struct {
@ -187,20 +187,55 @@ 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,22 @@ 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) {
+ Mode *new_mode_if_oneshot = active_mode->oneshot_mode;
+
+ 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 (new_mode_if_oneshot != NULL) {
+ active_mode = new_mode_if_oneshot;
+ }
+ return 1;
+ }
+ }
+ for (k = keys_always; k < END(keys_always); k++) {
+ if (keybinding_key(mods,sym,k) == true) {return 1;}
+ }
return 0;
}
@@ -2645,6 +2668,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 +245,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 +3213,8 @@ main(int argc, char *argv[])
{
char *startup_cmd = NULL;
int c;
@ -450,23 +485,25 @@ index 0000000..aa5fc4d
+</protocol>
diff --git a/river-control.h b/river-control.h
new file mode 100644
index 0000000..42a5067
index 0000000..59561b6
--- /dev/null
+++ b/river-control.h
@@ -0,0 +1,656 @@
@@ -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 +517,15 @@ index 0000000..42a5067
+ 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,6 +554,9 @@ index 0000000..42a5067
+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" },
+ { 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),
@ -555,6 +604,7 @@ index 0000000..42a5067
+const char *zriver_error_double_appid = "set appid more then once!";
+const char *zriver_error_double_title = "set title more then once!";
+const char *zriver_error_under_zero = "argument can't be less then zero!";
+const char *zriver_error_invalid_keysym = "invalid keysym!";
+#define STR_LINK_ARRAY_SIZE 10
+struct Str_link {
+ struct wl_list link;
@ -588,6 +638,7 @@ index 0000000..42a5067
+ 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;
@ -651,7 +702,6 @@ index 0000000..42a5067
+ 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;
@ -705,31 +755,77 @@ index 0000000..42a5067
+ 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 != 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++) {
@ -737,23 +833,33 @@ index 0000000..42a5067
+ 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,
@ -776,7 +882,6 @@ index 0000000..42a5067
+ 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;
+ }
@ -785,7 +890,6 @@ index 0000000..42a5067
+ 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) {
@ -890,7 +994,7 @@ index 0000000..42a5067
+ 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) {
@ -898,13 +1002,43 @@ index 0000000..42a5067
+ } 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) {
+ args->p.kl->key->keysym = xkb_keysym_from_name(argument,XKB_KEYSYM_NO_FLAGS);
+ } 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 {
+ args->p.kl->key->keysym = xkb_keysym_from_name(argument,XKB_KEYSYM_NO_FLAGS);
+ if (args->p.kl->key->keysym == XKB_KEY_NoSymbol) {
+ args->error = true;
+ args->error_msg = zriver_error_invalid_keysym;
+ }
+ }
+ } 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;
@ -912,11 +1046,11 @@ index 0000000..42a5067
+ 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) {
@ -924,7 +1058,6 @@ index 0000000..42a5067
+ 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;
@ -946,7 +1079,6 @@ index 0000000..42a5067
+ } else {
+ arg->v = args->str_link->string;
+ }
+ // printf("string arg %s \n",*(char**)arg->v);
+
+ break;
+ case(FUNC_STR_ARG_TYPE_WLR_DIRECTION):
@ -1020,7 +1152,7 @@ index 0000000..42a5067
+ } 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) {