Ship AppImage with bundled GTK runtime and automated releases
AppImage / build (push) Has been cancelled
AppImage / release (push) Has been cancelled
Tests / unit (de, 3.10) (push) Has been cancelled
Tests / unit (de, 3.14) (push) Has been cancelled
Tests / unit (en, 3.10) (push) Has been cancelled
Tests / unit (en, 3.14) (push) Has been cancelled
AppImage / build (push) Has been cancelled
AppImage / release (push) Has been cancelled
Tests / unit (de, 3.10) (push) Has been cancelled
Tests / unit (de, 3.14) (push) Has been cancelled
Tests / unit (en, 3.10) (push) Has been cancelled
Tests / unit (en, 3.14) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
APPDIR=${APPDIR:-$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)}
|
||||
export APPDIR
|
||||
. "$APPDIR/usr/share/btd700-control/appimage-environment.sh"
|
||||
case "${1:-}" in
|
||||
--install-desktop) exec "$APPDIR/usr/bin/python3.12" "$APPDIR/usr/share/btd700-control/install.py" ;;
|
||||
--remove-desktop) exec "$APPDIR/usr/bin/python3.12" "$APPDIR/usr/share/btd700-control/install.py" --uninstall ;;
|
||||
esac
|
||||
exec "$APPDIR/usr/bin/python3.12" -m btd700 "$@"
|
||||
@@ -0,0 +1,14 @@
|
||||
FROM docker.io/library/ubuntu:24.04
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
RUN sed -i 's/Types: deb$/Types: deb deb-src/' /etc/apt/sources.list.d/ubuntu.sources \
|
||||
&& apt-get update \
|
||||
&& apt-get upgrade -y \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
python3 python3-gi python3-gi-cairo gir1.2-gtk-4.0 gir1.2-adw-1 \
|
||||
gir1.2-dbusmenu-glib-0.4 adwaita-icon-theme fonts-dejavu-core \
|
||||
ca-certificates curl squashfs-tools desktop-file-utils binutils libglib2.0-bin \
|
||||
xvfb xauth dbus-x11 \
|
||||
&& rm -rf /var/cache/apt/archives/*.deb
|
||||
RUN apt-get install -y --no-install-recommends librsvg2-common xz-utils file
|
||||
WORKDIR /src
|
||||
CMD ["bash", "packaging/appimage/build.sh"]
|
||||
@@ -0,0 +1,23 @@
|
||||
Download **BTD_700_Control-0.3.0-x86_64.AppImage**, make it executable and open it.
|
||||
Python, GTK, libadwaita and the tray library are included. Requires **x86-64 Linux
|
||||
with glibc 2.39+** (such as Ubuntu 24.04 or newer). English and German are supported.
|
||||
|
||||
```bash
|
||||
chmod +x BTD_700_Control-0.3.0-x86_64.AppImage
|
||||
./BTD_700_Control-0.3.0-x86_64.AppImage
|
||||
```
|
||||
|
||||
Optional `--install-desktop` adds an application-menu entry. Keep the file at that
|
||||
location; enable Start at login in the app for tray autostart. `--remove-desktop`
|
||||
removes both entries. If FUSE is unavailable, prefix the launch command with
|
||||
`APPIMAGE_EXTRACT_AND_RUN=1`.
|
||||
|
||||
GNOME still needs a working AppIndicator/StatusNotifier extension. If USB access
|
||||
is denied, install the attached udev rule following the README instructions.
|
||||
SHA256SUMS covers the binary, package manifest, USB rule and dependency source archive.
|
||||
The large `dependency-sources.tar.gz` is for reviewing/rebuilding libraries and is
|
||||
not needed to run the app. GitHub's Source code archives contain the app itself.
|
||||
|
||||
**Independent, AI-developed (OpenAI Codex), experimental software. No warranty or
|
||||
guarantee of compatibility.** Project code is MIT-licensed; bundled dependencies
|
||||
retain their own licenses. Controls only: no firmware updates, downloads or update APIs.
|
||||
@@ -0,0 +1,7 @@
|
||||
# Deliberately no Python, GTK, libadwaita or libdbusmenu installed on this host.
|
||||
FROM docker.io/library/ubuntu:24.04
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
xvfb xauth dbus-x11 desktop-file-utils
|
||||
WORKDIR /src
|
||||
CMD ["dbus-run-session", "--", "xvfb-run", "-a", "bash", "packaging/appimage/check.sh"]
|
||||
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Bundle Ubuntu runtime files and record every contributing binary/source package."""
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
APPDIR = ROOT / 'build/appimage/BTD_700_Control.AppDir'
|
||||
if APPDIR.exists():
|
||||
shutil.rmtree(APPDIR)
|
||||
APPDIR.mkdir(parents=True)
|
||||
|
||||
|
||||
def output(*args):
|
||||
return subprocess.check_output(args, text=True)
|
||||
|
||||
|
||||
packages = {}
|
||||
owners = {}
|
||||
for line in output('dpkg-query', '-W', '-f=${binary:Package}\t${Version}\t${source:Package}\t${source:Version}\n').splitlines():
|
||||
name, version, source, source_version = line.split('\t')
|
||||
packages[name] = dict(package=name, version=version, source=source, source_version=source_version)
|
||||
for path in output('dpkg-query', '-L', name).splitlines():
|
||||
owners[path] = name
|
||||
if Path(path).is_file():
|
||||
owners[str(Path(path).resolve())] = name
|
||||
included = set()
|
||||
|
||||
|
||||
def copy(path, destination=None):
|
||||
path = Path(path)
|
||||
if path.name in {'__pycache__', 'icon-theme.cache', 'gschemas.compiled'} or path.suffix == '.pyc':
|
||||
return
|
||||
if path.is_dir():
|
||||
for child in sorted(path.iterdir()):
|
||||
copy(child, Path(destination) / child.name if destination else None)
|
||||
elif path.is_file():
|
||||
target = APPDIR / (str(destination) if destination else str(path).lstrip('/'))
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copy2(path, target)
|
||||
owner = owners.get(str(path)) or owners.get(str(path.resolve()))
|
||||
if not owner and path.is_relative_to('/usr/share/mime'):
|
||||
owner = owners['/usr/share/mime/packages/freedesktop.org.xml']
|
||||
if owner:
|
||||
included.add(owner)
|
||||
elif not path.is_relative_to(ROOT):
|
||||
raise RuntimeError(f'No package provenance for {path}')
|
||||
|
||||
|
||||
for path in ['/usr/bin/python3.12', '/usr/lib/python3.12',
|
||||
'/usr/lib/python3/dist-packages/gi', '/usr/lib/python3/dist-packages/cairo',
|
||||
'/usr/lib/x86_64-linux-gnu/girepository-1.0',
|
||||
'/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders',
|
||||
'/usr/share/glib-2.0/schemas', '/usr/share/icons/Adwaita',
|
||||
'/usr/share/icons/hicolor', '/usr/share/fonts/truetype/dejavu',
|
||||
'/usr/share/mime',
|
||||
'/etc/fonts', '/usr/share/fontconfig']:
|
||||
copy(path)
|
||||
# Include GTK/libadwaita's German translations as well as the app's own catalog.
|
||||
for path in Path('/usr/share/locale').glob('de*/LC_MESSAGES/*.mo'):
|
||||
if path.name.startswith(('gtk40', 'libadwaita', 'glib20', 'gdk-pixbuf')):
|
||||
copy(path)
|
||||
roots = list(APPDIR.rglob('*.so')) + [APPDIR / 'usr/bin/python3.12']
|
||||
roots += [Path('/usr/lib/x86_64-linux-gnu') / name for name in
|
||||
['libgtk-4.so.1', 'libadwaita-1.so.0', 'libdbusmenu-glib.so.4']]
|
||||
# ldd includes the transitive ELF dependencies. glibc and the ELF loader belong
|
||||
# to the host; Ubuntu 24.04 (glibc 2.39) is the documented compatibility floor.
|
||||
host_libraries = re.compile(r'^(ld-linux.*|lib(c|m|dl|rt|pthread|resolv|util|nss_.*)\.so\..*)$')
|
||||
libraries = set(roots[-3:])
|
||||
for path in roots:
|
||||
for line in output('ldd', str(path)).splitlines():
|
||||
if 'not found' in line:
|
||||
raise RuntimeError(f'Missing dependency of {path}: {line}')
|
||||
match = re.search(r'=> (/\S+)', line)
|
||||
if match and not host_libraries.match(Path(match[1]).name):
|
||||
libraries.add(Path(match[1]))
|
||||
for path in sorted(libraries):
|
||||
copy(path, 'usr/lib/x86_64-linux-gnu/' + path.name)
|
||||
|
||||
copy(ROOT / 'btd700', 'usr/share/btd700-control/btd700')
|
||||
for name in ['install.py', 'LICENSE', 'THIRD_PARTY_NOTICES.md']:
|
||||
copy(ROOT / name, 'usr/share/btd700-control/' + name)
|
||||
copy(ROOT / 'packaging/btd700-control.svg', 'btd700-control.svg')
|
||||
copy(ROOT / 'packaging/btd700-control.svg', 'usr/share/icons/hicolor/scalable/apps/btd700-control.svg')
|
||||
copy(ROOT / 'packaging/70-btd700-control.rules', 'usr/share/btd700-control/packaging/70-btd700-control.rules')
|
||||
copy(ROOT / 'packaging/appimage/AppRun', 'AppRun')
|
||||
copy(ROOT / 'packaging/appimage/environment.sh', 'usr/share/btd700-control/appimage-environment.sh')
|
||||
(APPDIR / 'AppRun').chmod(0o755)
|
||||
copy(ROOT / 'packaging/appimage/btd700-control.desktop', 'btd700-control.desktop')
|
||||
(APPDIR / '.DirIcon').symlink_to('btd700-control.svg')
|
||||
subprocess.run(['desktop-file-validate', str(APPDIR / 'btd700-control.desktop')], check=True)
|
||||
subprocess.run(['glib-compile-schemas', str(APPDIR / 'usr/share/glib-2.0/schemas')], check=True)
|
||||
cache = output('/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/gdk-pixbuf-query-loaders')
|
||||
# A bare module name is resolved by dlopen through our LD_LIBRARY_PATH. This
|
||||
# avoids storing a container or temporary AppImage mount path in the cache.
|
||||
cache = re.sub(r'^"[^"\n]*/([^/"\n]+\.so)"$', r'"\1"', cache, flags=re.MULTILINE)
|
||||
(APPDIR / 'usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders.cache').write_text(cache)
|
||||
# Make the bundled fonts available without relying on a host font installation.
|
||||
font_config = APPDIR / 'etc/fonts/fonts.conf'
|
||||
font_config.write_text(font_config.read_text().replace('<fontconfig>',
|
||||
'<fontconfig>\n <dir prefix="relative">../../usr/share/fonts</dir>'))
|
||||
|
||||
# License texts accompany the binary; complete corresponding Ubuntu source
|
||||
# archives are downloaded separately by build.sh and attached to the release.
|
||||
copy('/usr/share/common-licenses')
|
||||
for package in sorted(included):
|
||||
doc = Path('/usr/share/doc') / package.split(':')[0] / 'copyright'
|
||||
if not doc.is_file():
|
||||
raise RuntimeError(f'No copyright file for {package}')
|
||||
copy(doc, 'usr/share/doc/btd700-bundled/' + package.replace(':', '_') + '.copyright')
|
||||
manifest = [packages[name] for name in sorted(included)]
|
||||
(APPDIR / 'usr/share/doc/btd700-bundled/packages.json').write_text(json.dumps(manifest, indent=2) + '\n')
|
||||
(ROOT / 'build/appimage/packages.json').write_text(json.dumps(manifest, indent=2) + '\n')
|
||||
sources = sorted({f"{p['source']}={p['source_version']}" for p in manifest})
|
||||
(ROOT / 'build/appimage/sources.txt').write_text('\n'.join(sources) + '\n')
|
||||
# Do not ship bytecode, Python tests, or compilation-only metadata.
|
||||
for path in list(APPDIR.rglob('__pycache__')):
|
||||
shutil.rmtree(path)
|
||||
for path in [APPDIR / 'usr/lib/python3.12/test', APPDIR / 'usr/lib/python3.12/config-3.12-x86_64-linux-gnu']:
|
||||
if path.exists():
|
||||
shutil.rmtree(path)
|
||||
print(f'Assembled {APPDIR}: {len(manifest)} binary packages, {len(sources)} source packages')
|
||||
@@ -0,0 +1,11 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=BTD 700 Control
|
||||
Comment=Linux controls for the Sennheiser BTD 700
|
||||
Comment[de]=Linux-Steuerung für den Sennheiser BTD 700
|
||||
Exec=btd700-control
|
||||
Icon=btd700-control
|
||||
Terminal=false
|
||||
Categories=AudioVideo;Audio;
|
||||
Keywords=Sennheiser;Bluetooth;aptX;Auracast;Dongle;
|
||||
StartupWMClass=io.github.btd700linux.Control
|
||||
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env bash
|
||||
# Run inside the Ubuntu 24.04 builder (see docs/APPIMAGE.md).
|
||||
set -euo pipefail
|
||||
cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.."
|
||||
repo_dir=$PWD
|
||||
version=$(python3 -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')
|
||||
build_dir="$repo_dir/build/appimage"
|
||||
sources_dir="$build_dir/dependency-sources"
|
||||
mkdir -p "$build_dir/downloads" "$sources_dir" dist
|
||||
|
||||
download() {
|
||||
local url=$1 name=$2 checksum=$3
|
||||
if [[ ! -f "$build_dir/downloads/$name" ]]; then
|
||||
curl --fail --location --retry 3 "$url" -o "$build_dir/downloads/$name"
|
||||
fi
|
||||
echo "$checksum $build_dir/downloads/$name" | sha256sum --check
|
||||
}
|
||||
download https://github.com/AppImage/appimagetool/releases/download/1.9.1/appimagetool-x86_64.AppImage appimagetool \
|
||||
ed4ce84f0d9caff66f50bcca6ff6f35aae54ce8135408b3fa33abfc3cb384eb0
|
||||
download https://github.com/AppImage/type2-runtime/releases/download/20251108/runtime-x86_64 runtime-x86_64 \
|
||||
2fca8b443c92510f1483a883f60061ad09b46b978b2631c807cd873a47ec260d
|
||||
chmod +x "$build_dir/downloads/appimagetool"
|
||||
python3 packaging/appimage/assemble.py
|
||||
|
||||
# Keep the exact corresponding Ubuntu sources alongside the downloadable binary.
|
||||
# apt checks the archives against the signed repository's source index.
|
||||
cd "$sources_dir"
|
||||
while IFS= read -r package; do
|
||||
apt-get source --download-only "$package"
|
||||
done < "$build_dir/sources.txt"
|
||||
curl --fail --location --retry 3 https://github.com/AppImage/type2-runtime/archive/refs/tags/20251108.tar.gz -o type2-runtime-20251108.tar.gz
|
||||
download https://github.com/libfuse/libfuse/releases/download/fuse-3.15.0/fuse-3.15.0.tar.xz fuse-3.15.0.tar.xz \
|
||||
70589cfd5e1cff7ccd6ac91c86c01be340b227285c5e200baa284e401eea2ca0
|
||||
download https://github.com/vasi/squashfuse/archive/0.5.2.tar.gz squashfuse-0.5.2.tar.gz \
|
||||
db0238c5981dabbd80ee09ae15387f390091668ca060a7bc38047912491443d3
|
||||
cp "$build_dir/downloads/fuse-3.15.0.tar.xz" "$build_dir/downloads/squashfuse-0.5.2.tar.gz" .
|
||||
cp "$build_dir/packages.json" "$build_dir/sources.txt" .
|
||||
rm -rf -- "$sources_dir/build-instructions"
|
||||
cp -r "$repo_dir/packaging/appimage" build-instructions
|
||||
cp "$repo_dir/docs/APPIMAGE.md" README.md
|
||||
tar -xOf type2-runtime-20251108.tar.gz type2-runtime-20251108/LICENSE > "$build_dir/BTD_700_Control.AppDir/usr/share/doc/btd700-bundled/AppImage-runtime.LICENSE"
|
||||
tar -xOf fuse-3.15.0.tar.xz fuse-3.15.0/LGPL2.txt > "$build_dir/BTD_700_Control.AppDir/usr/share/doc/btd700-bundled/libfuse.LICENSE"
|
||||
tar -xOf squashfuse-0.5.2.tar.gz squashfuse-0.5.2/LICENSE > "$build_dir/BTD_700_Control.AppDir/usr/share/doc/btd700-bundled/squashfuse.LICENSE"
|
||||
# Permissive runtime dependency notices (the upstream runtime LICENSE links these).
|
||||
curl -fsSL --retry 3 https://git.musl-libc.org/cgit/musl/plain/COPYRIGHT?h=v1.2.5 -o "$build_dir/BTD_700_Control.AppDir/usr/share/doc/btd700-bundled/musl.COPYRIGHT"
|
||||
curl -fsSL --retry 3 https://raw.githubusercontent.com/facebook/zstd/v1.5.6/LICENSE -o "$build_dir/BTD_700_Control.AppDir/usr/share/doc/btd700-bundled/zstd.LICENSE"
|
||||
curl -fsSL --retry 3 https://raw.githubusercontent.com/madler/zlib/v1.3.1/LICENSE -o "$build_dir/BTD_700_Control.AppDir/usr/share/doc/btd700-bundled/zlib.LICENSE"
|
||||
cd "$repo_dir"
|
||||
archive="BTD_700_Control-$version-dependency-sources.tar.gz"
|
||||
tar --exclude='__pycache__' -czf "dist/$archive" -C "$build_dir" dependency-sources
|
||||
ARCH=x86_64 VERSION="$version" APPIMAGE_EXTRACT_AND_RUN=1 "$build_dir/downloads/appimagetool" \
|
||||
--no-appstream --mksquashfs-opt -processors --mksquashfs-opt 2 \
|
||||
--runtime-file "$build_dir/downloads/runtime-x86_64" \
|
||||
"$build_dir/BTD_700_Control.AppDir" "dist/BTD_700_Control-$version-x86_64.AppImage"
|
||||
cp "$build_dir/packages.json" "dist/BTD_700_Control-$version-packages.json"
|
||||
cp packaging/70-btd700-control.rules dist/
|
||||
cd dist
|
||||
sha256sum "BTD_700_Control-$version-x86_64.AppImage" "$archive" \
|
||||
"BTD_700_Control-$version-packages.json" 70-btd700-control.rules > SHA256SUMS
|
||||
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env bash
|
||||
# Run in the builder with: dbus-run-session -- xvfb-run -a bash packaging/appimage/check.sh
|
||||
set -euo pipefail
|
||||
repo_dir=$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)
|
||||
appimages=("$repo_dir"/dist/*-x86_64.AppImage)
|
||||
[[ ${#appimages[@]} == 1 ]] || { echo 'Expected exactly one AppImage in dist/' >&2; exit 1; }
|
||||
test_dir=$(mktemp -d)
|
||||
trap 'rm -rf -- "$test_dir"' EXIT
|
||||
export XDG_CONFIG_HOME="$test_dir/config" XDG_DATA_HOME="$test_dir/data"
|
||||
appimage=${appimages[0]}
|
||||
# No accessibility service runs inside this private headless test session.
|
||||
export GTK_A11Y=none
|
||||
APPIMAGE_EXTRACT_AND_RUN=1 "$appimage" --language en --demo status
|
||||
APPIMAGE_EXTRACT_AND_RUN=1 "$appimage" --install-desktop
|
||||
desktop-file-validate "$XDG_DATA_HOME/applications/io.github.btd700linux.Control.desktop"
|
||||
APPIMAGE_EXTRACT_AND_RUN=1 "$appimage" --remove-desktop
|
||||
test ! -e "$XDG_DATA_HOME/applications/io.github.btd700linux.Control.desktop"
|
||||
cd "$test_dir"
|
||||
"$appimage" --appimage-extract > /dev/null
|
||||
export APPDIR="$test_dir/squashfs-root"
|
||||
. "$APPDIR/usr/share/btd700-control/appimage-environment.sh"
|
||||
"$APPDIR/usr/bin/python3.12" - <<'PY'
|
||||
import os, sys, gi
|
||||
from pathlib import Path
|
||||
gi.require_version('GdkPixbuf', '2.0')
|
||||
from gi.repository import GdkPixbuf
|
||||
root = Path(os.environ['APPDIR'])
|
||||
assert Path(sys.prefix).is_relative_to(root)
|
||||
assert Path(gi.__file__).is_relative_to(root)
|
||||
icon = GdkPixbuf.Pixbuf.new_from_file(str(root / 'btd700-control.svg'))
|
||||
assert icon.get_width() == 128
|
||||
print('PASS: bundled Python, PyGObject, SVG loader and MIME recognition')
|
||||
PY
|
||||
export BTD700_TEST_INSTALLED=1 BTD700_TEST_TRAY_HOST=1
|
||||
for language in en de; do
|
||||
BTD700_LANGUAGE=$language "$APPDIR/usr/bin/python3.12" "$repo_dir/tools/check_gui.py"
|
||||
done
|
||||
# Also exercise the actual runtime/extraction entry point into the GUI.
|
||||
"$APPDIR/usr/bin/python3.12" "$repo_dir/tools/tray_test_host.py" > "$test_dir/tray.log" &
|
||||
tray_pid=$!
|
||||
trap 'kill "$tray_pid" 2>/dev/null || true; rm -rf -- "$test_dir"' EXIT
|
||||
for attempt in {1..50}; do
|
||||
if [[ $(cat "$test_dir/tray.log") == TEST_TRAY_READY ]]; then break; fi
|
||||
sleep 0.1
|
||||
done
|
||||
APPIMAGE_EXTRACT_AND_RUN=1 "$appimage" --demo --smoke-seconds 3
|
||||
@@ -0,0 +1,14 @@
|
||||
# Shared by the launcher and packaged-runtime verification. APPDIR must be set.
|
||||
export PYTHONHOME="$APPDIR/usr"
|
||||
export PYTHONPATH="$APPDIR/usr/lib/python3/dist-packages:$APPDIR/usr/share/btd700-control"
|
||||
export PYTHONNOUSERSITE=1
|
||||
export PYTHONDONTWRITEBYTECODE=1
|
||||
export LD_LIBRARY_PATH="$APPDIR/usr/lib/x86_64-linux-gnu:$APPDIR/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
||||
export GDK_PIXBUF_MODULE_FILE="$APPDIR/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders.cache"
|
||||
export GI_TYPELIB_PATH="$APPDIR/usr/lib/x86_64-linux-gnu/girepository-1.0"
|
||||
export GSETTINGS_SCHEMA_DIR="$APPDIR/usr/share/glib-2.0/schemas"
|
||||
export XDG_DATA_DIRS="$APPDIR/usr/share:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
|
||||
export FONTCONFIG_FILE="$APPDIR/etc/fonts/fonts.conf"
|
||||
export FONTCONFIG_PATH="$APPDIR/etc/fonts"
|
||||
# A control panel does not need GPU rendering or host-specific Mesa drivers.
|
||||
export GSK_RENDERER=${GSK_RENDERER:-cairo}
|
||||
Reference in New Issue
Block a user