From 2f719d8e071751615a0d6b91e15d9fc723061b8f Mon Sep 17 00:00:00 2001
From: Sergey Fedorov <barracuda@macos-powerpc.org>
Date: Mon, 1 Jun 2026 16:58:28 +0000
Subject: [PATCH] Remove blocks

---
 plugins/gtkui/covermanager/albumartwidget.c |  79 +++--
 plugins/gtkui/covermanager/covermanager.c   | 166 +++++----
 plugins/gtkui/covermanager/covermanager.h   |  21 +-
 plugins/gtkui/fileman.c                     | 358 ++++++++++++--------
 plugins/gtkui/gtkui.c                       |  40 ++-
 plugins/gtkui/gtkui.h                       |   2 +-
 plugins/gtkui/medialib/medialibwidget.c     |  64 ++--
 plugins/gtkui/playlist/playlistcontroller.c |  21 +-
 plugins/gtkui/prefwin/prefwinmedialib.c     |  68 ++--
 9 files changed, 505 insertions(+), 314 deletions(-)

diff --git a/plugins/gtkui/covermanager/albumartwidget.c b/plugins/gtkui/covermanager/albumartwidget.c
index 90636d178..1dd53f610 100644
--- a/plugins/gtkui/covermanager/albumartwidget.c
+++ b/plugins/gtkui/covermanager/albumartwidget.c
@@ -30,7 +30,6 @@
 #include "albumartwidget.h"
 #include "covermanager.h"
 #include "gobjcache.h"
-#include <Block.h>
 #include "../gtkui.h"
 
 #define min(x, y) ((x) < (y) ? (x) : (y))
@@ -63,6 +62,42 @@ typedef struct {
     GtkWidget *mode_playing_or_selected_track;
 } w_albumart_t;
 
+typedef struct {
+    w_albumart_t *w;
+    covermanager_t *cm;
+    GtkAllocation availableSize;
+    int64_t currentIndex;
+} albumart_cover_callback_ctx_t;
+
+static void albumart_cover_callback(GdkPixbuf *img, void *user_ctx) {
+    albumart_cover_callback_ctx_t *ctx = (albumart_cover_callback_ctx_t *)user_ctx;
+    w_albumart_t *w = ctx->w;
+    covermanager_t *cm = ctx->cm;
+    GtkAllocation availableSize = ctx->availableSize;
+    int64_t currentIndex = ctx->currentIndex;
+
+    if (currentIndex != w->request_index - 1) {
+        free(ctx);
+        return;
+    }
+    if (img != NULL) {
+        GtkAllocation originalSize = { 0 };
+        originalSize.width = gdk_pixbuf_get_width(img);
+        originalSize.height = gdk_pixbuf_get_height(img);
+        GtkAllocation desired_size = covermanager_desired_size_for_image_size(cm, originalSize, availableSize);
+        GdkPixbuf *scaled_image = covermanager_create_scaled_image(cm, img, desired_size);
+        w->image = scaled_image;
+    }
+    else {
+        if (w->image != NULL) {
+            gobj_unref(w->image);
+            w->image = NULL;
+        }
+    }
+    gtk_widget_queue_draw(w->drawing_area);
+    free(ctx);
+}
+
 static gboolean
 _update (w_albumart_t *w) {
     if (w->plugin == NULL) {
@@ -123,26 +158,16 @@ _update (w_albumart_t *w) {
 
     covermanager_t *cm = covermanager_shared ();
 
-    GdkPixbuf *image = covermanager_cover_for_track (cm, it, w->source_id, ^(GdkPixbuf *img) {
-        if (currentIndex != w->request_index - 1) {
-            return;
-        }
-        if (img != NULL) {
-            GtkAllocation originalSize = { 0 };
-            originalSize.width = gdk_pixbuf_get_width (img);
-            originalSize.height = gdk_pixbuf_get_height (img);
-            GtkAllocation desired_size = covermanager_desired_size_for_image_size (cm, originalSize, availableSize);
-            GdkPixbuf *scaled_image = covermanager_create_scaled_image (cm, img, desired_size);
-            w->image = scaled_image;
-        }
-        else {
-            if (w->image != NULL) {
-                gobj_unref (w->image);
-                w->image = NULL;
-            }
-        }
-        gtk_widget_queue_draw (w->drawing_area);
-    });
+    albumart_cover_callback_ctx_t *cb_ctx = malloc(sizeof(albumart_cover_callback_ctx_t));
+    cb_ctx->w = w;
+    cb_ctx->cm = cm;
+    cb_ctx->availableSize = availableSize;
+    cb_ctx->currentIndex = currentIndex;
+
+    GdkPixbuf *image = covermanager_cover_for_track(
+        cm, it, w->source_id,
+        albumart_cover_callback, cb_ctx
+    );
 
     deadbeef->pl_item_unref (it);
     it = NULL;
@@ -180,6 +205,10 @@ _size_did_change (GtkWidget *self, GdkEventConfigure *event, w_albumart_t *w) {
     return FALSE;
 }
 
+static void throttled_update_cb(void *user_data) {
+    _throttled_update((w_albumart_t *)user_data);
+}
+
 static int
 _message (ddb_gtkui_widget_t *base, uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) {
     w_albumart_t *w = (w_albumart_t *)base;
@@ -190,9 +219,7 @@ _message (ddb_gtkui_widget_t *base, uint32_t id, uintptr_t ctx, uint32_t p1, uin
     case DB_EV_PLAYLISTCHANGED:
     case DB_EV_PLAYLISTSWITCHED:
     case DB_EV_CURSOR_MOVED: {
-        gtkui_dispatch_on_main (^{
-            _throttled_update (w);
-        });
+        gtkui_dispatch_on_main(throttled_update_cb, w);
     } break;
     }
     return 0;
@@ -341,9 +368,7 @@ static void
 _artwork_listener (ddb_artwork_listener_event_t event, void *user_data, int64_t p1, int64_t p2) {
     w_albumart_t *w = (w_albumart_t *)user_data;
     if (event == DDB_ARTWORK_SETTINGS_DID_CHANGE) {
-        gtkui_dispatch_on_main (^{
-            _throttled_update (w);
-        });
+        gtkui_dispatch_on_main(throttled_update_cb, w);
     }
 }
 
diff --git a/plugins/gtkui/covermanager/covermanager.c b/plugins/gtkui/covermanager/covermanager.c
index cf732f691..8d7559e94 100644
--- a/plugins/gtkui/covermanager/covermanager.c
+++ b/plugins/gtkui/covermanager/covermanager.c
@@ -27,7 +27,6 @@
 #include "../../artwork/artwork.h"
 #include "covermanager.h"
 #include "gobjcache.h"
-#include <Block.h>
 #include "gtkui.h"
 
 #define min(x, y) ((x) < (y) ? (x) : (y))
@@ -40,7 +39,8 @@ extern DB_functions_t *deadbeef;
 typedef struct cover_completion_block_list_item_s {
     struct cover_completion_block_list_item_s *next;
     int want_default;
-    covermanager_completion_block_t completion_block;
+    void (*completion_func)(GdkPixbuf *, void *);
+    void *completion_ctx;
 } cover_completion_block_list_item_t;
 
 typedef struct {
@@ -114,15 +114,29 @@ _settings_did_change_for_track (covermanager_t *manager, ddb_playItem_t *track)
     }
 }
 
-static void
-_artwork_listener (ddb_artwork_listener_event_t event, void *user_data, int64_t p1, int64_t p2) {
-    covermanager_t *manager = user_data;
+typedef struct {
+    ddb_artwork_listener_event_t event;
+    covermanager_t *manager;
+    int64_t p1;
+} artwork_listener_ctx_t;
 
-    gtkui_dispatch_on_main (^{
-        if (event == DDB_ARTWORK_SETTINGS_DID_CHANGE) {
-            _settings_did_change_for_track (manager, (ddb_playItem_t *)p1);
-        }
-    });
+static gboolean
+_artwork_listener_main_cb(gpointer data) {
+    artwork_listener_ctx_t *ctx = data;
+    if (ctx->event == DDB_ARTWORK_SETTINGS_DID_CHANGE) {
+        _settings_did_change_for_track(ctx->manager, (ddb_playItem_t *)ctx->p1);
+    }
+    g_free(ctx);
+    return FALSE;
+}
+
+static void
+_artwork_listener(ddb_artwork_listener_event_t event, void *user_data, int64_t p1, int64_t p2) {
+    artwork_listener_ctx_t *ctx = g_malloc(sizeof(artwork_listener_ctx_t));
+    ctx->event = event;
+    ctx->manager = user_data;
+    ctx->p1 = p1;
+    g_idle_add(_artwork_listener_main_cb, ctx);
 }
 
 static char *
@@ -250,7 +264,9 @@ _callback_and_cleanup (ddb_cover_query_t *query, ddb_cover_info_t *cover, GdkPix
                 }
             }
 
-            item->completion_block (img);
+            if (item->completion_func) {
+                item->completion_func(img, item->completion_ctx);
+            }
             item = item->next;
         }
     }
@@ -262,42 +278,73 @@ _callback_and_cleanup (ddb_cover_query_t *query, ddb_cover_info_t *cover, GdkPix
 
     _cleanup_query(query);
 
-    // Release the cover on background queue
+    // Release the cover (synchronously for simplicity with GCC compatibility)
     if (cover != NULL) {
-        dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
-            impl->plugin->cover_info_release (cover);
-        });
+        impl->plugin->cover_info_release(cover);
     }
 }
 
