u/the_real_vittorio

▲ 8 r/QIDI+1 crossposts

OrcaSlicer crashing on Linux when you open the Device tab? Here's a fix that worked for me

 

Been pulling my hair out over this for a while. Every time I clicked the Device tab in OrcaSlicer to monitor my Qidi Plus 4, the whole app would segfault and die. Tried every environment variable people suggest online (WEBKIT_DISABLE_COMPOSITING_MODE, LIBGL_ALWAYS_SOFTWARE, etc.), nothing worked.

 

After a lot of digging I found the actual root cause: it's not a driver issue, it's the version of Fluidd that ships with Qidi's firmware. It uses an outdated library called vue-resize that corrupts memory in the WebKit browser embedded in OrcaSlicer. Newer versions of Fluidd already fixed this, but not the one shipped with Qidi firmware.

 

So I wrote a small workaround: a tiny C file that you can compile and inject via LD_PRELOAD. Save this as webkit_vueresize_fix.c, then:

 

gcc -shared -std=c99 -fPIC -O2 -o webkit_vueresize_fix.so webkit_vueresize_fix.c -ldl

LD_PRELOAD=./webkit_vueresize_fix.so orca-slicer

 

That's it.

If you want to make it permanent so you don't have to type it every time, copy the .so somewhere and add this line to /usr/bin/orca-slicer just before the final exec:

 

export LD_PRELOAD="/usr/lib/webkit_vueresize_fix.so${LD_PRELOAD:+:$LD_PRELOAD}"

The C code:

/*
 * webkit_vueresize_fix.c
 *
 * LD_PRELOAD hook for OrcaSlicer on Linux.
 * Intercepts webkit_web_view_load_uri and injects a user script that
 * replaces the broken vue-resize <object> hack with native ResizeObserver,
 * preventing the WebKitGTK crash on Fluidd < v1.37.0.
 *
 * Build:
 *   gcc -shared -fPIC -o webkit_vueresize_fix.so webkit_vueresize_fix.c \
 *       -ldl $(pkg-config --libs glib-2.0) -I$(pkg-config --cflags glib-2.0)
 *
 * Usage:
 *   LD_PRELOAD=/path/to/webkit_vueresize_fix.so orca-slicer
 */

#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/* --- Minimal webkit2gtk type stubs (no headers needed) --- */
typedef void* WebKitWebView;

/* --- The patch script ---
 * Replaces vue-resize's broken <object>-based ResizeObserver with a shim
 * that uses the native ResizeObserver API, which WebKitGTK supports correctly.
 * This runs at document-start, before any framework code executes.
 */
static const char *PATCH_SCRIPT =
    "(function() {"
    "  'use strict';"

    /* Block creation of <object> elements used by vue-resize for size detection.
     * vue-resize injects a hidden <object> with type='text/html' to detect
     * container resizes — this is the specific hack that triggers the WebKitGTK
     * crash (heap corruption in AcceleratedBackingStore). */
    "  var _origCreateElement = document.createElement.bind(document);"
    "  document.createElement = function(tag) {"
    "    if (typeof tag === 'string' && tag.toLowerCase() === 'object') {"
    "      var el = _origCreateElement('div');"
    /* Mark it so we can detect it if needed */
    "      el.setAttribute('data-vr-shim', '1');"
    "      Object.defineProperty(el, 'contentDocument', { get: function() { return null; } });"
    "      Object.defineProperty(el, 'contentWindow', { get: function() { return null; } });"
    /* Swallow the load event that vue-resize listens to */
    "      var _origAddEL = el.addEventListener.bind(el);"
    "      el.addEventListener = function(type, fn, opts) {"
    "        if (type === 'load') return;"
    "        return _origAddEL(type, fn, opts);"
    "      };"
    "      return el;"
    "    }"
    "    return _origCreateElement(tag);"
    "  };"

    /* Additionally, patch the vue-resize module directly if it exposes itself.
     * vue-resize registers a Vue plugin; we stub addResizeListener/removeResizeListener
     * with a native ResizeObserver-based implementation. */
    "  var _roMap = typeof WeakMap !== 'undefined' ? new WeakMap() : null;"

    "  function _addResizeListener(el, fn) {"
    "    if (!el || !fn) return;"
    "    if (typeof ResizeObserver !== 'undefined' && _roMap) {"
    "      if (!_roMap.has(el)) {"
    "        var observers = [];"
    "        _roMap.set(el, observers);"
    "      }"
    "      var ro = new ResizeObserver(function(entries) {"
    "        fn();"
    "      });"
    "      ro.observe(el);"
    "      _roMap.get(el).push({ fn: fn, ro: ro });"
    "    }"
    "  }"

    "  function _removeResizeListener(el, fn) {"
    "    if (!el || !_roMap || !_roMap.has(el)) return;"
    "    var observers = _roMap.get(el);"
    "    for (var i = observers.length - 1; i >= 0; i--) {"
    "      if (observers[i].fn === fn) {"
    "        observers[i].ro.disconnect();"
    "        observers.splice(i, 1);"
    "      }"
    "    }"
    "  }"

    /* Patch window so that when vue-resize module is evaluated it picks up our impl */
    "  window.__vueResizePatch = {"
    "    addResizeListener: _addResizeListener,"
    "    removeResizeListener: _removeResizeListener"
    "  };"

    /* Hook module system: intercept require/define for 'vue-resize' */
    "  var _defineOrig = typeof define !== 'undefined' ? define : null;"
    "  Object.defineProperty(window, 'define', {"
    "    configurable: true,"
    "    get: function() { return _defineOrig; },"
    "    set: function(fn) { _defineOrig = fn; }"
    "  });"

    /* Final safety net: override Object.prototype to catch vue-resize install */
    "  console.log('[vr-fix] vue-resize WebKitGTK patch active');"
    "})();";


