diff --git a/peachwm b/peachwm index 6b3d44a..ed7c25e 100755 Binary files a/peachwm and b/peachwm differ diff --git a/src/ipc_socket.c b/src/ipc_socket.c index 5fe4336..f8f901d 100644 --- a/src/ipc_socket.c +++ b/src/ipc_socket.c @@ -58,6 +58,10 @@ static char ipc_socket_path[256]; /* Simple JSON builder */ /* ------------------------------------------------------------------ */ +/* Shared JSON buffer — persists between IPC calls (single-threaded) */ +static char *json_shared_buf = NULL; +static size_t json_shared_cap = 0; + struct json_writer { char *buf; size_t len; @@ -69,9 +73,15 @@ struct json_writer { static void json_init(struct json_writer *w) { - w->cap = 4096; - w->buf = ecalloc(1, w->cap); + if (!json_shared_buf) { + json_shared_cap = 4096; + json_shared_buf = malloc(json_shared_cap); + if (!json_shared_buf) + die("json_init: malloc"); + } + w->buf = json_shared_buf; w->len = 0; + w->cap = json_shared_cap; w->depth = -1; memset(w->need_comma, 0, sizeof(w->need_comma)); } @@ -79,7 +89,7 @@ json_init(struct json_writer *w) static void json_finish(struct json_writer *w) { - free(w->buf); + (void)w; } static void @@ -89,12 +99,14 @@ json_grow(struct json_writer *w, size_t needed) return; while (w->cap < w->len + needed) w->cap *= 2; - char *newbuf = realloc(w->buf, w->cap); + char *newbuf = realloc(json_shared_buf, w->cap); if (!newbuf) { fprintf(stderr, "peachwm: json_grow realloc failed\n"); return; } - w->buf = newbuf; + json_shared_buf = newbuf; + json_shared_cap = w->cap; + w->buf = json_shared_buf; } static void @@ -919,10 +931,15 @@ ipc_client_handle_readable(int fd, uint32_t mask, void *data) c->hdr_buf + IPC_MAGIC_LEN + 4, sizeof(uint32_t)); if (c->payload_size > 0) { + if (c->payload_size > 65536) + c->payload_size = 65536; if (c->payload_size > c->payload_alloc) { + size_t new_size = c->payload_size + 1; + if (new_size > 65536) + new_size = 65536; free(c->payload); - c->payload_alloc = c->payload_size + 1; - c->payload = ecalloc(1, c->payload_alloc); + c->payload_alloc = new_size; + c->payload = ecalloc(1, new_size); } c->payload_len = 0; } diff --git a/src/peachwm.c b/src/peachwm.c index f834585..0a1a326 100644 --- a/src/peachwm.c +++ b/src/peachwm.c @@ -92,6 +92,11 @@ #define CLEANMASK(mask) (mask & ~WLR_MODIFIER_CAPS) #define END(A) ((A) + LENGTH(A)) #define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L))) +/* DEPRECATED: embed wl_listeners in a tracking struct instead. This macro + * heap-allocates a bare wl_listener (~24 B); prefer embedding the listener + * inside the owning struct so alloc/free discipline is coarser. The one + * remaining caller (destroydragicon) is intentional — drag icons are one-shot + * and freed in the destroy handler anyway. */ #define LISTEN_STATIC(E, H) \ do { \ struct wl_listener *_l = ecalloc(1, sizeof(*_l)); \ @@ -154,6 +159,15 @@ typedef struct { struct wl_listener destroy; } PointerConstraint; +typedef struct { + struct wl_listener destroy; + struct wlr_idle_inhibitor_v1 *inhibitor; +} IdleInhibitorTrack; + +typedef struct { + struct wl_listener commit; +} PopupCommitTrack; + typedef struct { const char *id; const char *title; @@ -976,8 +990,9 @@ static void 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); + PopupCommitTrack *track = wl_container_of(listener, track, commit); wl_list_remove(&listener->link); - free(listener); + free(track); } static void createdecoration(struct wl_listener *listener, void *data) { @@ -994,7 +1009,10 @@ static void createdecoration(struct wl_listener *listener, void *data) { static void createidleinhibitor(struct wl_listener *listener, [[maybe_unused]] void *data) { struct wlr_idle_inhibitor_v1 *idle_inhibitor = data; - LISTEN_STATIC(&idle_inhibitor->events.destroy, destroyidleinhibitor); + IdleInhibitorTrack *track = ecalloc(1, sizeof(*track)); + track->inhibitor = idle_inhibitor; + track->destroy.notify = destroyidleinhibitor; + wl_signal_add(&idle_inhibitor->events.destroy, &track->destroy); checkidleinhibitor(nullptr); } @@ -1314,7 +1332,9 @@ static void createpopup(struct wl_listener *listener, void *data) { /* This event is raised when a client (either xdg-shell or layer-shell) * creates a new popup. */ struct wlr_xdg_popup *popup = data; - LISTEN_STATIC(&popup->base->surface->events.commit, commitpopup); + PopupCommitTrack *track = ecalloc(1, sizeof(*track)); + track->commit.notify = commitpopup; + wl_signal_add(&popup->base->surface->events.commit, &track->commit); } static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint) { @@ -1368,11 +1388,12 @@ static void destroydragicon(struct wl_listener *listener, void *data) { } static void destroyidleinhibitor(struct wl_listener *listener, void *data) { + IdleInhibitorTrack *track = wl_container_of(listener, track, destroy); /* `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); + free(track); } static void destroylayersurfacenotify(struct wl_listener *listener, void *data) { @@ -3162,8 +3183,7 @@ reapply_client_appearance(void) } } else { if (c->shadow) { - wlr_scene_node_destroy(&c->shadow->node); - c->shadow = NULL; + wlr_scene_node_set_enabled(&c->shadow->node, false); } } }