+typedef struct {
+    ddb_cover_query_t *query;
+    ddb_cover_info_t *cover;
+    GdkPixbuf *img;
+} cover_callback_and_cleanup_ctx_t;
+
+static void
+_callback_and_cleanup_cb(void *user_data) {
+    cover_callback_and_cleanup_ctx_t *ctx = user_data;
+    _callback_and_cleanup(ctx->query, ctx->cover, ctx->img);
+    free(ctx);
+}
+
+typedef struct {
+    int error;
+    ddb_cover_query_t *query;
+    ddb_cover_info_t *cover;
+} cover_loaded_ctx_t;
+
 static void
-_cover_loaded_callback (int error, ddb_cover_query_t *query, ddb_cover_info_t *cover) {
+_cover_loaded_background(void *context) {
+    cover_loaded_ctx_t *ctx = context;
+    ddb_cover_query_t *query = ctx->query;
+    ddb_cover_info_t *cover = ctx->cover;
     query_userdata_t *user_data = query->user_data;
     covermanager_t *impl = user_data->impl;
 
     if (impl->is_terminating) {
-        _cleanup_query (query);
+        _cleanup_query(query);
+        free(ctx);
         return;
     }
 
-    // Load the image on background queue
-    dispatch_async (impl->loader_queue, ^{
-        if (impl->is_terminating) {
-            _cleanup_query (query);
-            return;
-        }
+    GdkPixbuf *img = NULL;
+    if (!(query->flags & DDB_ARTWORK_FLAG_CANCELLED)) {
+        img = _load_image_from_cover(impl, cover);
+    }
 
-        __block GdkPixbuf *img = NULL;
+    cover_callback_and_cleanup_ctx_t *main_ctx = malloc(sizeof(cover_callback_and_cleanup_ctx_t));
+    main_ctx->query = query;
+    main_ctx->cover = cover;
+    main_ctx->img = img;
+    gtkui_dispatch_on_main(_callback_and_cleanup_cb, main_ctx);
+    free(ctx);
+}
 
-        if (!(query->flags & DDB_ARTWORK_FLAG_CANCELLED)) {
-            img = _load_image_from_cover (impl, cover);
-        }
+static void
+_cover_loaded_callback(int error, ddb_cover_query_t *query, ddb_cover_info_t *cover) {
+    query_userdata_t *user_data = query->user_data;
+    covermanager_t *impl = user_data->impl;
+
+    if (impl->is_terminating) {
+        _cleanup_query(query);
+        return;
+    }
 
-        // Update the UI on main queue
-        gtkui_dispatch_on_main (^{
-            _callback_and_cleanup (query, cover, img);
-        });
-    });
+    cover_loaded_ctx_t *ctx = malloc(sizeof(cover_loaded_ctx_t));
+    ctx->error = error;
+    ctx->query = query;
+    ctx->cover = cover;
+    dispatch_async_f(impl->loader_queue, ctx, _cover_loaded_background);
 }
 
 covermanager_t *
@@ -324,8 +371,7 @@ _cover_request_free(gpointer data) {
     cover_completion_block_list_item_t *item = req->completion_blocks;
     while (item != NULL) {
         cover_completion_block_list_item_t *next = item->next;
-        Block_release(item->completion_block);
-        free (item);
+        free(item);
         item = next;
     }
     free(req);
@@ -398,9 +444,9 @@ covermanager_free (covermanager_t *impl) {
 }
 
 static gboolean
-_add_pending_request (covermanager_t *impl, const char *key, covermanager_completion_block_t completion_block, int want_default) {
+_add_pending_request(covermanager_t *impl, const char *key, void (*completion_func)(GdkPixbuf *, void *), void *completion_ctx, int want_default) {
     gboolean result = FALSE;
-    cover_request_t *request = g_hash_table_lookup (impl->pending_requests, key);
+    cover_request_t *request = g_hash_table_lookup(impl->pending_requests, key);
     if (request == NULL) {
         request = calloc(sizeof(cover_request_t), 1);
         g_hash_table_insert(impl->pending_requests, strdup(key), request);
@@ -409,7 +455,8 @@ _add_pending_request (covermanager_t *impl, const char *key, covermanager_comple
 
     cover_completion_block_list_item_t *item = calloc(sizeof(cover_completion_block_list_item_t), 1);
     item->want_default = want_default;
-    item->completion_block = Block_copy(completion_block);
+    item->completion_func = completion_func;
+    item->completion_ctx = completion_ctx;
     item->next = request->completion_blocks;
     request->completion_blocks = item;
 
@@ -417,69 +464,74 @@ _add_pending_request (covermanager_t *impl, const char *key, covermanager_comple
 }
 
 static GdkPixbuf *
-_cover_for_track (
+_cover_for_track(
     covermanager_t *impl,
     int want_default,
     DB_playItem_t *track,
     int64_t source_id,
-    covermanager_completion_block_t completion_block) {
+    void (*completion_func)(GdkPixbuf *, void *),
+    void *completion_ctx) {
     if (!impl->plugin) {
-        completion_block (NULL);
+        if (completion_func) {
+            completion_func(NULL, completion_ctx);
+        }
         return NULL;
     }
 
-    char *key = _cache_key_for_track (impl, track);
-    GdkPixbuf *cover = GDK_PIXBUF (gobj_cache_get (impl->cache, key));
+    char *key = _cache_key_for_track(impl, track);
+    GdkPixbuf *cover = GDK_PIXBUF(gobj_cache_get(impl->cache, key));
 
     // FIXME: need to check whether the cache has NULL object for the key
     if (cover != NULL) {
-        // completion_block is not executed if the image is non-nil, to avoid double drawing.
+        // completion_func is not executed if the image is non-nil, to avoid double drawing.
         // The caller must release user data if the returned image is not nil.
-        free (key);
+        free(key);
         key = NULL;
         return cover;
     }
 
-    gboolean is_new_request = _add_pending_request (impl, key, completion_block, want_default);
+    gboolean is_new_request = _add_pending_request(impl, key, completion_func, completion_ctx, want_default);
 
     if (!is_new_request) {
-        free (key);
+        free(key);
         key = NULL;
         return NULL;
     }
-    ddb_cover_query_t *query = calloc (1, sizeof (ddb_cover_query_t));
-    query->_size = sizeof (ddb_cover_query_t);
+    ddb_cover_query_t *query = calloc(1, sizeof(ddb_cover_query_t));
+    query->_size = sizeof(ddb_cover_query_t);
     query->track = track;
-    deadbeef->pl_item_ref (track);
+    deadbeef->pl_item_ref(track);
     query->source_id = source_id;
 
-    query_userdata_t *data = calloc (1, sizeof (query_userdata_t));
+    query_userdata_t *data = calloc(1, sizeof(query_userdata_t));
     data->impl = impl;
     data->key = key; // transfer ownership here, no need to free
     query->user_data = (void *)data;
     key = NULL;
 
-    impl->plugin->cover_get (query, _cover_loaded_callback);
+    impl->plugin->cover_get(query, _cover_loaded_callback);
 
     return NULL;
 }
 
 GdkPixbuf *
-covermanager_cover_for_track_no_default (
+covermanager_cover_for_track_no_default(
     covermanager_t *impl,
     DB_playItem_t *track,
     int64_t source_id,
-    covermanager_completion_block_t completion_block) {
-    return _cover_for_track (impl, 0, track, source_id, completion_block);
+    covermanager_completion_func_t completion_func,
+    void *completion_ctx) {
+    return _cover_for_track(impl, 0, track, source_id, completion_func, completion_ctx);
 }
 
 GdkPixbuf *
-covermanager_cover_for_track (
+covermanager_cover_for_track(
     covermanager_t *impl,
     DB_playItem_t *track,
     int64_t source_id,
-    covermanager_completion_block_t completion_block) {
-    return _cover_for_track (impl, 1, track, source_id, completion_block);
+    covermanager_completion_func_t completion_func,
+    void *completion_ctx) {
+    return _cover_for_track(impl, 1, track, source_id, completion_func, completion_ctx);
 }
 
 GdkPixbuf *
diff --git a/plugins/gtkui/covermanager/covermanager.h b/plugins/gtkui/covermanager/covermanager.h
index f7f35689b..3c4933d26 100644
--- a/plugins/gtkui/covermanager/covermanager.h
+++ b/plugins/gtkui/covermanager/covermanager.h
@@ -31,9 +31,7 @@
 
 typedef struct covermanager_s covermanager_t;
 
-/// Called by @c covermanager_cover_for_track when the cover is ready.
-/// The @c img argument is not retained, and will be released after the block completes.
-typedef void (^covermanager_completion_block_t) (GdkPixbuf *img);
+typedef void (*covermanager_completion_func_t)(GdkPixbuf *img, void *ctx);
 
 covermanager_t *
 covermanager_shared (void);
@@ -51,15 +49,22 @@ void
 covermanager_free (covermanager_t *manager);
 
 /// Gets the cover from in-memory cache, or initiates asynchronous request to cache it.
-///
-/// If the cover is immediately available -- it will be returned (retained), and the @c completion_block will not be called.
-/// Otherwise the @c completion_block will be called when the requests completes.
 GdkPixbuf *
-covermanager_cover_for_track (covermanager_t *manager, DB_playItem_t *track, int64_t source_id, covermanager_completion_block_t completion_block);
+covermanager_cover_for_track(
+    covermanager_t *manager,
+    DB_playItem_t *track,
+    int64_t source_id,
+    covermanager_completion_func_t completion_func,
+    void *completion_ctx);
 
 /// Same as @c covermanager_cover_for_track but would not return default cover
 GdkPixbuf *
-covermanager_cover_for_track_no_default (covermanager_t *impl, DB_playItem_t *track, int64_t source_id, covermanager_completion_block_t completion_block);
+covermanager_cover_for_track_no_default(
+    covermanager_t *impl,
+    DB_playItem_t *track,
+    int64_t source_id,
+    covermanager_completion_func_t completion_func,
+    void *completion_ctx);
 
 /// Create scaled image with specified dimensions. Returns retained object.
 GdkPixbuf *
diff --git a/plugins/gtkui/fileman.c b/plugins/gtkui/fileman.c
index 5aad9c8da..38d391465 100644
--- a/plugins/gtkui/fileman.c
+++ b/plugins/gtkui/fileman.c
@@ -40,6 +40,61 @@ gtkpl_adddir_cb (gpointer data, gpointer userdata) {
     g_free (data);
 }
 
+typedef struct {
+    ddb_playlist_t *plt_curr;
+    ddb_playlist_t *plt;
+    GSList *lst;
+} gtkui_add_dirs_ctx_t;
+
+static void
+gtkui_add_dirs_main(void *user_data) {
+    gtkui_add_dirs_ctx_t *ctx = user_data;
+    ddb_playItem_t *tail = deadbeef->plt_get_tail_item(ctx->plt_curr, PL_MAIN);
+
+    ddb_undo->set_action_name(_("Add Folders"));
+    deadbeef->plt_move_all_items(ctx->plt_curr, ctx->plt, tail);
+    if (tail != NULL) {
+        deadbeef->pl_item_unref(tail);
+    }
+
+    deadbeef->plt_save_config(ctx->plt_curr);
+    deadbeef->plt_add_files_end(ctx->plt_curr, 0);
+    deadbeef->plt_unref(ctx->plt_curr);
+    deadbeef->plt_unref(ctx->plt);
+    g_slist_free(ctx->lst);
+    free(ctx);
+}
+
+static void
+gtkui_add_dirs_bg(void *user_data) {
+    gtkui_add_dirs_ctx_t *ctx = user_data;
+    int is_empty = 0 == deadbeef->plt_get_item_count(ctx->plt_curr, PL_MAIN);
+    int is_single_item = g_slist_length(ctx->lst) == 1;
+    int is_autorename_enabled = deadbeef->conf_get_int("gtkui.name_playlist_from_folder", 1);
+    if (is_single_item && is_autorename_enabled) {
+        int is_default = 0;
+
+        char *t = calloc(1, 1000);
+        if (!deadbeef->plt_get_title(ctx->plt_curr, t, 1000)) {
+            const char *def = _("New Playlist");
+            is_default = is_empty || !strncmp(t, def, strlen(def));
+        }
+        free(t);
+        t = NULL;
+
+        if (is_default) {
+            const char *folder = strrchr((char*)ctx->lst->data, G_DIR_SEPARATOR);
+            if (!folder) {
+                folder = ctx->lst->data;
+            }
+            deadbeef->plt_set_title(ctx->plt_curr, folder+1);
+        }
+    }
+    g_slist_foreach(ctx->lst, gtkpl_adddir_cb, ctx->plt);
+
+    gtkui_dispatch_on_main(gtkui_add_dirs_main, ctx);
+}
+
 static void
 gtkpl_addfile_cb (gpointer data, gpointer userdata) {
     ddb_playlist_t *plt = userdata;
@@ -72,50 +127,49 @@ gtkui_add_dirs (GSList *lst) {
         return;
     }
 
-    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
-        int is_empty = 0 == deadbeef->plt_get_item_count (plt_curr, PL_MAIN);
-        int is_single_item = g_slist_length (lst) == 1;
-        int is_autorename_enabled = deadbeef->conf_get_int ("gtkui.name_playlist_from_folder", 1);
-        if (is_single_item && is_autorename_enabled) {
-            int is_default = 0;
+    typedef struct {
+        ddb_playlist_t *plt_curr;
+        ddb_playlist_t *plt;
+        GSList *lst;
+    } gtkui_add_dirs_ctx_t;
 
-            char *t = calloc(1, 1000);
-            if (!deadbeef->plt_get_title (plt_curr, t, 1000)) {
-                const char *def = _("New Playlist");
-                is_default = is_empty || !strncmp (t, def, strlen (def));
-            }
-            free (t);
-            t = NULL;
+    gtkui_add_dirs_ctx_t *ctx = malloc(sizeof(gtkui_add_dirs_ctx_t));
+    ctx->plt_curr = plt_curr;
+    ctx->plt = plt;
+    ctx->lst = lst;
 
-            if (is_default) {
-                const char *folder = strrchr ((char*)lst->data, G_DIR_SEPARATOR);
-                if (!folder) {
-                    folder = lst->data;
-                }
-                deadbeef->plt_set_title (plt_curr, folder+1);
-            }
-        }
-        g_slist_foreach(lst, gtkpl_adddir_cb, plt);
-
-
-        // TODO: handle cancel
+    dispatch_async_f(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ctx, gtkui_add_dirs_bg);
+}
 
-        gtkui_dispatch_on_main(^{
-            ddb_playItem_t *tail = deadbeef->plt_get_tail_item(plt_curr, PL_MAIN);
+typedef struct {
+    ddb_playlist_t *plt_curr;
+    ddb_playlist_t *plt;
+    GSList *lst;
+} gtkui_add_files_ctx_t;
 
-            ddb_undo->set_action_name (_("Add Folders"));
-            deadbeef->plt_move_all_items (plt_curr, plt, tail);
-            if (tail != NULL) {
-                deadbeef->pl_item_unref (tail);
-            }
+static void
+gtkui_add_files_main(void *user_data) {
+    gtkui_add_files_ctx_t *ctx = user_data;
+    ddb_playItem_t *tail = deadbeef->plt_get_tail_item(ctx->plt_curr, PL_MAIN);
+    ddb_undo->set_action_name(_("Add Files"));
+    deadbeef->plt_move_all_items(ctx->plt_curr, ctx->plt, tail);
+    if (tail != NULL) {
+        deadbeef->pl_item_unref(tail);
+        tail = NULL;
+    }
+
+    deadbeef->plt_save_config(ctx->plt_curr);
+    deadbeef->plt_add_files_end(ctx->plt_curr, 0);
+    deadbeef->plt_unref(ctx->plt_curr);
+    deadbeef->plt_unref(ctx->plt);
+    free(ctx);
+}
 
-            deadbeef->plt_save_config (plt_curr);
-            deadbeef->plt_add_files_end (plt_curr, 0);
-            deadbeef->plt_unref (plt_curr);
-            deadbeef->plt_unref (plt);
-            g_slist_free (lst);
-        });
-    });
+static void
+gtkui_add_files_bg(void *user_data) {
+    gtkui_add_files_ctx_t *ctx = user_data;
+    gtkpl_add_files(ctx->plt, ctx->lst);
+    gtkui_dispatch_on_main(gtkui_add_files_main, ctx);
 }
 
 void
@@ -130,23 +184,12 @@ gtkui_add_files (struct _GSList *lst) {
         return;
     }
 
-    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
-        gtkpl_add_files (plt, lst);
-        gtkui_dispatch_on_main (^{
-            ddb_playItem_t *tail = deadbeef->plt_get_tail_item (plt_curr, PL_MAIN);
-            ddb_undo->set_action_name (_("Add Files"));
-            deadbeef->plt_move_all_items (plt_curr, plt, tail);
-            if (tail != NULL) {
-                deadbeef->pl_item_unref (tail);
-                tail = NULL;
-            }
+    gtkui_add_files_ctx_t *ctx = malloc(sizeof(gtkui_add_files_ctx_t));
+    ctx->plt_curr = plt_curr;
+    ctx->plt = plt;
+    ctx->lst = lst;
 
-            deadbeef->plt_save_config (plt_curr);
-            deadbeef->plt_add_files_end (plt_curr, 0);
-            deadbeef->plt_unref (plt_curr);
-            deadbeef->plt_unref (plt);
-        });
-    });
+    dispatch_async_f(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ctx, gtkui_add_files_bg);
 }
 
 void
@@ -160,6 +203,68 @@ gtkui_open_files (struct _GSList *lst) {
     gtkui_add_files(lst);
 }
 
+typedef struct {
+    ddb_playlist_t *plt_curr;
+    ddb_playlist_t *plt;
+    char *path_copy;
+    char *custom_title_copy;
+    DB_playItem_t *it;
+} gtkui_add_location_ctx_t;
+
+static void
+gtkui_add_location_main(void *user_data) {
+    gtkui_add_location_ctx_t *ctx = user_data;
+    if (ctx->custom_title_copy != NULL) {
+        deadbeef->pl_replace_meta(ctx->it, ":CUSTOM_TITLE", ctx->custom_title_copy);
+    }
+
+    ddb_playItem_t *tail = deadbeef->plt_get_tail_item(ctx->plt_curr, PL_MAIN);
+    deadbeef->plt_move_all_items(ctx->plt_curr, ctx->plt, tail);
+    if (tail != NULL) {
+        deadbeef->pl_item_unref(tail);
+    }
+
+    deadbeef->plt_save_config(ctx->plt_curr);
+    ddb_undo->set_action_name(_("Add Location"));
+    deadbeef->plt_add_files_end(ctx->plt_curr, 0);
+
+    free(ctx->path_copy);
+    free(ctx->custom_title_copy);
+    deadbeef->pl_item_unref(ctx->it);
+    deadbeef->plt_unref(ctx->plt);
+    deadbeef->plt_unref(ctx->plt_curr);
+    free(ctx);
+}
+
+static void
+gtkui_add_location_bg(void *user_data) {
+    gtkui_add_location_ctx_t *ctx = user_data;
+    DB_playItem_t *tail = deadbeef->plt_get_last(ctx->plt, PL_MAIN);
+    DB_playItem_t *it = deadbeef->plt_insert_file2(0, ctx->plt, tail, ctx->path_copy, NULL, NULL, NULL);
+
+    if (tail) {
+        deadbeef->pl_item_unref(tail);
+        tail = NULL;
+    }
+
+    if (it == NULL) {
+        deadbeef->plt_add_files_end(ctx->plt_curr, 0);
+
+        free(ctx->path_copy);
+        free(ctx->custom_title_copy);
+        deadbeef->plt_unref(ctx->plt);
+        deadbeef->plt_unref(ctx->plt_curr);
+        free(ctx);
+
+        return;
+    }
+
+    deadbeef->pl_item_ref(it);
+    ctx->it = it;
+
+    gtkui_dispatch_on_main(gtkui_add_location_main, ctx);
+}
+
 void
 gtkui_add_location (const char *path, const char *custom_title) {
     ddb_playlist_t *plt_curr = deadbeef->plt_get_curr ();
@@ -173,55 +278,18 @@ gtkui_add_location (const char *path, const char *custom_title) {
 
     char *custom_title_copy = NULL;
     if (custom_title != NULL) {
-        custom_title_copy = strdup (custom_title);
+        custom_title_copy = strdup(custom_title);
     }
 
-    char *path_copy = strdup (path);
-
-    dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
-        DB_playItem_t *tail = deadbeef->plt_get_last (plt, PL_MAIN);
-        DB_playItem_t *it = deadbeef->plt_insert_file2 (0, plt, tail, path_copy, NULL, NULL, NULL);
-
-        if (tail) {
-            deadbeef->pl_item_unref (tail);
-            tail = NULL;
-        }
+    char *path_copy = strdup(path);
 
-        if (it == NULL) {
-            deadbeef->plt_add_files_end (plt_curr, 0);
+    gtkui_add_location_ctx_t *ctx = malloc(sizeof(gtkui_add_location_ctx_t));
+    ctx->plt_curr = plt_curr;
+    ctx->plt = plt;
+    ctx->path_copy = path_copy;
+    ctx->custom_title_copy = custom_title_copy;
 
-            free (path_copy);
-            free (custom_title_copy);
-            deadbeef->plt_unref (plt);
-            deadbeef->plt_unref (plt_curr);
-
-            return;
-        }
-
-        deadbeef->pl_item_ref (it);
-
-        gtkui_dispatch_on_main (^{
-            if (custom_title_copy != NULL) {
-                deadbeef->pl_replace_meta (it, ":CUSTOM_TITLE", custom_title_copy);
-            }
-
-            ddb_playItem_t *tail = deadbeef->plt_get_tail_item (plt_curr, PL_MAIN);
-            deadbeef->plt_move_all_items (plt_curr, plt, tail);
-            if (tail != NULL) {
-                deadbeef->pl_item_unref (tail);
-            }
-
-            deadbeef->plt_save_config (plt_curr);
-            ddb_undo->set_action_name (_("Add Location"));
-            deadbeef->plt_add_files_end (plt_curr, 0);
-
-            free (path_copy);
-            free (custom_title_copy);
-            deadbeef->pl_item_unref (it);
-            deadbeef->plt_unref (plt);
-            deadbeef->plt_unref (plt_curr);
-        });
-    });
+    dispatch_async_f(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ctx, gtkui_add_location_bg);
 }
 
 void
@@ -336,6 +404,56 @@ struct fmdrop_data {
     DB_playItem_t *drop_before;
 };
 
+typedef struct {
+    ddb_playlist_t *plt_curr;
+    ddb_playlist_t *plt;
+    struct fmdrop_data *data;
+    char *mem;
+    ddb_playItem_t *first;
+} gtkui_fm_drop_ctx_t;
+
+static void
+gtkui_fm_drop_main(void *user_data) {
+    gtkui_fm_drop_ctx_t *ctx = user_data;
+    ddb_undo->set_action_name(_("Drag & Drop"));
+
+    ddb_playItem_t *after;
+    if (ctx->data->drop_before != NULL) {
+        after = deadbeef->pl_get_prev(ctx->data->drop_before, PL_MAIN);
+    }
+    else {
+        after = deadbeef->plt_get_last(ctx->plt_curr, PL_MAIN);
+    }
+    deadbeef->plt_move_all_items(ctx->plt_curr, ctx->plt, after);
+    if (after != NULL) {
+        deadbeef->pl_item_unref(after);
+    }
+
+    deadbeef->plt_save_config(ctx->plt_curr);
+    deadbeef->plt_add_files_end(ctx->plt_curr, 0);
+    set_dnd_cursor(ctx->first);
+
+    if (ctx->first != NULL) {
+        deadbeef->pl_item_unref(ctx->first);
+    }
+    if (ctx->data->drop_before) {
+        deadbeef->pl_item_unref(ctx->data->drop_before);
+    }
+    free(ctx->mem);
+    free(ctx->data);
+    deadbeef->plt_unref(ctx->plt);
+    deadbeef->plt_unref(ctx->plt_curr);
+    free(ctx);
+}
+
+static void
+gtkui_fm_drop_bg(void *user_data) {
+    gtkui_fm_drop_ctx_t *ctx = user_data;
+    ddb_playItem_t *first = gtkpl_add_fm_dropped_files(ctx->plt, NULL, ctx->data->mem, ctx->data->length);
+    ctx->first = first;
+    gtkui_dispatch_on_main(gtkui_fm_drop_main, ctx);
+}
+
 void
 gtkui_receive_fm_drop (DB_playItem_t *before, char *mem, int length) {
     struct fmdrop_data *data = calloc (1, sizeof (struct fmdrop_data));
@@ -360,41 +478,13 @@ gtkui_receive_fm_drop (DB_playItem_t *before, char *mem, int length) {
         return;
     }
 
+    gtkui_fm_drop_ctx_t *ctx = malloc(sizeof(gtkui_fm_drop_ctx_t));
+    ctx->plt_curr = plt_curr;
+    ctx->plt = plt;
+    ctx->data = data;
+    ctx->mem = mem;
 
-    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
-        ddb_playItem_t *first = gtkpl_add_fm_dropped_files (plt, NULL, data->mem, data->length);
-
-        gtkui_dispatch_on_main(^{
-            ddb_undo->set_action_name (_("Drag & Drop"));
-
-            ddb_playItem_t *after;
-            if (data->drop_before != NULL) {
-                after = deadbeef->pl_get_prev(data->drop_before, PL_MAIN);
-            }
-            else {
-                after = deadbeef->plt_get_last(plt_curr, PL_MAIN);
-            }
-            deadbeef->plt_move_all_items (plt_curr, plt, after);
-            if (after != NULL) {
-                deadbeef->pl_item_unref (after);
-            }
-
-            deadbeef->plt_save_config (plt_curr);
-            deadbeef->plt_add_files_end (plt_curr, 0);
-            set_dnd_cursor (first);
-
-            if (first != NULL) {
-                deadbeef->pl_item_unref (first);
-            }
-            if (data->drop_before) {
-                deadbeef->pl_item_unref (data->drop_before);
-            }
-            free (mem);
-            free (data);
-            deadbeef->plt_unref (plt);
-            deadbeef->plt_unref (plt_curr);
-        });
-    });
+    dispatch_async_f(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ctx, gtkui_fm_drop_bg);
 }
 
 #if GTK_CHECK_VERSION(3,20,0)
diff --git a/plugins/gtkui/gtkui.c b/plugins/gtkui/gtkui.c
index d832bebd2..d2518c625 100644
--- a/plugins/gtkui/gtkui.c
+++ b/plugins/gtkui/gtkui.c
@@ -28,7 +28,6 @@
 #    include <sys/prctl.h>
 #endif
 #include <dispatch/dispatch.h>
-#include <Block.h>
 #include <gtk/gtk.h>
 #include <jansson.h>
 #include <math.h>
@@ -147,18 +146,25 @@ init_widget_layout (void);
 static int sb_context_id = -1;
 static char sb_text[512];
 
+typedef struct {
+    void (*callback)(void *);
+    void *user_data;
+} gtkui_dispatch_ctx_t;
+
 static gboolean
 _dispatch_on_main_wrapper (void *context) {
-    void (^block) (void) = context;
-    block ();
-    Block_release (block);
+    gtkui_dispatch_ctx_t *ctx = (gtkui_dispatch_ctx_t *)context;
+    ctx->callback(ctx->user_data);
+    free(ctx);
     return FALSE;
 }
 
 void
-gtkui_dispatch_on_main (void (^block) (void)) {
-    dispatch_block_t copy_block = Block_copy (block);
-    g_idle_add (_dispatch_on_main_wrapper, copy_block);
+gtkui_dispatch_on_main (void (*callback)(void *), void *user_data) {
+    gtkui_dispatch_ctx_t *ctx = malloc(sizeof(gtkui_dispatch_ctx_t));
+    ctx->callback = callback;
+    ctx->user_data = user_data;
+    g_idle_add (_dispatch_on_main_wrapper, ctx);
 }
 
 static void
@@ -982,6 +988,17 @@ trackfocus_cb (gpointer data) {
     return FALSE;
 }
 
+static void
+undo_process_cb(void *user_data) {
+    deadbeef->undo_process();
+}
+
+static void
+dspchain_changed_cb(void *user_data) {
+    eq_refresh();
+    dsp_setup_chain_changed();
+}
+
 static int
 gtkui_message (uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) {
     if (!gtkui_accept_messages) {
@@ -989,9 +1006,7 @@ gtkui_message (uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) {
     }
 
     if (id == DB_EV_PLAYLISTCHANGED) {
-        gtkui_dispatch_on_main(^{
-            deadbeef->undo_process();
-        });
+        gtkui_dispatch_on_main(undo_process_cb, NULL);
     }
 
     switch (id) {
@@ -1057,10 +1072,7 @@ gtkui_message (uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) {
         g_idle_add (add_mainmenu_actions_cb, NULL);
         break;
     case DB_EV_DSPCHAINCHANGED:
-        gtkui_dispatch_on_main(^{
-            eq_refresh ();
-            dsp_setup_chain_changed ();
-        });
+        gtkui_dispatch_on_main(dspchain_changed_cb, NULL);
         break;
     }
     return 0;
diff --git a/plugins/gtkui/gtkui.h b/plugins/gtkui/gtkui.h
index a47e93981..b6a29ec96 100644
--- a/plugins/gtkui/gtkui.h
+++ b/plugins/gtkui/gtkui.h
@@ -197,6 +197,6 @@ char *
 gtkui_trim_whitespace (char *p, size_t len);
 
 void
-gtkui_dispatch_on_main (void (^block) (void));
+gtkui_dispatch_on_main (void (*callback)(void *), void *user_data);
 
 #endif
diff --git a/plugins/gtkui/medialib/medialibwidget.c b/plugins/gtkui/medialib/medialibwidget.c
index 0998233da..e15682cb0 100644
--- a/plugins/gtkui/medialib/medialibwidget.c
+++ b/plugins/gtkui/medialib/medialibwidget.c
@@ -726,29 +726,64 @@
     tracks = NULL;
 }
 
+typedef struct {
+    w_medialib_viewer_t *mlv;
+    GtkTreePath *path;
+    GdkPixbuf *scaled_img;
+    GdkPixbuf *img;
+} receive_cover_data_t;
+
 static void
-_receive_cover (w_medialib_viewer_t *mlv, GtkTreePath *path, GdkPixbuf *img) {
-    g_object_ref (img);
-    dispatch_async (mlv->background_queue, ^{
-        // scale
-        GtkAllocation a;
-        a.x = 0;
-        a.y = 0;
-        a.width = ML_CELL_RENDERER_PIXBUF_SIZE;
-        a.height = ML_CELL_RENDERER_PIXBUF_SIZE;
-        GdkPixbuf *scaled_img = covermanager_create_scaled_image (covermanager_shared (), img, a);
-
-        gtkui_dispatch_on_main (^{
-            GtkTreeStore *store = mlv->store;
-            GtkTreeIter iter;
-            GtkTreeModel *model = GTK_TREE_MODEL (mlv->store);
-            gtk_tree_model_get_iter (model, &iter, path);
-            gtk_tree_store_set (store, &iter, COL_ICON, scaled_img, -1);
-            g_object_unref (scaled_img);
-            gtk_tree_path_free (path);
-            g_object_unref (img);
-        });
-    });
+_receive_cover_main_cb(void *user_data) {
+    receive_cover_data_t *data = user_data;
+    GtkTreeStore *store = data->mlv->store;
+    GtkTreeIter iter;
+    GtkTreeModel *model = GTK_TREE_MODEL(data->mlv->store);
+    gtk_tree_model_get_iter(model, &iter, data->path);
+    gtk_tree_store_set(store, &iter, COL_ICON, data->scaled_img, -1);
+    g_object_unref(data->scaled_img);
+    gtk_tree_path_free(data->path);
+    g_object_unref(data->img);
+    free(data);
+}
+
+static void
+_receive_cover_async_cb(void *user_data) {
+    receive_cover_data_t *data = user_data;
+    // scale
+    GtkAllocation a;
+    a.x = 0;
+    a.y = 0;
+    a.width = ML_CELL_RENDERER_PIXBUF_SIZE;
+    a.height = ML_CELL_RENDERER_PIXBUF_SIZE;
+    GdkPixbuf *scaled_img = covermanager_create_scaled_image(covermanager_shared(), data->img, a);
+    data->scaled_img = scaled_img;
+    gtkui_dispatch_on_main(_receive_cover_main_cb, data);
+}
+
+static void
+_receive_cover(w_medialib_viewer_t *mlv, GtkTreePath *path, GdkPixbuf *img) {
+    g_object_ref(img);
+    receive_cover_data_t *data = malloc(sizeof(receive_cover_data_t));
+    data->mlv = mlv;
+    data->path = path;
+    data->img = img;
+    dispatch_async_f(mlv->background_queue, data, _receive_cover_async_cb);
+}
+
+typedef struct {
+    w_medialib_viewer_t *mlv;
+    GtkTreePath *path;
+    int64_t reload_index;
+} cover_callback_ctx_t;
+
+static void cover_callback_func(GdkPixbuf *img, void *user_ctx) {
+    cover_callback_ctx_t *ctx = (cover_callback_ctx_t *)user_ctx;
+    if (ctx->reload_index == ctx->mlv->reload_index && img != NULL) {
+        _receive_cover(ctx->mlv, ctx->path, img);
+    }
+    gtk_tree_path_free(ctx->path);
+    free(ctx);
 }
 
 static GdkPixbuf *
@@ -794,15 +829,18 @@
     }
 
     int64_t reload_index = mlv->reload_index;
-    GdkPixbuf *cached_cover = covermanager_cover_for_track_no_default (
-        covermanager_shared (),
+    cover_callback_ctx_t *cb_ctx = malloc(sizeof(cover_callback_ctx_t));
+    cb_ctx->mlv = mlv;
+    cb_ctx->path = gtk_tree_path_copy(path);
+    cb_ctx->reload_index = reload_index;
+
+    GdkPixbuf *cached_cover = covermanager_cover_for_track_no_default(
+        covermanager_shared(),
         track,
         mlv->artwork_source_id,
-        ^(GdkPixbuf *img) {
-            if (reload_index == mlv->reload_index && img != NULL) {
-                _receive_cover (mlv, path, img);
-            }
-        });
+        cover_callback_func,
+        cb_ctx
+    );
 
     if (cached_cover != NULL) {
         _receive_cover (mlv, path, cached_cover);
diff --git a/plugins/gtkui/playlist/playlistcontroller.c b/plugins/gtkui/playlist/playlistcontroller.c
index fb39337e2..b7d16cad3 100644
--- a/plugins/gtkui/playlist/playlistcontroller.c
+++ b/plugins/gtkui/playlist/playlistcontroller.c
@@ -21,7 +21,6 @@
     3. This notice may not be removed or altered from any source distribution.
 */
 
-#include <Block.h>
 #include <dispatch/dispatch.h>
 #include <stdlib.h>
 #include <string.h>
@@ -45,25 +44,15 @@ struct playlist_controller_s {
 };
 
 static gboolean
-_dispatch_on_main_wrapper (void *context) {
-    void (^block)(void) = context;
-    block ();
-    Block_release(block);
+_artwork_listener_cb(gpointer user_data) {
+    playlist_controller_t *ctl = user_data;
+    ddb_listview_reset_artwork(ctl->listview);
     return FALSE;
 }
 
 static void
-_dispatch_on_main(void (^block)(void)) {
-    dispatch_block_t copy_block = Block_copy(block);
-    g_idle_add(_dispatch_on_main_wrapper, copy_block);
-}
-
-static void
-_artwork_listener (ddb_artwork_listener_event_t event, void *user_data, int64_t p1, int64_t p2) {
-    _dispatch_on_main(^{
-        playlist_controller_t *ctl = user_data;
-        ddb_listview_reset_artwork (ctl->listview);
-    });
+_artwork_listener(ddb_artwork_listener_event_t event, void *user_data, int64_t p1, int64_t p2) {
+    g_idle_add(_artwork_listener_cb, user_data);
 }
 
 playlist_controller_t *
diff --git a/plugins/gtkui/playlist/playlistrenderer.c b/plugins/gtkui/playlist/playlistrenderer.c
index 75e57ef39..83a33681b 100644
--- a/plugins/gtkui/playlist/playlistrenderer.c
+++ b/plugins/gtkui/playlist/playlistrenderer.c
@@ -265,6 +265,18 @@ cover_draw_cairo (GdkPixbuf *pixbuf, int x, int min_y, int max_y, int width, int
     cairo_restore(cr);
 }
 
+typedef struct {
+    DdbListview *listview;
+    DB_playItem_t *it;
+} playlist_cover_ctx_t;
+
+static void playlist_cover_callback(GdkPixbuf *img, void *ctx_) {
+    playlist_cover_ctx_t *ctx = (playlist_cover_ctx_t *)ctx_;
+    deadbeef->pl_item_unref(ctx->it);
+    gtk_widget_queue_draw(GTK_WIDGET(ctx->listview));
+    free(ctx);
+}
+
 void
 pl_common_draw_album_art (DdbListview *listview, cairo_t *cr, DdbListviewGroup *grp, void *user_data, int min_y, int next_y, int x, int y, int width, int height, int alignment) {
     int art_width = width - ART_PADDING_HORZ * 2;
@@ -282,13 +294,12 @@ pl_common_draw_album_art (DdbListview *listview, cairo_t *cr, DdbListviewGroup *
 
     GdkPixbuf *image = NULL;
 
-    deadbeef->pl_item_ref (it);
-    image = covermanager_cover_for_track(cm, it, 0, ^(GdkPixbuf *img) { // img only valid in this block
-        deadbeef->pl_item_unref (it);
+    deadbeef->pl_item_ref(it);
+    playlist_cover_ctx_t *cb_ctx = malloc(sizeof(playlist_cover_ctx_t));
+    cb_ctx->listview = listview;
+    cb_ctx->it = it;
+    image = covermanager_cover_for_track(cm, it, 0, playlist_cover_callback, cb_ctx);
 
-        gtk_widget_queue_draw(GTK_WIDGET(listview));
-        // FIXME: redraw only the group rect
-    });
     if (image != NULL) { // completion block won't be called
         deadbeef->pl_item_unref (it);
         it = NULL;
diff --git a/plugins/gtkui/prefwin/prefwinmedialib.c b/plugins/gtkui/prefwin/prefwinmedialib.c
index 9258faf5b..550aa6b04 100644
--- a/plugins/gtkui/prefwin/prefwinmedialib.c
+++ b/plugins/gtkui/prefwin/prefwinmedialib.c
@@ -21,7 +21,6 @@
     3. This notice may not be removed or altered from any source distribution.
 */
 
-#include <Block.h>
 #ifdef HAVE_CONFIG_H
 #  include <config.h>
 #endif
@@ -115,45 +114,44 @@ _remove_did_activate (GtkButton* self, gpointer user_data) {
     medialib_plugin->refresh (source);
 }
 
+typedef struct {
+    ddb_mediasource_event_type_t event;
+} listener_ctx_t;
+
 static gboolean
-_dispatch_on_main_wrapper (void *context) {
-    void (^block)(void) = context;
-    block ();
-    Block_release(block);
-    return FALSE;
-}
+_listener_on_main_cb(gpointer user_data) {
+    listener_ctx_t *ctx = user_data;
+    ddb_mediasource_source_t *source = gtkui_medialib_get_source();
+    if ((int)ctx->event < 1000) {
+        switch (ctx->event) {
+        case DDB_MEDIASOURCE_EVENT_ENABLED_DID_CHANGE:
+            {
+                GtkWidget *enable_button = lookup_widget(prefwin, "toggle_medialib_on");
+                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(enable_button), medialib_plugin->is_source_enabled(source));
+            }
+            break;
+        default:
+            break;
+        }
+        g_free(ctx);
+        return FALSE;
+    }
 
-static void
-_dispatch_on_main(void (^block)(void)) {
-    dispatch_block_t copy_block = Block_copy(block);
-    g_idle_add(_dispatch_on_main_wrapper, copy_block);
+    ddb_medialib_mediasource_event_type_t event = (ddb_medialib_mediasource_event_type_t)ctx->event;
+    switch (event) {
+    case DDB_MEDIALIB_MEDIASOURCE_EVENT_FOLDERS_DID_CHANGE:
+        _reload_data();
+        break;
+    }
+    g_free(ctx);
+    return FALSE;
 }
 
 static void
-_listener (ddb_mediasource_event_type_t _event, void *user_data) {
-    _dispatch_on_main(^{
-        ddb_mediasource_source_t *source = gtkui_medialib_get_source();
-        if ((int)_event < 1000) {
-            switch (_event) {
-            case DDB_MEDIASOURCE_EVENT_ENABLED_DID_CHANGE:
-                {
-                    GtkWidget *enable_button = lookup_widget(prefwin, "toggle_medialib_on");
-                    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(enable_button), medialib_plugin->is_source_enabled(source));
-                }
-                break;
-            default:
-                break;
-            }
-            return;
-        }
-
-        ddb_medialib_mediasource_event_type_t event = (ddb_medialib_mediasource_event_type_t)_event;
-        switch (event) {
-        case DDB_MEDIALIB_MEDIASOURCE_EVENT_FOLDERS_DID_CHANGE:
-            _reload_data();
-            break;
-        }
-    });
+_listener(ddb_mediasource_event_type_t _event, void *user_data) {
+    listener_ctx_t *ctx = g_malloc(sizeof(listener_ctx_t));
+    ctx->event = _event;
+    g_idle_add(_listener_on_main_cb, ctx);
 }
 
 void
