This commit is contained in:
2026-07-12 08:16:17 +03:30
commit 72be1234e1
2027 changed files with 221388 additions and 0 deletions
+110
View File
@@ -0,0 +1,110 @@
### Description
Enables hot-reloading of dwl; meaning almost all logic can be changed at runtime.
This obviously requires some black magic so for now there's a glibc 2.0 or later
dependency to this.
In particular this allows for every option in config.h to be changed at runtime.
#### Reloading
To reload rebuild dwl.so, perhaps reinstall it and then run trigger reload function (bound to Mod+Shift+R by default).
This currently calls `notify-send` in order to inform you of a reloading taking place.
So in short:
1. make changes to `config.h` or `dwl.c`
2. run `make` to rebuild dwl.so
3. run `sudo make install` to reinstall dwl
#### Limitations
Reloading the compositor will replace all functionality except for `main`, `setup`, `run` and the reload logic.
Note that you're responsible yourself for reloading ressources like fonts, which may only get acquired once.
A lot of components of dwl will also only get run on a trigger (the tiling for example).
So not every change will be immediate.
Furthermore, any patch adding more global state to dwl cannot currently be reloaded properly since
we keep state in the cold part. These patches will still work and their functionality will (hopefully) be
reloadable but you will need to restart the compositor once.
#### Notes
##### reduce compile errors
This patch triggers `-Wpedantic` a bunch (I don't think there's a way around this, `dlsym` yields `void*` pointers to functions).
This will show a lot of warnings but cause no errors.
So you may want to disable this compile option in order to get readable compiler output.
##### runtime dependencies
This does depend on you having a notification daemon like `dunst` or `mako` running as well as
having `notify-send` installed in order for the compositor to inform you of the reload.
#### How?
Most of all dwl functionality is moved into a shared object file `dwl.so`, which can be reloaded at runtime.
#### How do I make this work with other patches?
Most patches should already put everything in more or less the correct place but if they don't, then here is
where you learn how to fix it.
The concept itself is quite simple. We compile dwl.c twice once normally and once with the `HOT` macro defined.
The first run will yield the executable and the second will yield a shared object file to be reloaded at runtime.
From the cold part there are some newly available macros:
> symbol names are written as-is, never as string literals
* `TSYM(T, s)` dynamically loads the symbol `s` with type `T` from the shared object file use this if you need to call functions in the cold part (i.e. the `setup` function).
* `CSYM(T, v)` dynamically accesses the value of the symbol `v` of type `T` from the shared object. Use this to query values from config.h for example.
* `LISTEN_GLOBAL(E, L)` is similar to the `LISTEN` macro. `E` is an event and `L` the name of a global
listener. Current implementation is a bit messy and I may fix it if someone bothers me about it.
* `UNLISTEN(L)` takes a listener and unregisteres it. This is important for reloading.
When adding new code there are some considerations to be made. Since dwl decorates all symbols with `static` by default, we cannot access them as-is.
C's macro system is a bit too powerful though and we use this to our advantage. We will repeatedly define and
undefine a macro called `static` in order to replace the `static` keyword inside some sections.
This allows us to do less refactoring and preserve a lot of the original patch compatability since we're only
strategically adding lines. We're tring to be as minimally invasive as we can.
As a general guide:
* global state should be global for the cold part and `extern` in the cold part meaning it should be inside a block like this:
```C
#ifdef HOT
#define static extern
#else
#define static
#endif
... // your global variables go here
#undef static
```
* function declarations should be visible in the hot part but not included in the cold part meaning they should be enclosed like this:
```C
#ifdef HOT
#define static
... // your function declarations go here
#undef static
#endif
```
* static data like the event handler structs in the current `main` branch are a bit more difficult but we will let them reside inside the hot part.
Thus, we enclose them the same way we do functions:
```C
#ifdef HOT
#define static
... // your struct wl_listener event handlers go here
#undef static
#endif
```
* function definitions should go in the hot part, so they need to be inside a big block like this:
```C
#ifdef HOT
... // function definitions here
#endif
* enfore use of the `LISTEN_GLOBAL` and `UNLISTEN` macros (I know this sucks but what can I do, I need to get
access to the callbacks somehow). So you want
* `wl_list_remove(listener.link)` to become `UNLISTEN(listener)` and
* `wl_signal_add(event, global_listener)` to become `LISTEN_GLOBAL(event, global_listener)`.
* Make sure that any patch you're using also uses static everywhere.
* If a patch adds any config variables that are accessed in the cold part (i.e. probably `setup`),
then you'll have to manually remove the `static` keyword from them.
Note that usually you do not have to create the big `#ifdef` blocks yourself.
There is for example already a huge `#ifdef HOT`-delimited codeblock at the bottom
of dwl.c where all the function definitions go.
If you have any troubles, feel free to reach out.
### Download
- [0.7](/dwl/dwl-patches/raw/branch/main/patches/hot-reload/hot-reload-0.7.patch)
- [main 2025-05-30](/dwl/dwl-patches/raw/branch/main/patches/hot-reload/hot-reload.patch)
- find the repo for the patch [here](/Sivecano/dwl/src/branch/hot-reload)
### Authors
- [Sivecano](https://codeberg.org/Sivecano)
- Sérécano at [dwl Discord](https://discord.gg/jJxZnrGPWN)
+770
View File
@@ -0,0 +1,770 @@
From 559c635056f23d55df3f83c12d1201a7328f648f 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 | 337 ++++++++++++++++++++++++++++++++++++++++++++-------
util.c | 34 ++++++
util.h | 6 +
5 files changed, 351 insertions(+), 50 deletions(-)
diff --git a/Makefile b/Makefile
index 3358bae..e7ee9ff 100644
--- a/Makefile
+++ b/Makefile
@@ -13,13 +13,16 @@ DWLDEVCFLAGS = -g -pedantic -Wall -Wextra -Wdeclaration-after-statement \
# CFLAGS / LDFLAGS
PKGS = wlroots-0.18 wayland-server xkbcommon libinput $(XLIBS)
-DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS)
+DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS) -fPIC -rdynamic
LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` -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 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
+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)
@@ -63,6 +66,8 @@ install: dwl
mkdir -p $(DESTDIR)$(PREFIX)/bin
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
@@ -70,9 +75,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 def2562..1c9ab67 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>
@@ -67,6 +76,7 @@
#include <xcb/xcb_icccm.h>
#endif
+
#include "util.h"
/* macros */
@@ -77,8 +87,34 @@
#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 { static struct wl_listener _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 = malloc(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 */
@@ -242,6 +278,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);
@@ -253,7 +292,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 closemon(Monitor *m);
static void commitlayersurfacenotify(struct wl_listener *listener, void *data);
@@ -321,7 +371,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);
@@ -332,7 +393,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);
@@ -356,6 +428,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 const char broken[] = "broken";
static pid_t child_pid = -1;
@@ -400,7 +482,9 @@ static struct wlr_scene_rect *root_bg;
static struct wlr_session_lock_manager_v1 *session_lock_mgr;
static struct wlr_scene_rect *locked_bg;
static struct wlr_session_lock_v1 *cur_lock;
+#ifdef HOT
static struct wl_listener lock_listener = {.notify = locksession};
+#endif
static struct wlr_seat *seat;
static KeyboardGroup *kb_group;
@@ -426,6 +510,33 @@ static struct wlr_xwayland *xwayland;
static xcb_atom_t netatom[NetLast];
#endif
+/* undoes the shadowing of static from above */
+#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
+
/* configuration, allows nested code to access above variables */
#include "config.h"
@@ -673,6 +784,8 @@ checkidleinhibitor(struct wlr_surface *exclude)
wlr_idle_notifier_v1_set_inhibited(idle_notifier, inhibited);
}
+#endif
+
void
cleanup(void)
{
@@ -687,7 +800,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 */
@@ -699,6 +812,8 @@ cleanup(void)
wlr_scene_node_destroy(&scene->tree.node);
}
+#ifdef HOT
+
void
cleanupmon(struct wl_listener *listener, void *data)
{
@@ -712,10 +827,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);
@@ -848,7 +963,7 @@ commitpopup(struct wl_listener *listener, void *data)
box.x -= (type == LayerShell ? l->geom.x : c->geom.x);
box.y -= (type == LayerShell ? l->geom.y : c->geom.y);
wlr_xdg_popup_unconstrain_from_box(popup, &box);
- wl_list_remove(&listener->link);
+ UNLISTEN(listener);
}
void
@@ -1179,8 +1294,8 @@ destroydecoration(struct wl_listener *listener, void *data)
Client *c = wl_container_of(listener, c, destroy_decoration);
c->decoration = NULL;
- 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
@@ -1205,9 +1320,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);
@@ -1226,9 +1341,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;
@@ -1242,7 +1357,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;
@@ -1262,22 +1377,22 @@ 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);
+ UNLISTEN(&c->commit);
+ UNLISTEN(&c->map);
+ UNLISTEN(&c->unmap);
}
free(c);
}
@@ -1292,7 +1407,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);
}
@@ -1306,8 +1421,8 @@ destroysessionlock(struct wl_listener *listener, void *data)
void
destroysessionmgr(struct wl_listener *listener, void *data)
{
- wl_list_remove(&lock_listener.link);
- wl_list_remove(&listener->link);
+ UNLISTEN(&lock_listener);
+ UNLISTEN(listener);
}
void
@@ -1315,10 +1430,10 @@ destroykeyboardgroup(struct wl_listener *listener, void *data)
{
KeyboardGroup *group = wl_container_of(listener, group, destroy);
wl_event_source_remove(group->key_repeat_source);
+ UNLISTEN(&group->key);
+ UNLISTEN(&group->modifiers);
+ UNLISTEN(&group->destroy);
wlr_keyboard_group_destroy(group->wlr_group);
- wl_list_remove(&group->key.link);
- wl_list_remove(&group->modifiers.link);
- wl_list_remove(&group->destroy.link);
free(group);
}
@@ -2212,6 +2327,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)
{
@@ -2251,11 +2368,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
@@ -2271,6 +2388,9 @@ run(char *startup_cmd)
wl_display_run(dpy);
}
+#endif
+#ifdef HOT
+
void
setcursor(struct wl_listener *listener, void *data)
{
@@ -2428,17 +2548,19 @@ setsel(struct wl_listener *listener, void *data)
wlr_seat_set_selection(seat, event->source, event->serial);
}
+#else /*HOT*/
+
void
setup(void)
{
int 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. */
@@ -2454,7 +2576,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);
@@ -2550,7 +2672,7 @@ setup(void)
LISTEN_STATIC(&idle_inhibit_mgr->events.new_inhibitor, createidleinhibitor);
session_lock_mgr = wlr_session_lock_manager_v1_create(dpy);
- wl_signal_add(&session_lock_mgr->events.new_lock, &lock_listener);
+ LISTEN_GLOBAL(&session_lock_mgr->events.new_lock, lock_listener);
LISTEN_STATIC(&session_lock_mgr->events.destroy, destroysessionmgr);
locked_bg = wlr_scene_rect_create(layers[LyrBlock], sgeom.width, sgeom.height,
(float [4]){0.1f, 0.1f, 0.1f, 1.0f});
@@ -2620,7 +2742,7 @@ setup(void)
LISTEN_STATIC(&seat->events.request_start_drag, requeststartdrag);
LISTEN_STATIC(&seat->events.start_drag, startdrag);
- kb_group = createkeyboardgroup();
+ kb_group = TSYM(KeyboardGroup *(*)(void), createkeyboardgroup)();
wl_list_init(&kb_group->destroy.link);
output_mgr = wlr_output_manager_v1_create(dpy);
@@ -2647,6 +2769,9 @@ setup(void)
#endif
}
+#endif
+#ifdef HOT
+
void
spawn(const Arg *arg)
{
@@ -3121,8 +3246,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);
}
xcb_atom_t
@@ -3185,17 +3310,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
@@ -3215,3 +3464,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 51130af..2000731 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.49.0
File diff suppressed because it is too large Load Diff