diff --git a/home-manager/modules/graphics/default.nix b/home-manager/modules/graphics/default.nix index 802e665..5931d33 100644 --- a/home-manager/modules/graphics/default.nix +++ b/home-manager/modules/graphics/default.nix @@ -5,5 +5,6 @@ ./gimp.nix ./inkscape.nix ./krita.nix + ./lightburn.nix ]; } diff --git a/home-manager/modules/graphics/lightburn.nix b/home-manager/modules/graphics/lightburn.nix new file mode 100644 index 0000000..8aa5b20 --- /dev/null +++ b/home-manager/modules/graphics/lightburn.nix @@ -0,0 +1,7 @@ +{ nixosConfig, lib, pkgs, ... }: + +lib.mkIf nixosConfig.jalr.gui.enable { + home.packages = with pkgs; [ + lightburn-sandbox + ]; +} diff --git a/modules/unfree.nix b/modules/unfree.nix index 3394261..74c4998 100644 --- a/modules/unfree.nix +++ b/modules/unfree.nix @@ -2,7 +2,8 @@ { nixpkgs.config.allowUnfreePredicate = (pkg: lib.elem (lib.getName pkg) [ - "unifi-controller" + "lightburn" "mongodb" + "unifi-controller" ]); } diff --git a/pkgs/bwrap-helper/bwrap-helper.py b/pkgs/bwrap-helper/bwrap-helper.py new file mode 100755 index 0000000..877245d --- /dev/null +++ b/pkgs/bwrap-helper/bwrap-helper.py @@ -0,0 +1,264 @@ +#!/usr/bin/env python3 +from functools import partial +from itertools import chain +import argparse +import os +import shutil +import subprocess + + +def flat_map(f, iterable): + return list(chain.from_iterable(map(f, iterable))) + + +def add_switch(name: str, default=False): + if default: + parser.add_argument(f"--no-{name}", dest=name, action="store_false") + else: + parser.add_argument( + f"--{name}", dest=name, action="store_true", default=default + ) + + +def tmp_file(name: str): + tmpdir = f"/tmp/bwrap-helper-{os.getpid()}" + os.makedirs(tmpdir, exist_ok=True) + if name is None: + return tmpdir + else: + return f"{tmpdir}/{name}" + + +def generate_tmp_file(name: str, content: str): + file_path = tmp_file(name) + with open(file_path, "w") as f: + f.write(content) + return file_path + + +def bind(src: str, dest=None, type=None, required=True): + arg = "--" + if type is not None: + arg += type + "-" + arg += "bind" + if not required: + arg += "-try" + + if dest is None: + dest = src + + return [arg, src, dest] + + +def setenv(name: str, value: str): + return ["--setenv", name, value] + + +def parse_passthrough_arg(name: str, nargs: int): + parser.add_argument(f"--{name}", action="append", nargs=nargs) + + +def assemble_passthrough_arg(name: str): + for value in getattr(args, name.replace("-", "_")) or []: + assembled_args.extend([f"--{name}", *value]) + + +dev_bind = partial(bind, type="dev") +ro_bind = partial(bind, type="ro") +ro_bind_try = partial(bind, type="ro", required=False) + +username = os.getenv("USER") +uid = os.getuid() +gid = os.getgid() +home = os.getenv("HOME") + +argument_groups = { + "base": ( + True, + [ + "--tmpfs", + "/tmp", + "--proc", + "/proc", + "--dev", + "/dev", + "--dir", + home, + "--dir", + f"/run/user/{uid}", + *ro_bind("/etc/localtime"), + *ro_bind("/etc/ssl/certs"), + "--unshare-all", + "--die-with-parent", + ], + ), + "nix-store": ( + True, + [ + *flat_map( + ro_bind, + [ + "/nix/store", + "/etc/static", + ], + ), + ], + ), + "path": ( + True, + [ + *flat_map( + ro_bind, + [ + "/run/current-system/sw", # not exclusive to path, but also libraries etc. + f"/etc/profiles/per-user/{username}/bin", + ], + ), + ], + ), + "gui": ( + False, + [ + *dev_bind("/dev/dri"), + *flat_map( + ro_bind, + [ + "/sys/dev/char", + "/sys/devices/pci0000:00", + f"/run/user/{uid}/{os.getenv('WAYLAND_DISPLAY')}", + "/run/opengl-driver", + "/etc/fonts", + ], + ), + *ro_bind_try("/run/opengl-driver-32"), + ], + ), + "x11": ( + False, + [ + *ro_bind("/tmp/.X11-unix"), + ], + ), + "audio": ( + False, + [ + *ro_bind(f"/run/user/{uid}/pulse"), + # should in theory autodetect, but sometimes it does not work + *setenv("PULSE_SERVER", f"/run/user/{uid}/pulse/native"), + # some programs need the cookie + *ro_bind(f"{home}/.config/pulse/cookie"), + *setenv("PULSE_COOKIE", f"{home}/.config/pulse/cookie"), + # ALSA compat + *ro_bind("/etc/asound.conf", required=False), + *ro_bind("/etc/alsa/conf.d", required=False), + # pipewire + *ro_bind(f"/run/user/{uid}/pipewire-0", required=False), + ], + ), + "passwd": ( + False, + [ + *ro_bind( + generate_tmp_file( + "passwd", + f"{username}:x:{uid}:{gid}::{home}:/run/current-system/sw/bin/bash\n", + ), + "/etc/passwd", + ) + ], + ), + "network": ( + False, + [ + "--share-net", + *flat_map( + ro_bind, + [ + "/etc/resolv.conf", + ], + ), + ], + ), + "dbus": ( + False, + [ + *ro_bind("/run/dbus/system_bus_socket"), + *ro_bind(generate_tmp_file("machine-id", "0" * 32), "/etc/machine-id"), + ], + ), + "new-session": ( + True, + [ + "--new-session", + ], + ), + "pwd": ( + False, + [ + *ro_bind(os.getcwd()), + "--chdir", + os.getcwd(), + ], + ), + "pwd-rw": ( + False, + [ + *bind(os.getcwd()), + "--chdir", + os.getcwd(), + ], + ), +} + +passthrough_args = [ + ("bind", 2), + ("dev-bind", 2), + ("dev-bind-try", 2), + ("ro-bind", 2), + ("symlink", 2), +] + +for _, arguments in argument_groups.values(): + for argument in arguments: + assert type(argument) == str + +parser = argparse.ArgumentParser() +parser.add_argument("--show-cmdline", action="store_true") +for name, (default, _) in argument_groups.items(): + add_switch(name, default) +parser.add_argument("program") +parser.add_argument("args", nargs="*") + +for arg, nargs in passthrough_args: + parse_passthrough_arg(arg, nargs) + +args = parser.parse_args() + +assembled_args = ["bwrap"] + +for name, (_, arguments) in argument_groups.items(): + if getattr(args, name): + assembled_args.extend(arguments) + +for arg, _ in passthrough_args: + assemble_passthrough_arg(arg) + +if args.show_cmdline: + for idx, assembled_arg in enumerate(assembled_args): + if idx == 0: + print(assembled_arg, end="") + continue + if assembled_arg.startswith("--"): + print("\n ", end="") + else: + print(end=" ") + print(assembled_arg, end="") + print() + +assembled_args.append(args.program) +assembled_args.extend(args.args) + +try: + subprocess.run(assembled_args) +finally: + shutil.rmtree(tmp_file(None)) diff --git a/pkgs/bwrap-helper/default.nix b/pkgs/bwrap-helper/default.nix new file mode 100644 index 0000000..75de032 --- /dev/null +++ b/pkgs/bwrap-helper/default.nix @@ -0,0 +1,26 @@ +{ bubblewrap, lib, makeWrapper, python3, stdenvNoCC }: +stdenvNoCC.mkDerivation rec { + name = "bwrap-helper"; + + src = ./bwrap-helper.py; + + nativeBuildInputs = [ + makeWrapper + ]; + + buildInputs = [ + bubblewrap + python3 + ]; + + dontUnpack = true; + dontBuild = true; + installPhase = '' + install -D $src $out/bin/bwrap-helper + ''; + postFixup = '' + wrapProgram $out/bin/bwrap-helper --prefix PATH : ${lib.makeBinPath buildInputs} + ''; + + meta.license = lib.licenses.mit; +} diff --git a/pkgs/default.nix b/pkgs/default.nix index 08aa561..3c1483c 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -5,7 +5,10 @@ in { ariang = callPackage ./ariang { }; asterisk-sounds-de = callPackage ./asterisk/sounds-de.nix { }; + bwrap-helper = callPackage ./bwrap-helper { }; fpvout = callPackage ./fpvout { }; + lightburn = callPackage ./lightburn { inherit (prev) lightburn; }; + lightburn-sandbox = callPackage ./lightburn-sandbox { }; mute-indicator = callPackage ./mute-indicator { }; pretix = callPackage ./pretix/pretix.nix { }; pretix-banktool = callPackage ./pretix/pretix-banktool.nix { }; diff --git a/pkgs/lightburn-sandbox/default.nix b/pkgs/lightburn-sandbox/default.nix new file mode 100644 index 0000000..12694b7 --- /dev/null +++ b/pkgs/lightburn-sandbox/default.nix @@ -0,0 +1,13 @@ +{ bwrap-helper, lightburn, writeShellScriptBin }: +writeShellScriptBin "lightburn-sandbox" '' + # state_dir="''${XDG_DATA_HOME:-$HOME/.local/share}/lightburn" + # mkdir -p "$state_dir" + # --bind "$state_dir" "$state_dir" \ + ${bwrap-helper}/bin/bwrap-helper \ + --audio \ + --gui \ + --network \ + --passwd \ + --x11 \ + ${lightburn}/bin/LightBurn +'' diff --git a/pkgs/lightburn/default.nix b/pkgs/lightburn/default.nix new file mode 100644 index 0000000..671c8cb --- /dev/null +++ b/pkgs/lightburn/default.nix @@ -0,0 +1,24 @@ +{ lightburn, fetchurl, radare2 }: +let + patch = ./patch.r2; +in +lightburn.overrideAttrs (o: o // rec { + version = "1.4.01"; + src = fetchurl { + url = "https://github.com/LightBurnSoftware/deployment/releases/download/${version}/LightBurn-Linux64-v${version}.7z"; + sha256 = "sha256-i/5v/hSvz3EtDXFXPQN34IwXXJljp5NVzzMA6BgjS8U="; + }; + #nativeBuildInputs = o.nativeBuildInputs ++ [ + # radare2 + #]; + #patchPhase = '' + # radare2 -q -i ${patch} -w LightBurn/LightBurn + #''; + installPhase = '' + mkdir -p $out/share $out/bin + cp -ar LightBurn $out/share/LightBurn + ln -s $out/share/LightBurn/LightBurn $out/bin + + #wrapQtApp $out/bin/LightBurn + ''; +}) diff --git a/pkgs/lightburn/patch.r2 b/pkgs/lightburn/patch.r2 new file mode 100644 index 0000000..6a48215 --- /dev/null +++ b/pkgs/lightburn/patch.r2 @@ -0,0 +1,4 @@ +s sym.MainWindow::on_actionActivate_License_triggered__ + 4 +"wa ret; nop" +s method.LicenseHandler.Allow__ + 4 +"wa mov eax, 1; ret; nop; nop"