Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions debian/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.debhelper/
debhelper-build-stamp
files
libx11-compat/
libx11-compat.debhelper.log
libx11-compat.substvars
5 changes: 5 additions & 0 deletions debian/changelog
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
libx11-compat (0.1.0) unstable; urgency=medium

* Initial packaging.

-- libx11-compat maintainers <libx11-compat@users.noreply.github.com> Tue, 04 Aug 2026 17:00:00 +0000
30 changes: 30 additions & 0 deletions debian/control
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
Source: libx11-compat
Section: libs
Priority: optional
Maintainer: libx11-compat maintainers <libx11-compat@users.noreply.github.com>
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.
27 changes: 27 additions & 0 deletions debian/copyright
Original file line number Diff line number Diff line change
@@ -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.
29 changes: 29 additions & 0 deletions debian/rules
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions debian/source/format
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.0 (native)
Comment thread
erkinalp marked this conversation as resolved.
52 changes: 52 additions & 0 deletions packaging/README.md
Original file line number Diff line number Diff line change
@@ -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
```
58 changes: 58 additions & 0 deletions packaging/aur/PKGBUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Maintainer: libx11-compat maintainers <libx11-compat@users.noreply.github.com>
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"
}
70 changes: 70 additions & 0 deletions packaging/rpm/libx11-compat.spec
Original file line number Diff line number Diff line change
@@ -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
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
%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
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
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 <libx11-compat@users.noreply.github.com> - 0.1.0-1.20260804git93e0505
- Initial packaging.
26 changes: 23 additions & 3 deletions scripts/sync-upstream-headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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:
Expand Down
13 changes: 1 addition & 12 deletions src/wrapper/sdl-ttf-wrapper.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading