From 7ffea896fab52b3a694fda1472d16453eceedf0c 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.md | 54 ++ patches/riverctl/riverctl.patch | 1241 +++++++++++++++++++++++++++++++ 2 files changed, 1295 insertions(+) create mode 100644 patches/riverctl/README.md create mode 100644 patches/riverctl/riverctl.patch diff --git a/patches/riverctl/README.md b/patches/riverctl/README.md new file mode 100644 index 0000000..ac4bc6b --- /dev/null +++ b/patches/riverctl/README.md @@ -0,0 +1,54 @@ +### Description +This patch adds river-control-unstable-v1 support to dwl. +This protocol allows sending args to dwl to execute functions via the included dwlctl. +This is used to allow changing rules and binds, at runtime via dwlctl. + +Most of this patch is stored in river-control.h, It contains a list of functions which can be called via dwlctl so adding support for new functions is as simple as adding them to the list. + +This patch's 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 switched) 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 normal super,shift Return spawn kitty tmux + +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 + +dwlctl rule-add -appid steam -title steam float tags $((1 << 2)) + +dwlctl rule-add -appid kitty float + +dwlctl rule-add -title firefox float + +dwlctl spawn kitty tmux + +dwlctl setlayout 2 + +dwlctl togglefullscreen +``` + +### Download +- [git branch](/zuki/dwl-ctl/src/main) +- [0.7](/dwl/dwl-patches/raw/branch/main/patches/riverctl/riverctl.patch) +- [main 2025-07-29](/dwl/dwl-patches/raw/branch/main/patches/riverctl/riverctl.patch) +### Authors - latest at top +- [zuki](https://codeberg.org/zuki) + zukirust@gmail.com + zuki at [Libera IRC dwl channel](https://web.libera.chat/?channels=#dwl) + #zukigay at [dwl Discord](https://discord.gg/jJxZnrGPWN) diff --git a/patches/riverctl/riverctl.patch b/patches/riverctl/riverctl.patch new file mode 100644 index 0000000..ab93018 --- /dev/null +++ b/patches/riverctl/riverctl.patch @@ -0,0 +1,1241 @@ +From 2cb4d057e471059f1ad5bc9911ce055c55f67cf4 Mon Sep 17 00:00:00 2001 +From: Zuki Air +Date: Thu, 7 Aug 2025 13:19:59 +0100 +Subject: [PATCH] riverctl patch + +--- + .gitignore | 1 + + Makefile | 22 +- + config.def.h | 36 +- + dwl.c | 47 +- + dwlctl.c | 132 +++++ + protocols/river-control-unstable-v1.xml | 85 +++ + 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 + +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..72afbd6 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -6,7 +6,7 @@ + /* appearance */ + static const int sloppyfocus = 1; /* focus follows mouse */ + static const int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */ +-static const unsigned int borderpx = 1; /* border pixel of windows */ ++static unsigned int borderpx = 1; /* border pixel of windows */ + static const float rootcolor[] = COLOR(0x222222ff); + static const float bordercolor[] = COLOR(0x444444ff); + static const float focuscolor[] = COLOR(0x005577ff); +@@ -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..a064dcf 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; +@@ -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; ++ Mode *new_mode_if_oneshot = active_mode->oneshot_mode; ++ ++ wl_list_for_each(kl,&active_mode->linked_keys,link) { ++ k = kl->key; ++ 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,8 @@ setup(void) + wl_signal_add(&output_mgr->events.apply, &output_mgr_apply); + wl_signal_add(&output_mgr->events.test, &output_mgr_test); + ++ 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 +3212,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..59561b6 +--- /dev/null ++++ b/river-control.h +@@ -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(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*); ++struct wl_list arg_str_store; ++struct wl_list rule_str_store; ++struct wl_list rules_list; ++struct wl_list modes_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; ++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; ++}; ++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[] = { ++ { 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), ++ 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"}, ++ {WLR_MODIFIER_CAPS,"caps"}, ++ {WLR_MODIFIER_MOD3,"mod3"}, ++ {WLR_MODIFIER_MOD2,"mod2"}, ++ {WLR_MODIFIER_MOD5,"mod5"}, ++}; ++ ++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!"; ++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; ++ 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; ++ Mode *key_mode; ++ bool rule_valid; ++ bool error; ++ const char* error_msg; ++}; ++ ++void setborderpx(const Arg *arg) { ++ Client *c; ++ borderpx = arg->ui; ++ wl_list_for_each(c, &clients, link) { ++ c->bw = borderpx; ++ } ++} ++ ++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) { ++ 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(const Arg* arg) { ++ 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); ++} ++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; ++ } ++ 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(struct wl_list *keys_list) { ++ 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(const Arg* arg) { ++ Key_linked *kl,*tmp_kl; ++ Mode *mode,*tmp_mode; ++ active_mode = normal_mode; ++ ++ 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); ++ } ++ } ++ 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, ++ struct wl_resource *resource, ++ const char *argument) { ++ struct zriver_arg_list_resource *args = wl_resource_get_user_data(resource); ++ 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 { ++ 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) { ++ 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) { ++ 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 { ++ for (fst = Func_str_type_pair_list; fst < END(Func_str_type_pair_list); fst++) { ++ if (strcmp(argument,fst->func_str) == 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->p.fa->func = fst->func; ++ args->key_arg_type = fst->arg_type; ++ } ++ break; ++ } ++ } ++ if (args->error != true && args->type != ZRIVER_ARG_TYPE_FUNC) { ++ 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 { ++ args->error = true; ++ args->error_msg = zriver_error_double_appid; ++ } ++ 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 { ++ args->error = true; ++ args->error_msg = zriver_error_double_title; ++ } ++ 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 { ++ 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) { ++ arg_filter = true; ++ arg = &args->p.fa->arg; ++ } else if (args->argc > 1 && 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-1); ++ } ++ } else if (args->type == ZRIVER_ARG_TYPE_KEY) { ++ 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) { ++ args->p.kl->key->mod = args->p.kl->key->mod|ms->mod; ++ } else { ++ args->p.kl->key->mod = ms->mod; ++ } ++ } ++ } ++ } 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; ++ args->key_arg_type = fst->arg_type; ++ break; ++ } ++ } ++ } else if (args->argc == 5) { ++ arg_filter = true; ++ arg = &args->p.kl->key->arg; ++ } 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) { ++ switch (args->key_arg_type) { ++ case(FUNC_STR_ARG_TYPE_NONE): ++ break; ++ case(FUNC_STR_ARG_TYPE_UINT): ++ arg->i = strtol(argument,NULL,10); ++ if (arg->i >= 0) { ++ arg->ui = arg->i; ++ } else { ++ args->error = true; ++ args->error_msg = zriver_error_under_zero; ++ } ++ 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; ++ } ++ ++ 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 { ++ 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 < (int)LENGTH(layouts)) { ++ arg->v = &layouts[arg->ui]; ++ } else { ++ 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) { ++ switch (args->type) { ++ case(ZRIVER_ARG_TYPE_KEY): ++ if (args->p.kl != NULL) { ++ if (args->p.kl->key != NULL) { ++ free(args->p.kl->key); ++ } ++ free(args->p.kl); ++ } ++ break; ++ case(ZRIVER_ARG_TYPE_FUNC): ++ if (args->p.fa != NULL) { ++ free(args->p.fa); ++ } ++ break; ++ case(ZRIVER_ARG_TYPE_RULE): ++ if (args->p.rl != NULL) { ++ if (args->p.rl->rule != NULL) { ++ free(args->p.rl->rule); ++ } ++ free(args->p.rl); ++ } ++ break; ++ case(ZRIVER_ARG_TYPE_NONE): ++ break; ++ } ++ if (args->str_link != NULL) { ++ free_str_store(args->str_link); ++ free(args->str_link); ++ } ++ 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(&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) { ++ 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 +