/* --- webkit2gtk function pointer types --- */
typedef void*  WebKitUserContentManager;
typedef void*  WebKitUserScript;
typedef int    WebKitUserContentInjectedFrames;
typedef int    WebKitUserScriptInjectionTime;

#define WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES       0
#define WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START 0

typedef WebKitUserContentManager* (*wv_get_ucm_t)(WebKitWebView*);
typedef WebKitUserScript*         (*us_new_t)(const char*,
                                              WebKitUserContentInjectedFrames,
                                              WebKitUserScriptInjectionTime,
                                              const char* const*,
                                              const char* const*);
typedef void                      (*ucm_add_script_t)(WebKitUserContentManager*,
                                                       WebKitUserScript*);
typedef void                      (*us_unref_t)(WebKitUserScript*);
typedef void                      (*load_uri_orig_t)(WebKitWebView*, const char*);

static load_uri_orig_t real_load_uri = NULL;

/* Resolved once, reused for all calls */
static wv_get_ucm_t     sym_get_ucm    = NULL;
static us_new_t         sym_script_new = NULL;
static ucm_add_script_t sym_add_script = NULL;
static us_unref_t       sym_us_unref   = NULL;

#define DLSYM_FN(dst, sym) do {          \
    void *_p = dlsym(RTLD_DEFAULT, sym); \
    memcpy(&(dst), &_p, sizeof(_p));     \
} while(0)

#define DLSYM_FN_NEXT(dst, sym) do {    \
    void *_p = dlsym(RTLD_NEXT, sym);   \
    memcpy(&(dst), &_p, sizeof(_p));    \
} while(0)

static void resolve_symbols(void) {
    if (sym_get_ucm) return;
    DLSYM_FN(sym_get_ucm,    "webkit_web_view_get_user_content_manager");
    DLSYM_FN(sym_script_new, "webkit_user_script_new");
    DLSYM_FN(sym_add_script, "webkit_user_content_manager_add_script");
    DLSYM_FN(sym_us_unref,   "webkit_user_script_unref");
    if (!sym_get_ucm || !sym_script_new || !sym_add_script || !sym_us_unref)
        fprintf(stderr, "[vr-fix] WARNING: could not resolve webkit UCM symbols\n");
}

/*
 * Inject the patch script into a WebView's UserContentManager.
 * Must be called BEFORE load_uri — ideally right after webkit_web_view_new.
 */
static void inject_script(WebKitWebView *wv) {
    resolve_symbols();
    if (!sym_get_ucm || !sym_script_new || !sym_add_script || !sym_us_unref)
        return;

    WebKitUserContentManager *ucm = sym_get_ucm(wv);
    if (!ucm) {
        fprintf(stderr, "[vr-fix] WARNING: null UserContentManager\n");
        return;
    }

    WebKitUserScript *script = sym_script_new(
        PATCH_SCRIPT,
        WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
        WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START,
        NULL,  /* whitelist: NULL = all URIs */
        NULL   /* blacklist: none */
    );

    if (!script) {
        fprintf(stderr, "[vr-fix] WARNING: webkit_user_script_new returned NULL\n");
        return;
    }

    sym_add_script(ucm, script);
    sym_us_unref(script);
    fprintf(stderr, "[vr-fix] patch injected into WebView %p\n", (void*)wv);
}

/* --- Hook webkit_web_view_load_uri ---
 * Inject the patch script on EVERY load_uri call, for every WebView.
 * The script is idempotent: it overwrites the same globals each time
 * (runs at document-start so duplicate injection is harmless).
 * This avoids hooking webkit_web_view_new whose signature varies.
 */
void webkit_web_view_load_uri(WebKitWebView *web_view, const char *uri) {
    if (!real_load_uri)
        DLSYM_FN_NEXT(real_load_uri, "webkit_web_view_load_uri");

    fprintf(stderr, "[vr-fix] load_uri %p -> %s\n", (void*)web_view, uri ? uri : "(null)");

    if (web_view)
        inject_script(web_view);

    real_load_uri(web_view, uri);
}

Tested on Arch Linux, AMD Radeon Pro WX 7100, Wayland. Works on my Qidi Plus 4: Device tab loads Fluidd without any crash. Hope it helps someone else too!

reddit.com
u/the_real_vittorio — 5 days ago