• Rust 86%
  • Nix 14%
Find a file
2026-07-30 18:50:51 +08:00
crates Split into Cargo workspace: isolate xcap into standalone screenshot helper 2026-06-24 18:37:39 +08:00
.envrc Split into Cargo workspace: isolate xcap into standalone screenshot helper 2026-06-24 18:37:39 +08:00
.gitignore Split into Cargo workspace: isolate xcap into standalone screenshot helper 2026-06-24 18:37:39 +08:00
Cargo.lock Split into Cargo workspace: isolate xcap into standalone screenshot helper 2026-06-24 18:37:39 +08:00
Cargo.toml Split into Cargo workspace: isolate xcap into standalone screenshot helper 2026-06-24 18:37:39 +08:00
flake.lock feat: provide wrapped QQ flake outputs 2026-07-30 18:50:51 +08:00
flake.nix feat: provide wrapped QQ flake outputs 2026-07-30 18:50:51 +08:00
LICENSE update: flake, license and readme 2026-05-14 06:42:23 +08:00
README.md feat: provide wrapped QQ flake outputs 2026-07-30 18:50:51 +08:00

fix-qq-x11-leak

Local workaround for the Linux QQ X11 connection leak, rewritten in Rust.

Based on discovery from Jerry-Terrasse/fix-linuxqq-x11-leak, but with a simpler strategy: instead of tracing wrapper.node and blocking one exact module + offset, this version limits how many outstanding X11 connections wrapper.node can keep open.

What it does

This project injects a small LD_PRELOAD library that hooks XOpenDisplay, XCloseDisplay, and XGetImage.

  • Calls from wrapper.node are tracked.
  • Once the number of unclosed tracked connections reaches QQ_X11_MAX_CONN, new XOpenDisplay calls from wrapper.node return NULL.
  • XGetImage calls from wrapper.node are intercepted and bridged to a native Wayland screenshot via a separate helper process.
  • Other callers are passed through normally.

Architecture

The project is split into a Cargo workspace with two crates for maximum isolation:

Crate Type Purpose
fix-qq-x11-leak cdylib + bin The lightweight .so hook library (no xcap dependency) and the launcher binary.
wayland-screenshot-helper bin A standalone binary that uses xcap to capture the Wayland desktop. Heavy graphical dependencies (libgbm, libwayland, …) live only here.

The .so stays deliberately lightweight so that LD_PRELOAD-ing it into unrelated processes (your shell, ls, cat) won't crash them under Nix where library paths are strictly isolated. When XGetImage is intercepted, the .so spawns the helper process (with LD_PRELOAD stripped), reads RGBA pixels over a pipe, converts to BGRA, and feeds a forged XImage back to the caller.

Build

cargo build --release

Using Nix Flake:

nix build

Usage

Run wrapped QQ directly

nix run git+https://git.gjz010.com/gjz010/fix-qq-x11-leak#qq

The qq flake package is QQ wrapped with the workaround. Its QQ source is currently pinned to Linux QQ 3.2.32-51802 (9.9.33 beta), while the package logic and runtime dependencies are inherited from nixpkgs.

Overlay

Add this flake as an input and apply its default overlay. The resulting pkgs.qq is wrapped automatically. QQ has an unfree license, so the consuming nixpkgs configuration must allow it:

{
  inputs.fix-qq-x11-leak.url =
    "git+https://git.gjz010.com/gjz010/fix-qq-x11-leak";

  outputs = { nixpkgs, fix-qq-x11-leak, ... }: {
    nixosConfigurations.my-host = nixpkgs.lib.nixosSystem {
      modules = [
        {
          nixpkgs.overlays = [ fix-qq-x11-leak.overlays.default ];
          nixpkgs.config.allowUnfree = true;
        }
        ({ pkgs, ... }: {
          environment.systemPackages = [ pkgs.qq ];
        })
      ];
    };
  };
}

Wrap a specific QQ package

Use the flake-level lib.wrapQQ function when you need to choose the exact QQ derivation yourself:

let
  wrappedQQ = fix-qq-x11-leak.lib.wrapQQ {
    inherit pkgs;
    qq = myQQPackage;
  };
in
{
  environment.systemPackages = [ wrappedQQ ];
}

qq defaults to pkgs.qq, so the shorter form is also valid:

fix-qq-x11-leak.lib.wrapQQ { inherit pkgs; }

Wrap an arbitrary command

The default package remains the generic launcher:

FIX_QQ_X11_LEAK_LOG=trace nix run \
  git+https://git.gjz010.com/gjz010/fix-qq-x11-leak -- qq

For a local source build:

FIX_QQ_X11_LEAK_LOG=trace cargo run qq

Environment variables

Variable Description
QQ_X11_MAX_CONN Max outstanding wrapper.node X11 connections. Default: 5.
FIX_QQ_X11_LEAK_LOG Log level for env_logger, for example info or debug.
WAYLAND_SCREENSHOT_HELPER Path to the wayland-screenshot-helper binary. Set automatically by the launcher; override for manual LD_PRELOAD usage.

Notes

  • It is simpler than the original offset-based blocker, but also less precise.
  • If QQ changes how the leaking code is packaged, the /wrapper.node heuristic may stop matching.

Credit

Original investigation and preload-hook approach: Jerry-Terrasse/fix-linuxqq-x11-leak.

License

MIT. See LICENSE.