summaryrefslogtreecommitdiffstats
path: root/dwl-patches/patches/hot-reload/hot-reload.patch
diff options
context:
space:
mode:
Diffstat (limited to 'dwl-patches/patches/hot-reload/hot-reload.patch')
-rw-r--r--dwl-patches/patches/hot-reload/hot-reload.patch1009
1 files changed, 1009 insertions, 0 deletions
diff --git a/dwl-patches/patches/hot-reload/hot-reload.patch b/dwl-patches/patches/hot-reload/hot-reload.patch
new file mode 100644
index 0000000..84ad9f5
--- /dev/null
+++ b/dwl-patches/patches/hot-reload/hot-reload.patch
@@ -0,0 +1,1009 @@
+From 79fbc2405919a049a35dd58e860b6519ebb7943b Mon Sep 17 00:00:00 2001
+From: Sivecano <sivecano@gmail.com>
+Date: Sun, 26 Jan 2025 18:30:02 +0100
+Subject: [PATCH] redo hot-reloading in one file
+
+---
+ Makefile | 19 ++-
+ config.def.h | 5 +-
+ dwl.c | 475 +++++++++++++++++++++++++++++++++++++++------------
+ util.c | 34 ++++
+ util.h | 6 +
+ 5 files changed, 421 insertions(+), 118 deletions(-)
+
+diff --git a/Makefile b/Makefile
+index 578194f..0714ed1 100644
+--- a/Makefile
++++ b/Makefile
+@@ -13,13 +13,16 @@ DWLDEVCFLAGS = -g -Wpedantic -Wall -Wextra -Wdeclaration-after-statement \
+
+ # CFLAGS / LDFLAGS
+ PKGS = wayland-server xkbcommon libinput $(XLIBS)
+-DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(WLR_INCS) $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS)
++DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(WLR_INCS) $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS) -fPIC -rdynamic
+ LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(WLR_LIBS) -lm $(LIBS)
+
+-all: dwl
++all: dwl dwl.so
+ 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 \
++dwl.o: dwl.c 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
++dwl.so: 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
+ util.o: util.c util.h
+@@ -49,7 +52,7 @@ xdg-shell-protocol.h:
+ config.h:
+ cp config.def.h $@
+ clean:
+- rm -f dwl *.o *-protocol.h
++ rm -f dwl *.o *-protocol.h *.so
+
+ dist: clean
+ mkdir -p dwl-$(VERSION)
+@@ -64,6 +67,8 @@ install: dwl
+ rm -f $(DESTDIR)$(PREFIX)/bin/dwl
+ cp -f dwl $(DESTDIR)$(PREFIX)/bin
+ chmod 755 $(DESTDIR)$(PREFIX)/bin/dwl
++ mkdir -p $(DESTDIR)$(PREFIX)/lib
++ install -m 744 dwl.so $(DESTDIR)$(PREFIX)/lib
+ mkdir -p $(DESTDIR)$(MANDIR)/man1
+ cp -f dwl.1 $(DESTDIR)$(MANDIR)/man1
+ chmod 644 $(DESTDIR)$(MANDIR)/man1/dwl.1
+@@ -71,9 +76,13 @@ install: dwl
+ cp -f dwl.desktop $(DESTDIR)$(DATADIR)/wayland-sessions/dwl.desktop
+ chmod 644 $(DESTDIR)$(DATADIR)/wayland-sessions/dwl.desktop
+ uninstall:
+- rm -f $(DESTDIR)$(PREFIX)/bin/dwl $(DESTDIR)$(MANDIR)/man1/dwl.1 \
++ rm -f $(DESTDIR)$(PREFIX)/bin/dwl $(DESTDIR)$(PREFIX)/lib/dwl.so $(DESTDIR)$(MANDIR)/man1/dwl.1 \
+ $(DESTDIR)$(DATADIR)/wayland-sessions/dwl.desktop
+
+ .SUFFIXES: .c .o
+ .c.o:
+ $(CC) $(CPPFLAGS) $(DWLCFLAGS) -o $@ -c $<
++
++.SUFFIXES: .c .so
++.c.so:
++ $(CC) $(CPPFLAGS) $(DWLCFLAGS) -o $@ -shared -DHOT $<
+diff --git a/config.def.h b/config.def.h
+index 22d2171..6e3dda1 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -7,7 +7,7 @@
+ 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 const float rootcolor[] = COLOR(0x222222ff);
++const float rootcolor[] = COLOR(0x222222ff);
+ static const float bordercolor[] = COLOR(0x444444ff);
+ static const float focuscolor[] = COLOR(0x005577ff);
+ static const float urgentcolor[] = COLOR(0xff0000ff);
+@@ -18,7 +18,7 @@ static const float fullscreen_bg[] = {0.1f, 0.1f, 0.1f, 1.0f}; /* You ca
+ #define TAGCOUNT (9)
+
+ /* logging */
+-static int log_level = WLR_ERROR;
++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[] = {
+@@ -127,6 +127,7 @@ static const Key keys[] = {
+ /* modifier key function argument */
+ { MODKEY, XKB_KEY_p, spawn, {.v = menucmd} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} },
++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_R, reload, {0} },
+ { MODKEY, XKB_KEY_j, focusstack, {.i = +1} },
+ { MODKEY, XKB_KEY_k, focusstack, {.i = -1} },
+ { MODKEY, XKB_KEY_i, incnmaster, {.i = +1} },
+diff --git a/dwl.c b/dwl.c
+index ec4ca86..8d1eceb 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -1,6 +1,15 @@
+ /*
+ * See LICENSE file for copyright and license details.
+ */
++
++/* stuff for hot-reload */
++#define _GNU_SOURCE
++#include <dlfcn.h>
++#include <limits.h>
++#include <unistd.h>
++#include <libgen.h>
++#include <errno.h>
++
+ #include <getopt.h>
+ #include <libinput.h>
+ #include <linux/input-event-codes.h>
+@@ -68,6 +77,7 @@
+ #include <xcb/xcb_icccm.h>
+ #endif
+
++
+ #include "util.h"
+
+ /* macros */
+@@ -78,8 +88,33 @@
+ #define LENGTH(X) (sizeof X / sizeof X[0])
+ #define END(A) ((A) + LENGTH(A))
+ #define TAGMASK ((1u << TAGCOUNT) - 1)
+-#define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L)))
+-#define LISTEN_STATIC(E, H) do { struct wl_listener *_l = ecalloc(1, sizeof(*_l)); _l->notify = (H); wl_signal_add((E), _l); } while (0)
++#define SYM(a) dlsym(dwl_module, #a)
++#define TSYM(T, a) ((T)SYM(a))
++#define CSYM(T, a) *(TSYM(T*, a))
++
++#define LISTEN(E, L, H) do { \
++ (L)->notify = SYM(H); \
++ listeners = append_listener((L), listeners); \
++ wl_signal_add((E), (L)); \
++ } while(0)
++
++#define LISTEN_GLOBAL(E, L) do { \
++ struct wl_listener* l = SYM(L); \
++ listeners = append_listener(l, listeners); \
++ wl_signal_add((E), l); \
++ } while (0)
++
++#define LISTEN_STATIC(E, H) do { \
++ struct wl_listener* _l = ecalloc(1, sizeof(struct wl_listener)); \
++ _l->notify = SYM(H); \
++ listeners = append_listener(_l, listeners); \
++ wl_signal_add((E), _l); \
++ } while (0)
++
++#define UNLISTEN(L) do { \
++ wl_list_remove(&(L)->link); \
++ listeners = remove_listener((L), listeners);\
++ } while (0)
+
+ /* enums */
+ enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */
+@@ -239,6 +274,9 @@ typedef struct {
+ struct wl_listener destroy;
+ } SessionLock;
+
++#define static
++
++#ifdef HOT
+ /* function declarations */
+ static void applybounds(Client *c, struct wlr_box *bbox);
+ static void applyrules(Client *c);
+@@ -250,7 +288,18 @@ static void axisnotify(struct wl_listener *listener, void *data);
+ static void buttonpress(struct wl_listener *listener, void *data);
+ static void chvt(const Arg *arg);
+ static void checkidleinhibitor(struct wlr_surface *exclude);
++
++#undef static
++#define static extern
++#endif
++
++/* this is cold */
+ static void cleanup(void);
++
++#undef static
++#define static
++#ifdef HOT
++
+ static void cleanupmon(struct wl_listener *listener, void *data);
+ static void cleanuplisteners(void);
+ static void closemon(Monitor *m);
+@@ -318,7 +367,18 @@ static void requestdecorationmode(struct wl_listener *listener, void *data);
+ static void requeststartdrag(struct wl_listener *listener, void *data);
+ static void requestmonstate(struct wl_listener *listener, void *data);
+ static void resize(Client *c, struct wlr_box geo, int interact);
++
++#undef static
++#define static extern
++#endif
++
++/* this is cold */
+ static void run(char *startup_cmd);
++
++#ifdef HOT
++#undef static
++#define static
++
+ static void setcursor(struct wl_listener *listener, void *data);
+ static void setcursorshape(struct wl_listener *listener, void *data);
+ static void setfloating(Client *c, int floating);
+@@ -328,7 +388,18 @@ static void setmfact(const Arg *arg);
+ static void setmon(Client *c, Monitor *m, uint32_t newtags);
+ static void setpsel(struct wl_listener *listener, void *data);
+ static void setsel(struct wl_listener *listener, void *data);
++
++#undef static
++#define static extern
++#endif
++
++/* this is cold */
+ static void setup(void);
++
++#ifdef HOT
++#undef static
++#define static
++
+ static void spawn(const Arg *arg);
+ static void startdrag(struct wl_listener *listener, void *data);
+ static void tag(const Arg *arg);
+@@ -352,6 +423,16 @@ static void xytonode(double x, double y, struct wlr_surface **psurface,
+ Client **pc, LayerSurface **pl, double *nx, double *ny);
+ static void zoom(const Arg *arg);
+
++#endif
++
++#ifdef HOT
++ #undef static
++ #define static extern
++#else
++ #undef static
++ #define static
++#endif
++
+ /* variables */
+ static pid_t child_pid = -1;
+ static int locked;
+@@ -406,6 +487,10 @@ static struct wlr_box sgeom;
+ static struct wl_list mons;
+ static Monitor *selmon;
+
++#ifdef HOT
++#undef static
++#define static
++
+ /* global event handlers */
+ static struct wl_listener cursor_axis = {.notify = axisnotify};
+ static struct wl_listener cursor_button = {.notify = buttonpress};
+@@ -429,6 +514,7 @@ static struct wl_listener output_mgr_test = {.notify = outputmgrtest};
+ static struct wl_listener output_power_mgr_set_mode = {.notify = powermgrsetmode};
+ static struct wl_listener request_activate = {.notify = urgent};
+ static struct wl_listener request_cursor = {.notify = setcursor};
++
+ static struct wl_listener request_set_psel = {.notify = setpsel};
+ static struct wl_listener request_set_sel = {.notify = setsel};
+ static struct wl_listener request_set_cursor_shape = {.notify = setcursorshape};
+@@ -449,8 +535,38 @@ static struct wl_listener xwayland_ready = {.notify = xwaylandready};
+ static struct wlr_xwayland *xwayland;
+ #endif
+
++/* undoes the shadowing of static from above */
++#endif
++#undef static
++
++/* this is where we put global hot-reload state */
++#ifdef HOT
++#define COLD extern
++#else
++#define COLD
++
++static void* load(void);
++static const char* get_module_path(void);
++
++#endif
++
++COLD void * dwl_module = NULL;
++COLD void * last_module = NULL;
++COLD struct listens* listeners = NULL;
++COLD void reload(const Arg* arg);
++
++#ifndef HOT
++static char* runpath;
++
++#endif
++
++
++#ifdef HOT
++
++#define static
+ /* configuration, allows nested code to access above variables */
+ #include "config.h"
++#undef static
+
+ /* attempt to encapsulate suck into one file */
+ #include "client.h"
+@@ -692,10 +808,12 @@ checkidleinhibitor(struct wlr_surface *exclude)
+ wlr_idle_notifier_v1_set_inhibited(idle_notifier, inhibited);
+ }
+
++#endif
++
+ void
+ cleanup(void)
+ {
+- cleanuplisteners();
++ TSYM(void (*)(void), cleanuplisteners)();
+ #ifdef XWAYLAND
+ wlr_xwayland_destroy(xwayland);
+ xwayland = NULL;
+@@ -707,7 +825,7 @@ cleanup(void)
+ }
+ wlr_xcursor_manager_destroy(cursor_mgr);
+
+- destroykeyboardgroup(&kb_group->destroy, NULL);
++ TSYM(void (*)(struct wl_listener*, void*), destroykeyboardgroup)(&kb_group->destroy, NULL);
+
+ /* If it's not destroyed manually it will cause a use-after-free of wlr_seat.
+ * Destroy it until it's fixed in the wlroots side */
+@@ -719,6 +837,8 @@ cleanup(void)
+ wlr_scene_node_destroy(&scene->tree.node);
+ }
+
++#ifdef HOT
++
+ void
+ cleanupmon(struct wl_listener *listener, void *data)
+ {
+@@ -732,10 +852,10 @@ cleanupmon(struct wl_listener *listener, void *data)
+ wlr_layer_surface_v1_destroy(l->layer_surface);
+ }
+
+- wl_list_remove(&m->destroy.link);
+- wl_list_remove(&m->frame.link);
++ UNLISTEN(&m->destroy);
++ UNLISTEN(&m->frame);
+ wl_list_remove(&m->link);
+- wl_list_remove(&m->request_state.link);
++ UNLISTEN(&m->request_state);
+ m->wlr_output->data = NULL;
+ wlr_output_layout_remove(output_layout, m->wlr_output);
+ wlr_scene_output_destroy(m->scene_output);
+@@ -748,37 +868,37 @@ cleanupmon(struct wl_listener *listener, void *data)
+ void
+ cleanuplisteners(void)
+ {
+- wl_list_remove(&cursor_axis.link);
+- wl_list_remove(&cursor_button.link);
+- wl_list_remove(&cursor_frame.link);
+- wl_list_remove(&cursor_motion.link);
+- wl_list_remove(&cursor_motion_absolute.link);
+- wl_list_remove(&gpu_reset.link);
+- wl_list_remove(&new_idle_inhibitor.link);
+- wl_list_remove(&layout_change.link);
+- wl_list_remove(&new_input_device.link);
+- wl_list_remove(&new_virtual_keyboard.link);
+- wl_list_remove(&new_virtual_pointer.link);
+- wl_list_remove(&new_pointer_constraint.link);
+- wl_list_remove(&new_output.link);
+- wl_list_remove(&new_xdg_toplevel.link);
+- wl_list_remove(&new_xdg_decoration.link);
+- wl_list_remove(&new_xdg_popup.link);
+- wl_list_remove(&new_layer_surface.link);
+- wl_list_remove(&output_mgr_apply.link);
+- wl_list_remove(&output_mgr_test.link);
+- wl_list_remove(&output_power_mgr_set_mode.link);
+- wl_list_remove(&request_activate.link);
+- wl_list_remove(&request_cursor.link);
+- wl_list_remove(&request_set_psel.link);
+- wl_list_remove(&request_set_sel.link);
+- wl_list_remove(&request_set_cursor_shape.link);
+- wl_list_remove(&request_start_drag.link);
+- wl_list_remove(&start_drag.link);
+- wl_list_remove(&new_session_lock.link);
++ UNLISTEN(&cursor_axis);
++ UNLISTEN(&cursor_button);
++ UNLISTEN(&cursor_frame);
++ UNLISTEN(&cursor_motion);
++ UNLISTEN(&cursor_motion_absolute);
++ UNLISTEN(&gpu_reset);
++ UNLISTEN(&new_idle_inhibitor);
++ UNLISTEN(&layout_change);
++ UNLISTEN(&new_input_device);
++ UNLISTEN(&new_virtual_keyboard);
++ UNLISTEN(&new_virtual_pointer);
++ UNLISTEN(&new_pointer_constraint);
++ UNLISTEN(&new_output);
++ UNLISTEN(&new_xdg_toplevel);
++ UNLISTEN(&new_xdg_decoration);
++ UNLISTEN(&new_xdg_popup);
++ UNLISTEN(&new_layer_surface);
++ UNLISTEN(&output_mgr_apply);
++ UNLISTEN(&output_mgr_test);
++ UNLISTEN(&output_power_mgr_set_mode);
++ UNLISTEN(&request_activate);
++ UNLISTEN(&request_cursor);
++ UNLISTEN(&request_set_psel);
++ UNLISTEN(&request_set_sel);
++ UNLISTEN(&request_set_cursor_shape);
++ UNLISTEN(&request_start_drag);
++ UNLISTEN(&start_drag);
++ UNLISTEN(&new_session_lock);
+ #ifdef XWAYLAND
+- wl_list_remove(&new_xwayland_surface.link);
+- wl_list_remove(&xwayland_ready.link);
++ UNLISTEN(&new_xwayland_surface);
++ UNLISTEN(&xwayland_ready);
+ #endif
+ }
+
+@@ -905,8 +1025,7 @@ commitpopup(struct wl_listener *listener, void *data)
+ box.x -= (type == LayerShell ? l->scene->node.x : c->geom.x);
+ box.y -= (type == LayerShell ? l->scene->node.y : c->geom.y);
+ wlr_xdg_popup_unconstrain_from_box(popup, &box);
+- wl_list_remove(&listener->link);
+- free(listener);
++ UNLISTEN(listener);
+ }
+
+ void
+@@ -1236,8 +1355,8 @@ destroydecoration(struct wl_listener *listener, void *data)
+ {
+ Client *c = wl_container_of(listener, c, destroy_decoration);
+
+- wl_list_remove(&c->destroy_decoration.link);
+- wl_list_remove(&c->set_decoration_mode.link);
++ UNLISTEN(&c->destroy_decoration);
++ UNLISTEN(&c->set_decoration_mode);
+ }
+
+ void
+@@ -1246,8 +1365,7 @@ destroydragicon(struct wl_listener *listener, void *data)
+ /* Focus enter isn't sent during drag, so refocus the focused node. */
+ focusclient(focustop(selmon), 1);
+ motionnotify(0, NULL, 0, 0, 0, 0);
+- wl_list_remove(&listener->link);
+- free(listener);
++ UNLISTEN(listener);
+ }
+
+ void
+@@ -1256,8 +1374,7 @@ destroyidleinhibitor(struct wl_listener *listener, void *data)
+ /* `data` is the wlr_surface of the idle inhibitor being destroyed,
+ * at this point the idle inhibitor is still in the list of the manager */
+ checkidleinhibitor(wlr_surface_get_root_surface(data));
+- wl_list_remove(&listener->link);
+- free(listener);
++ UNLISTEN(listener);
+ }
+
+ void
+@@ -1266,9 +1383,9 @@ destroylayersurfacenotify(struct wl_listener *listener, void *data)
+ LayerSurface *l = wl_container_of(listener, l, destroy);
+
+ wl_list_remove(&l->link);
+- wl_list_remove(&l->destroy.link);
+- wl_list_remove(&l->unmap.link);
+- wl_list_remove(&l->surface_commit.link);
++ UNLISTEN(&l->destroy);
++ UNLISTEN(&l->unmap);
++ UNLISTEN(&l->surface_commit);
+ wlr_scene_node_destroy(&l->scene->node);
+ wlr_scene_node_destroy(&l->popups->node);
+ free(l);
+@@ -1287,9 +1404,9 @@ destroylock(SessionLock *lock, int unlock)
+ motionnotify(0, NULL, 0, 0, 0, 0);
+
+ destroy:
+- wl_list_remove(&lock->new_surface.link);
+- wl_list_remove(&lock->unlock.link);
+- wl_list_remove(&lock->destroy.link);
++ UNLISTEN(&lock->new_surface);
++ UNLISTEN(&lock->unlock);
++ UNLISTEN(&lock->destroy);
+
+ wlr_scene_node_destroy(&lock->scene->node);
+ cur_lock = NULL;
+@@ -1303,7 +1420,7 @@ destroylocksurface(struct wl_listener *listener, void *data)
+ struct wlr_session_lock_surface_v1 *surface, *lock_surface = m->lock_surface;
+
+ m->lock_surface = NULL;
+- wl_list_remove(&m->destroy_lock_surface.link);
++ UNLISTEN(&m->destroy_lock_surface);
+
+ if (lock_surface->surface != seat->keyboard_state.focused_surface)
+ return;
+@@ -1323,23 +1440,23 @@ destroynotify(struct wl_listener *listener, void *data)
+ {
+ /* Called when the xdg_toplevel is destroyed. */
+ Client *c = wl_container_of(listener, c, destroy);
+- wl_list_remove(&c->destroy.link);
+- wl_list_remove(&c->set_title.link);
+- wl_list_remove(&c->fullscreen.link);
++ UNLISTEN(&c->destroy);
++ UNLISTEN(&c->set_title);
++ UNLISTEN(&c->fullscreen);
+ #ifdef XWAYLAND
+ if (c->type != XDGShell) {
+- wl_list_remove(&c->activate.link);
+- wl_list_remove(&c->associate.link);
+- wl_list_remove(&c->configure.link);
+- wl_list_remove(&c->dissociate.link);
+- wl_list_remove(&c->set_hints.link);
++ UNLISTEN(&c->activate);
++ UNLISTEN(&c->associate);
++ UNLISTEN(&c->configure);
++ UNLISTEN(&c->dissociate);
++ UNLISTEN(&c->set_hints);
+ } else
+ #endif
+ {
+- wl_list_remove(&c->commit.link);
+- wl_list_remove(&c->map.link);
+- wl_list_remove(&c->unmap.link);
+- wl_list_remove(&c->maximize.link);
++ UNLISTEN(&c->commit);
++ UNLISTEN(&c->map);
++ UNLISTEN(&c->unmap);
++ UNLISTEN(&c->maximize);
+ }
+ free(c);
+ }
+@@ -1354,7 +1471,7 @@ destroypointerconstraint(struct wl_listener *listener, void *data)
+ active_constraint = NULL;
+ }
+
+- wl_list_remove(&pointer_constraint->destroy.link);
++ UNLISTEN(&pointer_constraint->destroy);
+ free(pointer_constraint);
+ }
+
+@@ -1370,9 +1487,9 @@ destroykeyboardgroup(struct wl_listener *listener, void *data)
+ {
+ KeyboardGroup *group = wl_container_of(listener, group, destroy);
+ wl_event_source_remove(group->key_repeat_source);
+- wl_list_remove(&group->key.link);
+- wl_list_remove(&group->modifiers.link);
+- wl_list_remove(&group->destroy.link);
++ UNLISTEN(&group->key);
++ UNLISTEN(&group->modifiers);
++ UNLISTEN(&group->destroy);
+ wlr_keyboard_group_destroy(group->wlr_group);
+ free(group);
+ }
+@@ -1538,8 +1655,8 @@ gpureset(struct wl_listener *listener, void *data)
+ if (!(alloc = wlr_allocator_autocreate(backend, drw)))
+ die("couldn't recreate allocator");
+
+- wl_list_remove(&gpu_reset.link);
+- wl_signal_add(&drw->events.lost, &gpu_reset);
++ UNLISTEN(&gpu_reset);
++ LISTEN_GLOBAL(&drw->events.lost, gpu_reset);
+
+ wlr_compositor_set_renderer(compositor, drw);
+
+@@ -2229,6 +2346,8 @@ resize(Client *c, struct wlr_box geo, int interact)
+ wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip);
+ }
+
++#else /*HOT*/
++
+ void
+ run(char *startup_cmd)
+ {
+@@ -2268,11 +2387,11 @@ run(char *startup_cmd)
+ if (fd_set_nonblock(STDOUT_FILENO) < 0)
+ close(STDOUT_FILENO);
+
+- printstatus();
++ TSYM(void (*)(void), printstatus)();
+
+ /* At this point the outputs are initialized, choose initial selmon based on
+ * cursor position, and set default cursor image */
+- selmon = xytomon(cursor->x, cursor->y);
++ selmon = TSYM(Monitor* (*)(double x, double y), xytomon)(cursor->x, cursor->y);
+
+ /* TODO hack to get cursor to display in its initial location (100, 100)
+ * instead of (0, 0) and then jumping. still may not be fully
+@@ -2288,6 +2407,9 @@ run(char *startup_cmd)
+ wl_display_run(dpy);
+ }
+
++#endif
++#ifdef HOT
++
+ void
+ setcursor(struct wl_listener *listener, void *data)
+ {
+@@ -2434,17 +2556,19 @@ setsel(struct wl_listener *listener, void *data)
+ wlr_seat_set_selection(seat, event->source, event->serial);
+ }
+
++#else /*HOT*/
++
+ void
+ setup(void)
+ {
+ int drm_fd, i, sig[] = {SIGCHLD, SIGINT, SIGTERM, SIGPIPE};
+- struct sigaction sa = {.sa_flags = SA_RESTART, .sa_handler = handlesig};
++ struct sigaction sa = {.sa_flags = SA_RESTART, .sa_handler = SYM(handlesig)};
+ sigemptyset(&sa.sa_mask);
+
+ for (i = 0; i < (int)LENGTH(sig); i++)
+ sigaction(sig[i], &sa, NULL);
+
+- wlr_log_init(log_level, NULL);
++ wlr_log_init(CSYM(enum wlr_log_importance, log_level), NULL);
+
+ /* The Wayland display is managed by libwayland. It handles accepting
+ * clients from the Unix socket, manging Wayland globals, and so on. */
+@@ -2460,7 +2584,7 @@ setup(void)
+
+ /* Initialize the scene graph used to lay out windows */
+ scene = wlr_scene_create();
+- root_bg = wlr_scene_rect_create(&scene->tree, 0, 0, rootcolor);
++ root_bg = wlr_scene_rect_create(&scene->tree, 0, 0, TSYM(float*, rootcolor));
+ for (i = 0; i < NUM_LAYERS; i++)
+ layers[i] = wlr_scene_tree_create(&scene->tree);
+ drag_icon = wlr_scene_tree_create(&scene->tree);
+@@ -2472,7 +2596,7 @@ setup(void)
+ * supports for shared memory, this configures that for clients. */
+ if (!(drw = wlr_renderer_autocreate(backend)))
+ die("couldn't create renderer");
+- wl_signal_add(&drw->events.lost, &gpu_reset);
++ LISTEN_GLOBAL(&drw->events.lost, gpu_reset);
+
+ /* Create shm, drm and linux_dmabuf interfaces by ourselves.
+ * The simplest way is call:
+@@ -2519,24 +2643,24 @@ setup(void)
+
+ /* Initializes the interface used to implement urgency hints */
+ activation = wlr_xdg_activation_v1_create(dpy);
+- wl_signal_add(&activation->events.request_activate, &request_activate);
++ LISTEN_GLOBAL(&activation->events.request_activate, request_activate);
+
+ wlr_scene_set_gamma_control_manager_v1(scene, wlr_gamma_control_manager_v1_create(dpy));
+
+ power_mgr = wlr_output_power_manager_v1_create(dpy);
+- wl_signal_add(&power_mgr->events.set_mode, &output_power_mgr_set_mode);
++ LISTEN_GLOBAL(&power_mgr->events.set_mode, output_power_mgr_set_mode);
+
+ /* Creates an output layout, which a wlroots utility for working with an
+ * arrangement of screens in a physical layout. */
+ output_layout = wlr_output_layout_create(dpy);
+- wl_signal_add(&output_layout->events.change, &layout_change);
++ LISTEN_GLOBAL(&output_layout->events.change, layout_change);
+
+ wlr_xdg_output_manager_v1_create(dpy, output_layout);
+
+ /* Configure a listener to be notified when new outputs are available on the
+ * backend. */
+ wl_list_init(&mons);
+- wl_signal_add(&backend->events.new_output, &new_output);
++ LISTEN_GLOBAL(&backend->events.new_output, new_output);
+
+ /* Set up our client lists, the xdg-shell and the layer-shell. The xdg-shell is a
+ * Wayland protocol which is used for application windows. For more
+@@ -2548,19 +2672,19 @@ setup(void)
+ wl_list_init(&fstack);
+
+ xdg_shell = wlr_xdg_shell_create(dpy, 6);
+- wl_signal_add(&xdg_shell->events.new_toplevel, &new_xdg_toplevel);
+- wl_signal_add(&xdg_shell->events.new_popup, &new_xdg_popup);
++ LISTEN_GLOBAL(&xdg_shell->events.new_toplevel, new_xdg_toplevel);
++ LISTEN_GLOBAL(&xdg_shell->events.new_popup, new_xdg_popup);
+
+ layer_shell = wlr_layer_shell_v1_create(dpy, 3);
+- wl_signal_add(&layer_shell->events.new_surface, &new_layer_surface);
++ LISTEN_GLOBAL(&layer_shell->events.new_surface, new_layer_surface);
+
+ idle_notifier = wlr_idle_notifier_v1_create(dpy);
+
+ idle_inhibit_mgr = wlr_idle_inhibit_v1_create(dpy);
+- wl_signal_add(&idle_inhibit_mgr->events.new_inhibitor, &new_idle_inhibitor);
++ LISTEN_GLOBAL(&idle_inhibit_mgr->events.new_inhibitor, new_idle_inhibitor);
+
+ session_lock_mgr = wlr_session_lock_manager_v1_create(dpy);
+- wl_signal_add(&session_lock_mgr->events.new_lock, &new_session_lock);
++ LISTEN_GLOBAL(&session_lock_mgr->events.new_lock, new_session_lock);
+ locked_bg = wlr_scene_rect_create(layers[LyrBlock], sgeom.width, sgeom.height,
+ (float [4]){0.1f, 0.1f, 0.1f, 1.0f});
+ wlr_scene_node_set_enabled(&locked_bg->node, 0);
+@@ -2570,10 +2694,10 @@ setup(void)
+ wlr_server_decoration_manager_create(dpy),
+ WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
+ xdg_decoration_mgr = wlr_xdg_decoration_manager_v1_create(dpy);
+- wl_signal_add(&xdg_decoration_mgr->events.new_toplevel_decoration, &new_xdg_decoration);
++ LISTEN_GLOBAL(&xdg_decoration_mgr->events.new_toplevel_decoration, new_xdg_decoration);
+
+ pointer_constraints = wlr_pointer_constraints_v1_create(dpy);
+- wl_signal_add(&pointer_constraints->events.new_constraint, &new_pointer_constraint);
++ LISTEN_GLOBAL(&pointer_constraints->events.new_constraint, new_pointer_constraint);
+
+ relative_pointer_mgr = wlr_relative_pointer_manager_v1_create(dpy);
+
+@@ -2601,14 +2725,14 @@ setup(void)
+ *
+ * And more comments are sprinkled throughout the notify functions above.
+ */
+- wl_signal_add(&cursor->events.motion, &cursor_motion);
+- wl_signal_add(&cursor->events.motion_absolute, &cursor_motion_absolute);
+- wl_signal_add(&cursor->events.button, &cursor_button);
+- wl_signal_add(&cursor->events.axis, &cursor_axis);
+- wl_signal_add(&cursor->events.frame, &cursor_frame);
++ LISTEN_GLOBAL(&cursor->events.motion, cursor_motion);
++ LISTEN_GLOBAL(&cursor->events.motion_absolute, cursor_motion_absolute);
++ LISTEN_GLOBAL(&cursor->events.button, cursor_button);
++ LISTEN_GLOBAL(&cursor->events.axis, cursor_axis);
++ LISTEN_GLOBAL(&cursor->events.frame, cursor_frame);
+
+ cursor_shape_mgr = wlr_cursor_shape_manager_v1_create(dpy, 1);
+- wl_signal_add(&cursor_shape_mgr->events.request_set_shape, &request_set_cursor_shape);
++ LISTEN_GLOBAL(&cursor_shape_mgr->events.request_set_shape, request_set_cursor_shape);
+
+ /*
+ * Configures a seat, which is a single "seat" at which a user sits and
+@@ -2616,27 +2740,27 @@ setup(void)
+ * pointer, touch, and drawing tablet device. We also rig up a listener to
+ * let us know when new input devices are available on the backend.
+ */
+- wl_signal_add(&backend->events.new_input, &new_input_device);
++ LISTEN_GLOBAL(&backend->events.new_input, new_input_device);
+ virtual_keyboard_mgr = wlr_virtual_keyboard_manager_v1_create(dpy);
+- wl_signal_add(&virtual_keyboard_mgr->events.new_virtual_keyboard,
+- &new_virtual_keyboard);
++ LISTEN_GLOBAL(&virtual_keyboard_mgr->events.new_virtual_keyboard,
++ new_virtual_keyboard);
+ virtual_pointer_mgr = wlr_virtual_pointer_manager_v1_create(dpy);
+- wl_signal_add(&virtual_pointer_mgr->events.new_virtual_pointer,
+- &new_virtual_pointer);
++ LISTEN_GLOBAL(&virtual_pointer_mgr->events.new_virtual_pointer,
++ new_virtual_pointer);
+
+ seat = wlr_seat_create(dpy, "seat0");
+- wl_signal_add(&seat->events.request_set_cursor, &request_cursor);
+- wl_signal_add(&seat->events.request_set_selection, &request_set_sel);
+- wl_signal_add(&seat->events.request_set_primary_selection, &request_set_psel);
+- wl_signal_add(&seat->events.request_start_drag, &request_start_drag);
+- wl_signal_add(&seat->events.start_drag, &start_drag);
++ LISTEN_GLOBAL(&seat->events.request_set_cursor, request_cursor);
++ LISTEN_GLOBAL(&seat->events.request_set_selection, request_set_sel);
++ LISTEN_GLOBAL(&seat->events.request_set_primary_selection, request_set_psel);
++ LISTEN_GLOBAL(&seat->events.request_start_drag, request_start_drag);
++ LISTEN_GLOBAL(&seat->events.start_drag, start_drag);
+
+- kb_group = createkeyboardgroup();
++ kb_group = TSYM(KeyboardGroup *(*)(void), createkeyboardgroup)();
+ wl_list_init(&kb_group->destroy.link);
+
+ output_mgr = wlr_output_manager_v1_create(dpy);
+- wl_signal_add(&output_mgr->events.apply, &output_mgr_apply);
+- wl_signal_add(&output_mgr->events.test, &output_mgr_test);
++ LISTEN_GLOBAL(&output_mgr->events.apply, output_mgr_apply);
++ LISTEN_GLOBAL(&output_mgr->events.test, output_mgr_test);
+
+ /* 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
+@@ -2648,8 +2772,8 @@ setup(void)
+ * It will be started when the first X client is started.
+ */
+ if ((xwayland = wlr_xwayland_create(dpy, compositor, 1))) {
+- wl_signal_add(&xwayland->events.ready, &xwayland_ready);
+- wl_signal_add(&xwayland->events.new_surface, &new_xwayland_surface);
++ LISTEN_GLOBAL(&xwayland->events.ready, xwayland_ready);
++ LISTEN_GLOBAL(&xwayland->events.new_surface, new_xwayland_surface);
+
+ setenv("DISPLAY", xwayland->display_name, 1);
+ } else {
+@@ -2658,6 +2782,9 @@ setup(void)
+ #endif
+ }
+
++#endif
++#ifdef HOT
++
+ void
+ spawn(const Arg *arg)
+ {
+@@ -3139,8 +3266,8 @@ void
+ dissociatex11(struct wl_listener *listener, void *data)
+ {
+ Client *c = wl_container_of(listener, c, dissociate);
+- wl_list_remove(&c->map.link);
+- wl_list_remove(&c->unmap.link);
++ UNLISTEN(&c->map);
++ UNLISTEN(&c->unmap);
+ }
+
+ void
+@@ -3175,17 +3302,141 @@ xwaylandready(struct wl_listener *listener, void *data)
+ }
+ #endif
+
++#else /* HOT */
++void*
++load(void)
++{
++ const char* path = get_module_path();
++ char load[PATH_MAX] = "/tmp/dwl.soXXXXXX";
++ void* new;
++
++ if (!path) {
++ fprintf(stderr, "cannot find dwl.so\n");
++ }
++
++ do {
++ mktemp(load);
++ errno = 0;
++ symlink(path, load);
++ } while(errno == EEXIST);
++
++ new = dlopen(load, RTLD_NOW|RTLD_LOCAL);
++
++ unlink(load);
++ if (new == NULL)
++ fprintf(stderr, "error while loading %s: %s\n", path, dlerror());
++ else
++ printf("loaded: %s\n", path);
++
++ return new;
++}
++
++const char *
++get_module_path(void) {
++ char home[PATH_MAX];
++ strcpy(home, getenv("HOME"));
++ strcat(home, "/.local/lib");
++ const char* abspaths[] = {".", home, "/usr/share/lib", "/usr/local/lib", "/usr/local/share/lib"};
++ const char* relpaths[] = {"", "/../lib"};
++ char paths[LENGTH(abspaths) + LENGTH(relpaths)][PATH_MAX];
++ static char out[PATH_MAX] = "./";
++
++ for (size_t i = 0; i < LENGTH(abspaths); i++)
++ realpath(abspaths[i], paths[i]);
++
++ for (size_t i = 0; i < LENGTH(relpaths); i++)
++ {
++ char tmp[PATH_MAX];
++ strcpy(tmp, runpath);
++ strcat(tmp, relpaths[i]);
++ realpath(tmp, paths[LENGTH(abspaths) + i]);
++ }
++
++
++
++ for (size_t i = 0; i < LENGTH(paths); i++)
++ {
++ char tmp[PATH_MAX];
++ printf("checking path: %s\n", paths[i]);
++ strcpy(tmp, paths[i]);
++ strcat(tmp, "/dwl.so");
++ if (access(tmp, F_OK|R_OK) == 0)
++ {
++ strcpy(out, tmp);
++ return out;
++ }
++ }
++
++ return NULL;
++}
++
++void
++reload(const Arg* arg)
++{
++ char* error;
++ void* new;
++ size_t i = 0;
++
++ // deinitialize previous module
++ if (last_module) {
++ // dlclose(last_module);
++ last_module = NULL;
++ }
++
++ wlr_log(WLR_INFO, "reloading");
++
++ new = load();
++
++ if (new == NULL)
++ {
++ wlr_log(WLR_ERROR, "couldn't load new dwl module from %s", get_module_path());
++
++ if (fork() == 0)
++ execl("/bin/env", "--", "Notify-send", "-u", "low", "failed to reload dwl", NULL);
++ return;
++ }
++
++ wlr_log(WLR_DEBUG, "---------- listens ---------");
++ for(listens* a = listeners; a != NULL; a = a->next)
++ {
++ Dl_info info;
++ void* old = a->listen->notify;
++ dladdr(a->listen->notify, &info);
++ a->listen->notify = dlsym(new, info.dli_sname);
++ if ((error = dlerror()) != NULL){
++ fprintf(stderr, "reload failure: %s", error);
++ a->listen->notify = old;
++ return;
++ }
++ wlr_log(WLR_DEBUG, "replaced listener: %s", info.dli_sname);
++ i++;
++ }
++
++ wlr_log(WLR_DEBUG, "---------- done! ---------");
++ wlr_log(WLR_DEBUG, "replaced %zu listeners", i);
++
++ last_module = dwl_module;
++ dwl_module = new;
++
++ if (fork() == 0)
++ execl("/bin/env", "--", "notify-send", "-u", "low", "reloaded dwl", NULL);
++
++}
++
+ int
+ main(int argc, char *argv[])
+ {
+ char *startup_cmd = NULL;
+ int c;
+
++ runpath = dirname(argv[0]);
++ dwl_module = load();
++
+ while ((c = getopt(argc, argv, "s:hdv")) != -1) {
+ if (c == 's')
+ startup_cmd = optarg;
+ else if (c == 'd')
+- log_level = WLR_DEBUG;
++ CSYM(enum wlr_log_importance, log_level) = WLR_DEBUG;
+ else if (c == 'v')
+ die("dwl " VERSION);
+ else
+@@ -3205,3 +3456,5 @@ main(int argc, char *argv[])
+ usage:
+ die("Usage: %s [-v] [-d] [-s startup command]", argv[0]);
+ }
++
++#endif
+diff --git a/util.c b/util.c
+index b925987..8fb9b77 100644
+--- a/util.c
++++ b/util.c
+@@ -49,3 +49,37 @@ fd_set_nonblock(int fd) {
+
+ return 0;
+ }
++
++struct listens*
++append_listener(struct wl_listener* new, struct listens* list)
++{
++ struct listens* l = malloc(sizeof(struct listens));
++ l->listen = new;
++ l->next = list;
++ return l;
++}
++
++struct listens*
++remove_listener(struct wl_listener* l, struct listens* ls)
++{
++ struct listens* out = ls;
++ struct listens* f = NULL;
++ for(struct listens* last = NULL; ls != NULL; ls = ls->next)
++ {
++ if (ls->listen == l)
++ {
++ if (last != NULL)
++ last->next = ls->next;
++ else
++ out = ls->next;
++
++ f = ls;
++ }
++ else
++ last = ls;
++ }
++
++ free(f);
++
++ return out;
++}
+diff --git a/util.h b/util.h
+index 226980d..11aab34 100644
+--- a/util.h
++++ b/util.h
+@@ -1,5 +1,11 @@
+ /* See LICENSE.dwm file for copyright and license details. */
++typedef struct listens {
++ struct wl_listener* listen;
++ struct listens* next;
++} listens;
+
+ void die(const char *fmt, ...);
+ void *ecalloc(size_t nmemb, size_t size);
+ int fd_set_nonblock(int fd);
++struct listens* append_listener(struct wl_listener* l, struct listens* ls);
++struct listens* remove_listener(struct wl_listener* l, struct listens* ls);
+--
+2.48.1
+