diff --git a/debian/.gitignore b/debian/.gitignore new file mode 100644 index 00000000..cf56f2dd --- /dev/null +++ b/debian/.gitignore @@ -0,0 +1,6 @@ +.debhelper/ +debhelper-build-stamp +files +libx11-compat/ +libx11-compat.debhelper.log +libx11-compat.substvars diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 00000000..8a1b938c --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +libx11-compat (0.1.0) unstable; urgency=medium + + * Initial packaging. + + -- libx11-compat maintainers Tue, 04 Aug 2026 17:00:00 +0000 diff --git a/debian/control b/debian/control new file mode 100644 index 00000000..593afd2b --- /dev/null +++ b/debian/control @@ -0,0 +1,30 @@ +Source: libx11-compat +Section: libs +Priority: optional +Maintainer: libx11-compat maintainers +Build-Depends: debhelper-compat (= 13), + pkgconf, + python3, + libsdl2-dev, + libsdl2-ttf-dev, + libpixman-1-dev +Standards-Version: 4.6.2 +Homepage: https://github.com/sysprog21/libx11-compat +Vcs-Git: https://github.com/sysprog21/libx11-compat.git +Vcs-Browser: https://github.com/sysprog21/libx11-compat +Rules-Requires-Root: no + +Package: libx11-compat +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: in-process Xlib implementation layered on SDL + libx11-compat is an in-process implementation of the X Window System + client library (Xlib) layered on top of SDL (SDL2 or SDL3), SDL_ttf, + and pixman. It lets existing Xlib clients keep their source unchanged + while running on platforms where a conventional X server is + unavailable or inconvenient: Wayland-only sessions, headless CI, and + similar environments. + . + This package ships the compat shared libraries (libX11-compat.so and + friends) with standard-name aliases plus the public X11 API headers, + so a downstream build can link against the shim as an ordinary X11. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 00000000..9fe12436 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,27 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: libx11-compat +Source: https://github.com/sysprog21/libx11-compat + +Files: * +Copyright: 2016-2019, 2022 Sebastian Scholz + 2026 National Cheng Kung University, Taiwan +License: MIT + +License: MIT + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. diff --git a/debian/rules b/debian/rules new file mode 100755 index 00000000..e5fadbd5 --- /dev/null +++ b/debian/rules @@ -0,0 +1,29 @@ +#!/usr/bin/make -f +# Note: the build stages pinned upstream Xorg headers via +# scripts/sync-upstream-headers.py, which downloads sha256-pinned +# tarballs. For an offline build, place those tarballs in a directory +# outside the source tree (the clean target wipes the in-tree cache) +# and export LIBX11_COMPAT_TARBALL_DIR pointing at it; the script then +# seeds from that directory instead of the network. + +export DEB_BUILD_MAINT_OPTIONS = hardening=+all + +%: + dh $@ + + +# Install into a private prefix: the shipped headers (X11/, GL/, ...) +# and libX11.so aliases would otherwise clash with the real X11 dev +# packages. Downstreams point their build at this tree, e.g. +# ./configure --with-libx11-compat=/usr/lib/libx11-compat +override_dh_auto_install: + dh_auto_install -- PREFIX=/usr/lib/libx11-compat + +override_dh_auto_test: + # The full check suite needs a display and optional workloads; + # skip it during package builds. + +# The compat libraries deliberately carry unversioned sonames +# (libX11-compat.so, ...); skip symbols/shlibs generation. +override_dh_makeshlibs: + dh_makeshlibs -n diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 00000000..89ae9db8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/packaging/README.md b/packaging/README.md new file mode 100644 index 00000000..cbedf467 --- /dev/null +++ b/packaging/README.md @@ -0,0 +1,52 @@ +# Packaging recipes + +Distribution packaging recipes for libx11-compat. Each recipe builds the +compat shared libraries with standard-name aliases plus the public X11 +API headers via `make install` (see `mk/install.mk`), into the private +prefix `/usr/lib/libx11-compat` (`%{_libdir}/libx11-compat` on RPM +distros): the shipped headers (`X11/`, `GL/`, ...) and `libX11.so` +aliases would clash with the real X11 dev packages under `/usr`. +Downstreams point their build at this tree, e.g. +`./configure --with-libx11-compat=/usr/lib/libx11-compat`. + +Note for all recipes: the build stages pinned upstream Xorg headers via +`scripts/sync-upstream-headers.py`, which downloads sha256-pinned +tarballs. For offline builds (sbuild, mock/koji, or a clean chroot +without network), the script seeds those tarballs from the directory +named by `LIBX11_COMPAT_TARBALL_DIR` instead of the network, verifying +the pinned sha256 either way. The RPM spec declares the tarballs as +`Source1:`..`Source6:` and points the variable at `%{_sourcedir}`; the +PKGBUILD lists them in `source=()` (checksummed by makepkg) and points +the variable at `$srcdir`; for Debian, place the tarballs in a directory +outside the source tree (the clean target wipes the in-tree cache) and +export the variable before a direct `dpkg-buildpackage`. Under sbuild +the build runs in a clean chroot where the caller's environment does +not reach the inner make, so inject both the tarballs and the variable +into the chroot explicitly (e.g. copy the tarballs in with +`--chroot-setup-commands` or a bind mount, and pass the variable via +`$build_environment` in `.sbuildrc`). + +## Debian / Ubuntu / Pardus (`../debian/`) + +debhelper-13 packaging at the repository root. Build with: + +```sh +dpkg-buildpackage -us -uc -b +``` + +The changelog targets `unstable`; retarget the distribution field when +uploading to a specific distro. + +## RPM (Fedora / RHEL / openSUSE) (`rpm/libx11-compat.spec`) + +```sh +rpmbuild -ba packaging/rpm/libx11-compat.spec +``` + +## Arch Linux (AUR) (`aur/PKGBUILD`) + +A `-git` package tracking the main branch: + +```sh +cd packaging/aur && makepkg -si +``` diff --git a/packaging/aur/PKGBUILD b/packaging/aur/PKGBUILD new file mode 100644 index 00000000..0e9c7ae7 --- /dev/null +++ b/packaging/aur/PKGBUILD @@ -0,0 +1,58 @@ +# Maintainer: libx11-compat maintainers +pkgname=libx11-compat-git +pkgver=r1.0000000 +pkgrel=1 +pkgdesc="In-process Xlib implementation layered on SDL (SDL2/SDL3), SDL_ttf, and pixman" +arch=('x86_64' 'aarch64') +url="https://github.com/sysprog21/libx11-compat" +license=('MIT') +depends=('sdl2' 'sdl2_ttf' 'pixman') +makedepends=('git' 'python' 'pkgconf') +provides=('libx11-compat') +conflicts=('libx11-compat') +# The pinned upstream Xorg tarballs the build stages headers/sources +# from are declared as sources so makepkg downloads and checksums them; +# scripts/sync-upstream-headers.py seeds them from $srcdir via +# LIBX11_COMPAT_TARBALL_DIR (re-verifying the pinned sha256) instead of +# fetching over the network mid-build. +source=("libx11-compat::git+$url.git" + 'https://xorg.freedesktop.org/archive/individual/lib/libX11-1.8.13.tar.xz' + 'https://xorg.freedesktop.org/archive/individual/proto/xorgproto-2025.1.tar.xz' + 'https://xorg.freedesktop.org/archive/individual/lib/libXt-1.3.1.tar.xz' + 'https://xorg.freedesktop.org/archive/individual/lib/libXpm-3.5.19.tar.xz' + 'https://xorg.freedesktop.org/archive/individual/lib/libXmu-1.3.1.tar.xz' + 'https://xorg.freedesktop.org/archive/individual/lib/libXaw-1.0.16.tar.xz') +noextract=('libX11-1.8.13.tar.xz' + 'xorgproto-2025.1.tar.xz' + 'libXt-1.3.1.tar.xz' + 'libXpm-3.5.19.tar.xz' + 'libXmu-1.3.1.tar.xz' + 'libXaw-1.0.16.tar.xz') +sha256sums=('SKIP' + '69606f485c2c07c14ef64f75b7bb326d48587af33795d9ab3e607c0b5f94f11c' + '56898c716c0578df8a2d828c9c3e5c528277705c0484381a81960fe1a67668e8' + 'e0a774b33324f4d4c05b199ea45050f87206586d81655f8bef4dba434d931288' + 'ad3576d689221a39dc728f0e0dc02ca7bb6a0d724c9a77fd1bfa1e9af83be900' + '81a99e94c4501e81c427cbaa4a11748b584933e94b7a156830c3621256857bc4' + '731d572b54c708f81e197a6afa8016918e2e06dfd3025e066ca642a5b8c39c8f') + +pkgver() { + cd libx11-compat + printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)" +} + +build() { + cd libx11-compat + LIBX11_COMPAT_TARBALL_DIR="$srcdir" make +} + +package() { + cd libx11-compat + # Install into a private prefix: the shipped headers (X11/, GL/, ...) + # and libX11.so aliases would otherwise clash with the real X11 dev + # packages. Downstreams point their build at this tree, e.g. + # ./configure --with-libx11-compat=/usr/lib/libx11-compat + make install PREFIX=/usr/lib/libx11-compat DESTDIR="$pkgdir" + install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" + install -Dm644 README.md "$pkgdir/usr/share/doc/$pkgname/README.md" +} diff --git a/packaging/rpm/libx11-compat.spec b/packaging/rpm/libx11-compat.spec new file mode 100644 index 00000000..bf30b009 --- /dev/null +++ b/packaging/rpm/libx11-compat.spec @@ -0,0 +1,70 @@ +# Pin the source to a fixed commit so the NEVRA always maps to the same +# code; bump %%commit (and Version/Release) when updating the package. +%global commit 93e0505ce8880115187b32d80d25535f1cc66fe2 +%global shortcommit %(c=%{commit}; echo ${c:0:7}) + +# Install into a private prefix: the shipped headers (X11/, GL/, ...) +# and libX11.so aliases would otherwise clash with the real X11 dev +# packages. Downstreams point their build at this tree, e.g. +# ./configure --with-libx11-compat=%%{_libdir}/libx11-compat +%global compat_prefix %{_libdir}/libx11-compat + +Name: libx11-compat +Version: 0.1.0 +Release: 1.20260804git%{shortcommit}%{?dist} +Summary: In-process Xlib implementation layered on SDL + +License: MIT +URL: https://github.com/sysprog21/libx11-compat +Source0: %{url}/archive/%{commit}/%{name}-%{shortcommit}.tar.gz +# Pinned upstream Xorg tarballs the build stages headers/sources from +# (see scripts/sync-upstream-headers.py, which verifies their sha256). +# Declaring them as sources keeps the build fully offline in mock/koji. +Source1: https://xorg.freedesktop.org/archive/individual/lib/libX11-1.8.13.tar.xz +Source2: https://xorg.freedesktop.org/archive/individual/proto/xorgproto-2025.1.tar.xz +Source3: https://xorg.freedesktop.org/archive/individual/lib/libXt-1.3.1.tar.xz +Source4: https://xorg.freedesktop.org/archive/individual/lib/libXpm-3.5.19.tar.xz +Source5: https://xorg.freedesktop.org/archive/individual/lib/libXmu-1.3.1.tar.xz +Source6: https://xorg.freedesktop.org/archive/individual/lib/libXaw-1.0.16.tar.xz + +BuildRequires: gcc +BuildRequires: make +BuildRequires: pkgconfig +BuildRequires: python3 +BuildRequires: pkgconfig(sdl2) +BuildRequires: pkgconfig(SDL2_ttf) +BuildRequires: pkgconfig(pixman-1) + +%description +libx11-compat is an in-process implementation of the X Window System +client library (Xlib) layered on top of SDL (SDL2 or SDL3), SDL_ttf, +and pixman. It lets existing Xlib clients keep their source unchanged +while running on platforms where a conventional X server is +unavailable or inconvenient: Wayland-only sessions, headless CI, and +similar environments. + +This package ships the compat shared libraries (libX11-compat.so and +friends) with standard-name aliases plus the public X11 API headers, +so a downstream build can link against the shim as an ordinary X11. + +%prep +%autosetup -n %{name}-%{commit} + +# The staging script seeds the pinned upstream tarballs from +# LIBX11_COMPAT_TARBALL_DIR (sha256-verified) instead of the network, +# so the build works in an offline mock/koji chroot. +%build +export LIBX11_COMPAT_TARBALL_DIR=%{_sourcedir} +%make_build + +%install +%make_install PREFIX=%{compat_prefix} + +%files +%license LICENSE +%doc README.md +%{compat_prefix}/ + +%changelog +* Tue Aug 04 2026 libx11-compat maintainers - 0.1.0-1.20260804git93e0505 +- Initial packaging. diff --git a/scripts/sync-upstream-headers.py b/scripts/sync-upstream-headers.py index 77dcb953..e4b49c82 100644 --- a/scripts/sync-upstream-headers.py +++ b/scripts/sync-upstream-headers.py @@ -12,7 +12,10 @@ ``fetch DEST [--force]`` Download (cached) tarballs and extract headers into ``DEST/X11/`` and the whitelisted libX11 ``src/`` files into ``DEST/../src/``. Idempotent - through ``DEST/.upstream-stamp``. + through ``DEST/.upstream-stamp``. For offline builds, set + ``LIBX11_COMPAT_TARBALL_DIR`` to a directory holding the pinned + tarballs; entries whose sha256 matches the pin are used instead of + downloading. ``diff`` Compare local ``include/X11/`` to upstream and classify each path as @@ -478,6 +481,23 @@ def download(url: str, expected_sha: str) -> Path: file=sys.stderr, ) dest.unlink(missing_ok=True) + # Offline builds (distro packaging, network-restricted chroots) can drop + # the pinned tarballs into a directory and point this variable at it; a + # tarball whose sha256 matches the pin is used instead of the network. + # The cache under build/ does not survive `make distclean`, so the seed + # directory should live outside the build tree. + seed_dir = os.environ.get("LIBX11_COMPAT_TARBALL_DIR") + if seed_dir: + candidate = Path(seed_dir) / dest.name + if candidate.is_file(): + if sha256_of(candidate) == expected_sha: + print(f" SEED {candidate}", file=sys.stderr) + shutil.copyfile(candidate, dest) + return dest + print( + f" WARN seed {candidate} has unexpected sha256; ignoring", + file=sys.stderr, + ) print(f" FETCH {url}", file=sys.stderr) tmp_fd, tmp_name = tempfile.mkstemp(dir=CACHE_DIR, prefix=".dl-") os.close(tmp_fd) @@ -509,8 +529,8 @@ def download(url: str, expected_sha: str) -> Path: if last_exc is not None: raise SystemExit( f"failed to download {url} after {attempts} attempts: {last_exc}\n" - f" pre-seed the cache at {CACHE_DIR}/{dest.name} " - f"to build offline" + f" to build offline, place {dest.name} in a directory and\n" + f" set LIBX11_COMPAT_TARBALL_DIR to that directory" ) from last_exc actual = sha256_of(tmp_path) if actual != expected_sha: diff --git a/src/wrapper/sdl-ttf-wrapper.c b/src/wrapper/sdl-ttf-wrapper.c index 9cae2119..2f9d1693 100644 --- a/src/wrapper/sdl-ttf-wrapper.c +++ b/src/wrapper/sdl-ttf-wrapper.c @@ -57,20 +57,9 @@ TTF_WRAP(char *, TTF_FontFaceFamilyName, (const TTF_Font *font), (font)) TTF_WRAP(int, TTF_FontFaceIsFixedWidth, (const TTF_Font *font), (font)) TTF_WRAP(const char *, TTF_GetError, (void), ()) TTF_WRAP(int, TTF_GetFontStyle, (const TTF_Font *font), (font)) -#if SDL_TTF_VERSION_ATLEAST(2, 20, 0) TTF_WRAP(int, TTF_GlyphIsProvided, (TTF_Font * font, Uint16 ch), (font, ch)) -TTF_WRAP(int, TTF_GlyphIsProvided32, (TTF_Font * font, Uint32 ch), (font, ch)) -#else -TTF_WRAP(int, - TTF_GlyphIsProvided, - (const TTF_Font *font, Uint16 ch), - (font, ch)) #if SDL_TTF_VERSION_ATLEAST(2, 0, 18) -TTF_WRAP(int, - TTF_GlyphIsProvided32, - (const TTF_Font *font, Uint32 ch), - (font, ch)) -#endif +TTF_WRAP(int, TTF_GlyphIsProvided32, (TTF_Font * font, Uint32 ch), (font, ch)) #endif TTF_WRAP(int, TTF_GlyphMetrics,