From 1ec0cbae78bd954fb4c5a1025b8f132cbce47e88 Mon Sep 17 00:00:00 2001 From: Zuki Air Date: Mon, 28 Jul 2025 16:01:04 +0100 Subject: [PATCH] add riverctl patch --- patches/riverctl/readme | 33 + patches/riverctl/riverctl.patch | 1162 +++++++++++++++++++++++++++++++ 2 files changed, 1195 insertions(+) create mode 100644 patches/riverctl/readme create mode 100644 patches/riverctl/riverctl.patch diff --git a/patches/riverctl/readme b/patches/riverctl/readme new file mode 100644 index 0000000..5fbfbf6 --- /dev/null +++ b/patches/riverctl/readme @@ -0,0 +1,33 @@ +This patch adds river-control-unstable-v1 support to dwl, allowing changing dwl settings on the fly via dwlctl. +dwlctl is a small wayland program included with this patch that connects to dwl and is able to change some dwl settings at runtime, +such as rules and keybinds. + +build for dwl-git commit 15bfffd8 + +currently only the following is supported: +clearing binds, +clearing rules, +adding rules, +adding binds, +running random functions, + +example commands for using dwlctl with dwl: + +dwlctl clear-binds + +dwlctl bind super,shift Return spawn kitty tmux + +dwlctl bind supershift q killclient + +dwlctl clear-rules + +dwlctl rule-add -appid steam -title steam float tags $((1 << 2)) + +dwlctl rule-add -appid kitty float + +dwlctl rule-add -title firefox float + +dwlctl func spawn kitty tmux + +dwlctl func setlayout 2 + diff --git a/patches/riverctl/riverctl.patch b/patches/riverctl/riverctl.patch new file mode 100644 index 0000000..e24078e --- /dev/null +++ b/patches/riverctl/riverctl.patch @@ -0,0 +1,1162 @@ +From 49240ee7200281d28fcf0c64b32b81b489ad7ca5 Mon Sep 17 00:00:00 2001 +From: Zuki Air +Date: Sun, 27 Jul 2025 11:30:32 +0100 +Subject: [PATCH] river-ctl patch + +--- + .gitignore | 1 + + Makefile | 22 +- + config.def.h | 34 +- + dwl.c | 20 +- + dwlctl.c | 132 +++++ + protocols/river-control-unstable-v1.xml | 85 +++ + river-control.h | 717 ++++++++++++++++++++++++ + 7 files changed, 996 insertions(+), 15 deletions(-) + create mode 100644 dwlctl.c + create mode 100644 protocols/river-control-unstable-v1.xml + create mode 100644 river-control.h + +diff --git a/.gitignore b/.gitignore +index 0dde90e..251aa4f 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -1,4 +1,5 @@ + dwl ++dwlctl + *.o + *-protocol.c + *-protocol.h +diff --git a/Makefile b/Makefile +index 578194f..029dfad 100644 +--- a/Makefile ++++ b/Makefile +@@ -21,8 +21,15 @@ dwl: dwl.o util.o + $(CC) dwl.o util.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@ + dwl.o: dwl.c client.h config.h config.mk cursor-shape-v1-protocol.h \ + pointer-constraints-unstable-v1-protocol.h wlr-layer-shell-unstable-v1-protocol.h \ +- wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h ++ wlr-output-power-management-unstable-v1-protocol.h \ ++ xdg-shell-protocol.h \ ++ river-control-unstable-v1-protocol.h river-control-unstable-v1-private-protocol.c river-control.h \ ++ dwlctl + util.o: util.c util.h ++#if there is a cleaner way of doing this please inform me this looks a little ugly ++dwlctl: river-control-unstable-v1-client-protocol.h river-control-unstable-v1-private-protocol.c river-control-unstable-v1-private-protocol.o dwlctl.c ++ $(CC) -c -o $@.o dwlctl.c ++ $(CC) -lwayland-client -o $@ dwlctl.o river-control-unstable-v1-private-protocol.o + + # wayland-scanner is a tool which generates C headers and rigging for Wayland + # protocols, which are specified in XML. wlroots requires you to rig these up +@@ -30,6 +37,17 @@ util.o: util.c util.h + WAYLAND_SCANNER = `$(PKG_CONFIG) --variable=wayland_scanner wayland-scanner` + WAYLAND_PROTOCOLS = `$(PKG_CONFIG) --variable=pkgdatadir wayland-protocols` + ++river-control-unstable-v1-client-protocol.h: ++ $(WAYLAND_SCANNER) client-header \ ++ protocols/river-control-unstable-v1.xml $@ ++river-control-unstable-v1-protocol.h: ++ $(WAYLAND_SCANNER) server-header \ ++ protocols/river-control-unstable-v1.xml $@ ++river-control-unstable-v1-private-protocol.c: ++ $(WAYLAND_SCANNER) private-code \ ++ protocols/river-control-unstable-v1.xml $@ ++river-control-unstable-v1-private-protocol.o: ++ $(CC) -c -o $@ river-control-unstable-v1-private-protocol.c + cursor-shape-v1-protocol.h: + $(WAYLAND_SCANNER) enum-header \ + $(WAYLAND_PROTOCOLS)/staging/cursor-shape/cursor-shape-v1.xml $@ +@@ -49,7 +67,7 @@ xdg-shell-protocol.h: + config.h: + cp config.def.h $@ + clean: +- rm -f dwl *.o *-protocol.h ++ rm -f dwl *.o *-protocol.h *-protocol.c + + dist: clean + mkdir -p dwl-$(VERSION) +diff --git a/config.def.h b/config.def.h +index 95c2afa..754b50c 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -21,7 +21,14 @@ static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You ca + static int log_level = WLR_ERROR; + + /* NOTE: ALWAYS keep a rule declared even if you don't use rules (e.g leave at least one example) */ +-static const Rule rules[] = { ++/* with river-control patch these rules are still applied but cannot be disabled ++ * Intended for when you have startup programs you want to apply rules to and want to a avoid a race condition with riverctl ++ * ++ * the USE_RULES macro sets the rules array to be used without it the rules array goes full unused, allowing it to be removed. ++ * The new_rules_override macro makes new rules with the exact same app_id and title replace the old one. */ ++#define USE_RULES ++#define NEW_RULES_OVERRIDE ++static Rule rules[] = { + /* app_id title tags mask isfloating monitor */ + /* examples: */ + { "Gimp_EXAMPLE", NULL, 0, 1, -1 }, /* Start on currently visible tags floating, not tiled */ +@@ -122,7 +129,11 @@ static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TA + static const char *termcmd[] = { "foot", NULL }; + static const char *menucmd[] = { "wmenu-run", NULL }; + +-static const Key keys[] = { ++/* note keys gets cleared with riverctl clear-binds but the keys_always are excluded from being cleared ++ * this is to have a list of fallback keybinds if your riverctl script fails ++ * if you won't like to have keys[] declared commented out the KEYS_USED macro bellow to disable the functionality*/ ++#define KEYS_USED ++static Key keys[] = { + /* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */ + /* modifier key function argument */ + { MODKEY, XKB_KEY_p, spawn, {.v = menucmd} }, +@@ -157,13 +168,18 @@ static const Key keys[] = { + TAGKEYS( XKB_KEY_7, XKB_KEY_ampersand, 6), + TAGKEYS( XKB_KEY_8, XKB_KEY_asterisk, 7), + TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8), +- { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Q, quit, {0} }, +- +- /* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */ +- { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_Terminate_Server, quit, {0} }, +- /* Ctrl-Alt-Fx is used to switch to another VT, if you don't know what a VT is +- * do not remove them. +- */ ++}; ++static Key keys_always[] = { ++// /* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */ ++// /* modifier key function argument */ ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_D, spawn, SHCMD("foot")}, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Q, quit, {0}}, ++// ++// /* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */ ++// { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_Terminate_Server, quit, {0} }, ++// /* Ctrl-Alt-Fx is used to switch to another VT, if you don't know what a VT is ++// * do not remove them. ++// */ + #define CHVT(n) { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_XF86Switch_VT_##n, chvt, {.ui = (n)} } + 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 +--- a/dwl.c ++++ b/dwl.c +@@ -145,7 +145,7 @@ typedef struct { + uint32_t mod; + xkb_keysym_t keysym; + void (*func)(const Arg *); +- const Arg arg; ++ Arg arg; + } Key; + + typedef struct { +@@ -452,6 +452,9 @@ static struct wlr_xwayland *xwayland; + /* configuration, allows nested code to access above variables */ + #include "config.h" + ++/* river control */ ++#include "river-control.h" ++ + /* attempt to encapsulate suck into one file */ + #include "client.h" + +@@ -480,13 +483,15 @@ applyrules(Client *c) + const char *appid, *title; + uint32_t newtags = 0; + int i; ++ const Rule_linked *rl; + const Rule *r; + Monitor *mon = selmon, *m; + + appid = client_get_appid(c); + title = client_get_title(c); + +- for (r = rules; r < END(rules); r++) { ++ wl_list_for_each(rl,&rules_list,link) { ++ r = rl->rule; + 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) + * processing keys, rather than passing them on to the client for its own + * processing. + */ +- const Key *k; +- for (k = keys; k < END(keys); k++) { ++ const Key_linked *kl; ++ const Key *k; ++ wl_list_for_each(kl,&keys_list,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) + wl_signal_add(&output_mgr->events.apply, &output_mgr_apply); + wl_signal_add(&output_mgr->events.test, &output_mgr_test); + ++ // river_control_v1 = river_control_unstable_v1_create(dpy, 1); ++ wl_global_create(dpy, &zriver_control_v1_interface, 1, NULL, zriver_control_handle_bind); ++ + /* 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[]) + { + char *startup_cmd = NULL; + int c; ++ setup_binds(); ++ setup_rules(); + + while ((c = getopt(argc, argv, "s:hdv")) != -1) { + if (c == 's') +diff --git a/dwlctl.c b/dwlctl.c +new file mode 100644 +index 0000000..3db6fbf +--- /dev/null ++++ b/dwlctl.c +@@ -0,0 +1,132 @@ ++#include ++#include ++#include ++#include ++#include ++#include "river-control-unstable-v1-protocol.h" ++#include "river-control-unstable-v1-client-protocol.h" ++ ++struct wl_display *wl_display; ++struct wl_registry *wl_registry; ++struct wl_callback *sync_callback; ++struct zriver_control_v1 *zriver_ctl = NULL; ++struct zriver_command_callback_v1 *zriver_callback = NULL; ++struct wl_seat *seat = NULL; ++struct wl_callback *sync_callback; ++bool loop = true; ++char** argv; ++int argc; ++ ++static void noop () {} ++ ++ ++static void callback_failure(void *data, ++ struct zriver_command_callback_v1 *zriver_command_callback_v1, ++ const char *failure_message) { ++ if (failure_message != NULL) { ++ printf("error: %s\n",failure_message); ++ } ++ zriver_command_callback_v1_destroy(zriver_command_callback_v1); ++ zriver_control_v1_destroy(zriver_ctl); ++ zriver_ctl = NULL; ++ loop = false; ++} ++static void callback_success(void *data, ++ struct zriver_command_callback_v1 *zriver_command_callback_v1, ++ const char *output) { ++ if (output[0] != '\0') { ++ printf("%s\n",output); ++ } ++ zriver_command_callback_v1_destroy(zriver_command_callback_v1); ++ loop = false; ++} ++ ++struct zriver_command_callback_v1_listener zriver_callback_listener = { ++ .success = callback_success, ++ .failure = callback_failure, ++}; ++ ++ ++ ++static void ++registry_handle_global(void *data, struct wl_registry *registry, ++ uint32_t name, const char *interface, uint32_t version) { ++ if ( strcmp(interface, zriver_control_v1_interface.name) == 0 ) { ++ zriver_ctl = wl_registry_bind(registry, name, ++ &zriver_control_v1_interface, 1); ++ } else if (strcmp(interface, wl_seat_interface.name) == 0) { ++ seat = wl_registry_bind(registry, name, ++ &wl_seat_interface, 1); ++ } ++} ++ ++static void add_arguments(){ ++ for (char **p = argv + 1; *p != NULL; p++) { ++ zriver_control_v1_add_argument(zriver_ctl,*p); ++ } ++ zriver_callback = zriver_control_v1_run_command(zriver_ctl,seat); ++ zriver_command_callback_v1_add_listener(zriver_callback,&zriver_callback_listener,NULL); ++} ++ ++static void sync_handle_done (void *data, struct wl_callback *wl_callback, ++ uint32_t irrelevant) { ++ wl_callback_destroy(wl_callback); ++ sync_callback = NULL; ++ if ( seat == NULL ) { ++ fputs("compositor doesn't support wl_seat?\n", stderr); ++ loop = false; ++ return; ++ } ++ if ( zriver_ctl == NULL ) { ++ fputs("compositor doesn't support riverctl.\n", stderr); ++ loop = false; ++ return; ++ } ++ add_arguments(); ++} ++ ++static const struct wl_callback_listener sync_callback_listener = { ++ .done = sync_handle_done, ++}; ++ ++static const struct wl_registry_listener registry_listener = { ++ .global = registry_handle_global, ++ .global_remove = noop ++}; ++ ++static bool init_wayland (void) { ++ const char *display_name = getenv("WAYLAND_DISPLAY"); ++ if ( display_name == NULL ) ++ { ++ fputs("WAYLAND_DISPLAY is not set.\n", stderr); ++ return false; ++ } ++ ++ wl_display = wl_display_connect(display_name); ++ if ( wl_display == NULL ) ++ { ++ fputs("Can not connect to Wayland server.\n", stderr); ++ return false; ++ } ++ ++ /* The registry is a global object which is used to advertise all ++ * available global objects. ++ */ ++ wl_registry = wl_display_get_registry(wl_display); ++ wl_registry_add_listener(wl_registry, ®istry_listener, NULL); ++ ++ sync_callback = wl_display_sync(wl_display); ++ wl_callback_add_listener(sync_callback, &sync_callback_listener, NULL); ++ ++ return true; ++} ++ ++int main(int argc_local, char *argv_local[]) { ++ argc = argc_local; ++ argv = argv_local; ++ if (init_wayland()) { ++ while (loop && wl_display_dispatch(wl_display) != -1) {}; ++ } ++ // cleanup(); ++ return 0; ++} +diff --git a/protocols/river-control-unstable-v1.xml b/protocols/river-control-unstable-v1.xml +new file mode 100644 +index 0000000..aa5fc4d +--- /dev/null ++++ b/protocols/river-control-unstable-v1.xml +@@ -0,0 +1,85 @@ ++ ++ ++ ++ Copyright 2020 The River Developers ++ ++ Permission to use, copy, modify, and/or distribute this software for any ++ purpose with or without fee is hereby granted, provided that the above ++ copyright notice and this permission notice appear in all copies. ++ ++ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ ++ ++ ++ ++ This interface allows clients to run compositor commands and receive a ++ success/failure response with output or a failure message respectively. ++ ++ Each command is built up in a series of add_argument requests and ++ executed with a run_command request. The first argument is the command ++ to be run. ++ ++ A complete list of commands should be made available in the man page of ++ the compositor. ++ ++ ++ ++ ++ This request indicates that the client will not use the ++ river_control object any more. Objects that have been created ++ through this instance are not affected. ++ ++ ++ ++ ++ ++ Arguments are stored by the server in the order they were sent until ++ the run_command request is made. ++ ++ ++ ++ ++ ++ ++ Execute the command built up using the add_argument request for the ++ given seat. ++ ++ ++ ++ ++ ++ ++ ++ ++ This object is created by the run_command request. Exactly one of the ++ success or failure events will be sent. This object will be destroyed ++ by the compositor after one of the events is sent. ++ ++ ++ ++ ++ Sent when the command has been successfully received and executed by ++ the compositor. Some commands may produce output, in which case the ++ output argument will be a non-empty string. ++ ++ ++ ++ ++ ++ ++ Sent when the command could not be carried out. This could be due to ++ sending a non-existent command, no command, not enough arguments, too ++ many arguments, invalid arguments, etc. ++ ++ ++ ++ ++ +diff --git a/river-control.h b/river-control.h +new file mode 100644 +index 0000000..7d54cdf +--- /dev/null ++++ b/river-control.h +@@ -0,0 +1,717 @@ ++#include "river-control-unstable-v1-private-protocol.c" ++#include "river-control-unstable-v1-protocol.h" ++#ifdef KEYS_USED ++void default_binds(void); ++#endif ++// 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; ++typedef struct { ++ Key *key; ++ struct wl_list link; ++ bool no_free_key; ++ bool no_remove; ++} Key_linked; ++typedef struct { ++ Rule *rule; ++ struct wl_list link; ++ bool no_free_rule; ++ bool no_remove; ++ struct Str_link *str_link; ++} Rule_linked; ++struct Keysym_str_pair { ++ xkb_keysym_t keysym; ++ const char * keysym_str; ++}; ++struct Mod_str_pair { ++ uint32_t mod; ++ const char * mod_str; ++}; ++typedef enum { ++ FUNC_STR_ARG_TYPE_NONE, ++ FUNC_STR_ARG_TYPE_INT, ++ FUNC_STR_ARG_TYPE_UINT, ++ FUNC_STR_ARG_TYPE_FLOAT, ++ FUNC_STR_ARG_TYPE_STRING_ARRAY, ++ FUNC_STR_ARG_TYPE_WLR_DIRECTION, ++ FUNC_STR_ARG_TYPE_LAYOUT, ++} Func_str_arg_type; ++ ++struct Func_str_type_pair { ++ void (*func)(const Arg *); ++ Func_str_arg_type arg_type; ++ const char * func_str; ++}; ++#define STR(a,b) \ ++ { a, b, #a } ++struct Func_str_type_pair Func_str_type_pair_list[] = { ++ STR(setlayout,FUNC_STR_ARG_TYPE_LAYOUT), ++ STR(spawn,FUNC_STR_ARG_TYPE_STRING_ARRAY), ++ STR(focusstack,FUNC_STR_ARG_TYPE_INT), ++ STR(setmfact,FUNC_STR_ARG_TYPE_FLOAT), ++ STR(zoom,FUNC_STR_ARG_TYPE_NONE), ++ STR(killclient,FUNC_STR_ARG_TYPE_NONE), ++ STR(incnmaster,FUNC_STR_ARG_TYPE_INT), ++ STR(togglefloating,FUNC_STR_ARG_TYPE_NONE), ++ STR(togglefullscreen,FUNC_STR_ARG_TYPE_NONE), ++ STR(view,FUNC_STR_ARG_TYPE_UINT), ++ STR(toggleview,FUNC_STR_ARG_TYPE_UINT), ++ STR(tagmon,FUNC_STR_ARG_TYPE_WLR_DIRECTION), ++ STR(focusmon,FUNC_STR_ARG_TYPE_WLR_DIRECTION), ++ STR(tag,FUNC_STR_ARG_TYPE_UINT), ++ STR(toggletag,FUNC_STR_ARG_TYPE_UINT), ++ STR(togglefullscreen,FUNC_STR_ARG_TYPE_NONE), ++ STR(quit,FUNC_STR_ARG_TYPE_NONE), ++}; ++#undef STR ++struct Mod_str_pair Mod_str_pair_list[] = { ++ {0,"none"}, ++ {WLR_MODIFIER_LOGO,"super"}, ++ {WLR_MODIFIER_LOGO,"logo"}, ++ {WLR_MODIFIER_CTRL,"ctrl"}, ++ {WLR_MODIFIER_ALT,"alt"}, ++ {WLR_MODIFIER_SHIFT,"shift"}, ++}; ++#define STR(a) \ ++ { XKB_KEY_##a, #a } ++struct Keysym_str_pair Keysym_str_pair_list[] = { ++ STR(1), ++ STR(2), ++ STR(3), ++ STR(4), ++ STR(5), ++ STR(6), ++ STR(7), ++ STR(8), ++ STR(9), ++ STR(0), ++ STR(percent), ++ STR(asciicircum), ++ STR(ampersand), ++ STR(asterisk), ++ STR(parenleft), ++ STR(parenright), ++ STR(comma), ++ STR(period), ++ STR(less), ++ STR(greater), ++ STR(exclam), ++ STR(at), ++ STR(numbersign), ++ STR(dollar), ++ STR(percent), ++ STR(Return), ++ STR(Tab), ++ STR(q), ++ STR(w), ++ STR(e), ++ STR(r), ++ STR(t), ++ STR(y), ++ STR(u), ++ STR(i), ++ STR(o), ++ STR(p), ++ STR(a), ++ STR(s), ++ STR(d), ++ STR(f), ++ STR(g), ++ STR(h), ++ STR(j), ++ STR(k), ++ STR(l), ++ STR(z), ++ STR(x), ++ STR(c), ++ STR(v), ++ STR(b), ++ STR(n), ++ STR(m), ++ STR(Q), ++ STR(W), ++ STR(E), ++ STR(R), ++ STR(T), ++ STR(Y), ++ STR(U), ++ STR(I), ++ STR(O), ++ STR(P), ++ STR(A), ++ STR(S), ++ STR(D), ++ STR(F), ++ STR(G), ++ STR(H), ++ STR(J), ++ STR(K), ++ STR(L), ++ STR(Z), ++ STR(X), ++ STR(C), ++ STR(V), ++ STR(B), ++ STR(N), ++ STR(M), ++}; ++ ++typedef enum { ++ ZRIVER_ARG_TYPE_NONE=0, ++ ZRIVER_ARG_TYPE_KEY, ++ ZRIVER_ARG_TYPE_RULE, ++ ZRIVER_ARG_TYPE_FUNC, ++} ZRIVER_ARG_TYPE; ++const char *zriver_error_generic = "catchall error"; ++const char *zriver_error_alloc = "alloc error"; ++const char *zriver_error_too_few_args = "too few args"; ++const char *zriver_error_out_of_range = "out of arg range"; ++const char *zriver_error_no_matching_argument = "no matching argument"; ++const char *zriver_error_double_appid = "set appid more then once!"; ++const char *zriver_error_double_title = "set title more then once!"; ++#define STR_LINK_ARRAY_SIZE 10 ++struct Str_link { ++ struct wl_list link; ++ char* string[STR_LINK_ARRAY_SIZE]; ++}; ++struct zriver_func_arg_pair { ++ void (*func)(const Arg *); ++ Arg arg; ++}; ++union zriver_arg_ptr { ++ Rule_linked *rl; ++ Key_linked *kl; ++ struct zriver_func_arg_pair *fa; ++}; ++typedef enum { ++ ZRIVER_RULE_MATCH_TYPE_NONE=0, ++ ZRIVER_RULE_MATCH_TYPE_APPID, ++ ZRIVER_RULE_MATCH_TYPE_TITLE, ++ ZRIVER_RULE_MATCH_TYPE_APPLYING, ++} Rule_match_type_next; ++typedef enum { ++ ZRIVER_RULE_TYPE_NONE=0, ++ ZRIVER_RULE_TYPE_TAGS, ++ ZRIVER_RULE_TYPE_MONITOR, ++} Rule_type; ++struct zriver_arg_list_resource { ++ int argc; ++ ZRIVER_ARG_TYPE type; ++ union zriver_arg_ptr p; ++ struct Str_link *str_link; ++ Func_str_arg_type key_arg_type; ++ Rule_match_type_next rule_match_type; ++ Rule_type rule_type; ++ bool rule_valid; ++ bool error; ++ const char* error_msg; ++}; ++ ++void zriver_control_destroy(struct wl_client *client, ++ struct wl_resource *resource) { ++ printf("destroy!\n"); ++} ++void clear_str_store(struct wl_list *str_store) { ++ struct Str_link *str_link,*str_link_tmp; ++ int i; ++ wl_list_for_each_safe(str_link,str_link_tmp,str_store,link) { ++ wl_list_remove(&str_link->link); ++ for (i = 0; i < STR_LINK_ARRAY_SIZE; i++) { ++ if (str_link->string[i] != NULL) { ++ free(str_link->string[i]); ++ } ++ } ++ free(str_link); ++ } ++} ++void free_str_store(struct Str_link *str_link) { ++ int i; ++ char** string = str_link->string; ++ for (i = 0; i < STR_LINK_ARRAY_SIZE; i++) { ++ if (string != NULL) { ++ free(*string); ++ } ++ string++; ++ } ++ wl_list_remove(&str_link->link); ++} ++char* append_str_store(char** str_store_array,const char * string,int index) { ++ char** append_str = str_store_array+index; ++ int string_len = strlen(string) + 1; ++ *append_str = malloc(sizeof(char) * string_len); ++ if (*append_str != NULL) { ++ memcpy(*append_str,string,string_len); ++ } ++ return *append_str; ++} ++struct Str_link* add_rule_str_store(void) { ++ struct Str_link *str_link = calloc(1,sizeof(struct Str_link)); ++ int i; ++ if (str_link == NULL) { ++ return NULL; ++ } ++ for (i = 1; i < STR_LINK_ARRAY_SIZE ;i++) { ++ str_link->string[i] = NULL; ++ } ++ wl_list_insert(&rule_str_store,&str_link->link); ++ 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; ++ if (str_link == NULL) { ++ return NULL; ++ } ++ str_link->string[0] = malloc(sizeof(char) * string_len); ++ if (str_link->string[0] == NULL) { ++ free(str_link); ++ return NULL; ++ } ++ memcpy(str_link->string[0],string,string_len); ++ for (i = 1; i < STR_LINK_ARRAY_SIZE ;i++) { ++ str_link->string[i] = NULL; ++ } ++ wl_list_insert(&arg_str_store,&str_link->link); ++ return str_link; ++} ++void setup_rules(void) { ++#ifdef USE_RULES ++ Rule *r; ++ Rule_linked *rl; ++#endif ++ if (rules_list.next == NULL) { ++ wl_list_init(&rules_list); ++ } ++#ifdef USE_RULES ++ for (r = rules; r < END(rules); r++) { ++ rl = calloc(1,sizeof(Rule_linked)); ++ if (rl != NULL) { ++ rl->rule = r; ++ rl->no_free_rule = true; ++ rl->no_remove = true; /* remove this line to make rules[] removed by clear-rules */ ++ wl_list_insert(&rules_list,&rl->link); ++ } ++ } ++#endif ++ if (rule_str_store.next == NULL) { ++ wl_list_init(&rule_str_store); ++ } ++} ++void clear_rules(void) { ++ Rule_linked *rl,*tmp_rl; ++ ++ wl_list_for_each_safe(rl,tmp_rl,&rules_list,link) { ++ if (rl->no_remove == false) { ++ wl_list_remove(&rl->link); ++ if (rl->no_free_rule == false) { ++ free(rl->rule); ++ } ++ 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); ++ } ++ } ++#ifdef KEYS_USED ++ default_binds(); ++#endif ++ if (arg_str_store.next == NULL) { ++ wl_list_init(&arg_str_store); ++ } ++} ++#ifdef KEYS_USED ++void default_binds(void) { ++ Key *k; ++ Key_linked *kl; ++ for (k = keys; k < END(keys); k++) { ++ kl = calloc(1,sizeof(Key_linked)); ++ if (kl != NULL) { ++ kl->key = k; ++ kl->no_free_key = true; ++ wl_list_insert(&keys_list,&kl->link); ++ } ++ } ++} ++#endif ++void clear_binds(void) { ++ Key_linked *kl,*tmp_kl; ++ ++ 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); ++ } ++ free(kl); ++ } ++ } ++ clear_str_store(&arg_str_store); ++} ++ ++void zriver_control_add_argument(struct wl_client *client, ++ struct wl_resource *resource, ++ const char *argument) { ++ struct zriver_arg_list_resource *args = wl_resource_get_user_data(resource); ++ const struct Keysym_str_pair *ks; ++ const struct Mod_str_pair *ms; ++ const struct Func_str_type_pair *fst; ++ bool arg_filter = false; ++ Arg *arg = NULL; ++ ++ if (args->error == true) {return;} ++ if (args->argc == 0) { ++ if (strcmp("rule-add",argument) == 0) { ++ args->type = ZRIVER_ARG_TYPE_RULE; ++ args->p.rl = calloc(1,sizeof(Rule_linked)); ++ if (args->p.rl != NULL) { ++ args->p.rl->rule = calloc(1,sizeof(Rule)); ++ if (args->p.rl->rule != NULL) { ++ 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; ++ } ++ } else { ++ args->error = true; ++ 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) { ++ args->p.kl->key = calloc(1,sizeof(Key)); ++ if (args->p.kl->key == NULL) { ++ free(args->p.kl); ++ args->error = true; ++ args->error_msg = zriver_error_alloc; ++ } else { ++ args->str_link = add_arg_str_store(argument); ++ } ++ } else { ++ args->error = true; ++ args->error_msg = zriver_error_alloc; ++ } ++ } else if (strcmp("clear-binds",argument) == 0 ) { ++ clear_binds(); ++ } else if (strcmp("clear-rules",argument) == 0 ) { ++ clear_rules(); ++ } else if (strcmp("func",argument) == 0) { ++ args->type = ZRIVER_ARG_TYPE_FUNC; ++ args->p.fa = calloc(1,sizeof(struct zriver_func_arg_pair)); ++ if (args->p.fa == NULL) { ++ args->error = true; ++ args->error_msg = zriver_error_alloc; ++ } else { ++ args->str_link = add_arg_str_store(argument); ++ } ++ } else { ++ args->error = true; ++ args->error_msg = zriver_error_no_matching_argument; ++ } ++ } else if (args->type == ZRIVER_ARG_TYPE_RULE && args->str_link != NULL) { ++ switch (args->rule_match_type) { ++ case(ZRIVER_RULE_MATCH_TYPE_NONE): ++ if (strcmp(argument,"-appid") == 0) { ++ args->rule_match_type = ZRIVER_RULE_MATCH_TYPE_APPID; ++ } else if (strcmp(argument,"-title") == 0) { ++ args->rule_match_type = ZRIVER_RULE_MATCH_TYPE_TITLE; ++ } else { ++ args->rule_match_type = ZRIVER_RULE_MATCH_TYPE_APPLYING; ++ } ++ break; ++ case(ZRIVER_RULE_MATCH_TYPE_APPID): ++ if (args->p.rl->rule->id == NULL) { ++ args->p.rl->rule->id = append_str_store(args->str_link->string,argument,args->argc-1); ++ args->rule_match_type = ZRIVER_RULE_MATCH_TYPE_NONE; ++ } else { ++ free(args->p.rl->rule); ++ free(args->p.rl); ++ args->error = true; ++ args->error_msg = zriver_error_double_appid; ++ free_str_store(args->str_link); ++ } ++ break; ++ case(ZRIVER_RULE_MATCH_TYPE_TITLE): ++ if (args->p.rl->rule->title == NULL) { ++ args->p.rl->rule->title = append_str_store(args->str_link->string,argument,args->argc-1); ++ args->rule_match_type = ZRIVER_RULE_MATCH_TYPE_NONE; ++ } else { ++ free(args->p.rl->rule); ++ free(args->p.rl); ++ args->error = true; ++ args->error_msg = zriver_error_double_title; ++ free_str_store(args->str_link); ++ } ++ break; ++ case(ZRIVER_RULE_MATCH_TYPE_APPLYING): ++ break; ++ } ++ if (args->rule_match_type == ZRIVER_RULE_MATCH_TYPE_APPLYING) { ++ switch (args->rule_type) { ++ case(ZRIVER_RULE_TYPE_NONE): ++ if (strcmp(argument,"float") == 0) { ++ args->p.rl->rule->isfloating = true; ++ args->rule_valid = true; ++ } else if (strcmp(argument,"tags") == 0){ ++ args->rule_type = ZRIVER_RULE_TYPE_TAGS; ++ args->rule_valid = true; ++ } else if (strcmp(argument,"monitor") == 0){ ++ args->rule_type = ZRIVER_RULE_TYPE_MONITOR; ++ args->rule_valid = true; ++ } else { ++ free(args->p.rl->rule); ++ free(args->p.rl); ++ free_str_store(args->str_link); ++ args->error = true; ++ } ++ break; ++ case(ZRIVER_RULE_TYPE_TAGS): ++ args->p.rl->rule->tags = strtol(argument,NULL,10); ++ args->rule_type = ZRIVER_RULE_TYPE_NONE; ++ args->rule_valid = true; ++ break; ++ case(ZRIVER_RULE_TYPE_MONITOR): ++ args->p.rl->rule->monitor = strtol(argument,NULL,10); ++ args->rule_type = ZRIVER_RULE_TYPE_NONE; ++ args->rule_valid = true; ++ break; ++ } ++ ++ } ++ ++ } else if (args->type == ZRIVER_ARG_TYPE_FUNC) { ++ if (args->argc == 1) { ++ for (fst = Func_str_type_pair_list; fst < END(Func_str_type_pair_list); fst++) { ++ if (strcmp(argument,fst->func_str) == 0) { ++ args->p.fa->func = fst->func; ++ args->key_arg_type = fst->arg_type; ++ break; ++ } ++ } ++ } else if (args->argc == 2) { ++ arg_filter = true; ++ arg = &args->p.fa->arg; ++ } else if (args->argc > 2 && 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.fa->arg.v,argument,args->argc-2); ++ } ++ } else if (args->type == ZRIVER_ARG_TYPE_KEY) { ++ if (args->argc == 1) { ++ 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) { ++ args->p.kl->key->mod = args->p.kl->key->mod|ms->mod; ++ } 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) { ++ for (ks = Keysym_str_pair_list; ks < END(Keysym_str_pair_list); ks++) { ++ if (strcmp(argument,ks->keysym_str) == 0) { ++ // printf("keysym %s picked\n",ks->keysym_str); ++ args->p.kl->key->keysym = ks->keysym; ++ break; ++ } ++ } ++ } else if (args->argc == 3) { ++ 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; ++ args->key_arg_type = fst->arg_type; ++ break; ++ } ++ } ++ } else if (args->argc == 4) { ++ 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); ++ } ++ } ++ if (arg_filter == true && arg != NULL) { ++ switch (args->key_arg_type) { ++ case(FUNC_STR_ARG_TYPE_NONE): ++ break; ++ case(FUNC_STR_ARG_TYPE_UINT): ++ // TODO make overflow check ++ arg->ui = strtol(argument,NULL,10); ++ break; ++ case(FUNC_STR_ARG_TYPE_INT): ++ arg->i = strtol(argument,NULL,10); ++ break; ++ case(FUNC_STR_ARG_TYPE_FLOAT): ++ arg->f = strtof(argument,NULL); ++ break; ++ case(FUNC_STR_ARG_TYPE_STRING_ARRAY): ++ args->str_link = add_arg_str_store(argument); ++ if (args->str_link == NULL) { ++ printf("string arg NULL \n"); ++ } else { ++ arg->v = args->str_link->string; ++ } ++ // printf("string arg %s \n",*(char**)arg->v); ++ ++ break; ++ case(FUNC_STR_ARG_TYPE_WLR_DIRECTION): ++ if (strcmp("up",argument)) { ++ arg->i = WLR_DIRECTION_UP; ++ } else if (strcmp("left",argument)) { ++ arg->i = WLR_DIRECTION_LEFT; ++ } else if (strcmp("right",argument)) { ++ arg->i = WLR_DIRECTION_RIGHT; ++ } else if (strcmp("down",argument)) { ++ arg->i = WLR_DIRECTION_DOWN; ++ } else { ++ if (ZRIVER_ARG_TYPE_KEY) { ++ free(args->p.kl->key); ++ free(args->p.kl); ++ } ++ args->error = true; ++ args->error_msg = zriver_error_out_of_range; ++ } ++ break; ++ case(FUNC_STR_ARG_TYPE_LAYOUT): ++ arg->ui = strtol(argument,NULL,10); ++ if (arg->ui < LENGTH(layouts)) { ++ arg->v = &layouts[arg->ui]; ++ } else { ++ if (ZRIVER_ARG_TYPE_KEY) { ++ free(args->p.kl->key); ++ free(args->p.kl); ++ } ++ args->error = true; ++ args->error_msg = zriver_error_out_of_range; ++ } ++ ++ } ++ } ++ args->argc++; ++ printf("add arg '%s' !\n",argument); ++} ++void zriver_control_run_command(struct wl_client *client, ++ struct wl_resource *resource, ++ struct wl_resource *run_command_seat, ++ uint32_t callback) { ++ struct zriver_arg_list_resource *args = wl_resource_get_user_data(resource); ++ struct wl_resource *callback_interface = wl_resource_create( ++ client, &zriver_command_callback_v1_interface, zriver_command_callback_v1_interface.version, callback); ++ if (args->argc == 0) { ++ zriver_command_callback_v1_send_failure(callback_interface,zriver_error_too_few_args); ++ } else if (args->error == true) { ++ zriver_command_callback_v1_send_failure(callback_interface,args->error_msg); ++ } 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); ++ zriver_command_callback_v1_send_success(callback_interface,"bind success!"); ++ } else { ++ if (args->str_link != NULL) { ++ free_str_store(args->str_link); ++ free(args->str_link); ++ } ++ if (args->p.kl->key != NULL) { ++ free(args->p.kl->key); ++ } ++ zriver_command_callback_v1_send_failure(callback_interface,zriver_error_too_few_args); ++ } ++ } else if (args->type == ZRIVER_ARG_TYPE_FUNC) { ++ if (args->p.fa->func != NULL) { ++ args->p.fa->func(&args->p.fa->arg); ++ zriver_command_callback_v1_send_success(callback_interface,"command success!"); ++ } else { ++ zriver_command_callback_v1_send_failure(callback_interface,zriver_error_too_few_args); ++ } ++ if (args->str_link != NULL) { ++ free_str_store(args->str_link); ++ free(args->str_link); ++ } ++ free(args->p.fa); ++ } else if (args->type == ZRIVER_ARG_TYPE_RULE) { ++ if (args->rule_valid == true) { ++ /* check for rule with same title and id */ ++ bool replaced_rule = false; ++#ifdef NEW_RULES_OVERRIDE ++ if (args->p.rl->rule->title != NULL || args->p.rl->rule->id != NULL) { ++ Rule_linked *rl; ++ Rule *r; ++ wl_list_for_each(rl,&rules_list,link) { ++ r = rl->rule; ++#define CHECKNULL(a,b) ((a != NULL && b != NULL && strcmp(a,b) == 0) || (a == NULL && b == NULL)) ++ if (CHECKNULL(args->p.rl->rule->title,r->title) && CHECKNULL(args->p.rl->rule->id,r->id)) { ++ wl_list_remove(&rl->link); ++ if (rl->no_free_rule == false) { ++ free(rl->rule); ++ } ++ if (rl->str_link != NULL) { ++ free_str_store(rl->str_link); ++ free(rl->str_link); ++ } ++ free(rl); ++ replaced_rule = true; ++ break; ++ } ++ } ++ } ++#endif ++ wl_list_insert(rules_list.prev,&args->p.rl->link); ++ if (replaced_rule == true) { ++ zriver_command_callback_v1_send_success(callback_interface,"rule replaced!"); ++ } else { ++ zriver_command_callback_v1_send_success(callback_interface,"rule success!"); ++ } ++ } else { ++ zriver_command_callback_v1_send_failure(callback_interface,zriver_error_too_few_args); ++ free_str_store(args->str_link); ++ free(args->str_link); ++ free(args->p.rl->rule); ++ free(args->p.rl); ++ } ++ } else { ++ zriver_command_callback_v1_send_success(callback_interface,""); ++ } ++ } ++} ++struct zriver_control_v1_interface zriver_control_interface = { ++ .run_command = zriver_control_run_command, ++ .add_argument = zriver_control_add_argument, ++ .destroy = zriver_control_destroy, ++}; ++static void zriver_control_handle_destory(struct wl_resource *resource) { ++ struct zriver_arg_list_resource *zriver_arg_list_resource = wl_resource_get_user_data(resource); ++ free(zriver_arg_list_resource); ++ printf("handle destroy\n"); ++} ++static void zriver_control_handle_bind(struct wl_client *client, void *data, ++ uint32_t version, uint32_t id) { ++ struct zriver_arg_list_resource *zriver_arg_list_resource = calloc(1,sizeof(struct zriver_arg_list_resource) ); ++ struct wl_resource *resource = wl_resource_create( ++ client, &zriver_control_v1_interface, zriver_control_v1_interface.version, id); ++ zriver_arg_list_resource->error_msg = zriver_error_generic; ++ ++ ++ wl_resource_set_implementation(resource, &zriver_control_interface, ++ zriver_arg_list_resource, zriver_control_handle_destory); ++} +-- +2.49.1 +