Skip to content

Linux: enable HTML5 and Android builds, fix display.save() and network.requestId - #936

Open
chkuendig wants to merge 6 commits into
coronalabs:masterfrom
chkuendig:linux-builder-fixes
Open

Linux: enable HTML5 and Android builds, fix display.save() and network.requestId#936
chkuendig wants to merge 6 commits into
coronalabs:masterfrom
chkuendig:linux-builder-fixes

Conversation

@chkuendig

@chkuendig chkuendig commented Aug 14, 2026

Copy link
Copy Markdown

I recently started using Solar2D in Docker (mostly for Github Action automations and to use solar2d-mcp and noticed a few issues:

build fixes:

  • linux: enable the HTML5 builder in Solar2DBuilder 2a40a99
    CORONABUILDER_HTML5 was never defined for the Linux build, so Solar2DBuilder rejected html5 as an unknown platform. Defining it wires up the existing HTML5 packager that was already compiled in.
  • linux: enable Android builds in Solar2DBuilder 36cb5ce
    Same shape: CORONABUILDER_ANDROID undefined, plus
    AndroidValidation.lua wasn’t copied next to the builder, so the packager couldn’t find it at runtime. This supersedes fix(linux): enable Solar2DBuilder Android builds on Linux #891
  • linux: move builder runs to a temp directory 7608f1f
    The four sandbox paths started as empty strings, so appending gave /Documents, /TemporaryFiles, /CachedFiles and .system — the filesystem root, writable only by root. Each run now gets its own mkdtemp directory under TMPDIR (atomic, mode 0700), removed when the run ends, matching the per-process directory the Windows builder already creates.

screenshot fixes

  • linux: fix the channel order of saved framebuffer captures efd41a5
    A Linux capture is tagged kBGRA, and both writers read that name as the byte order. The capture is actually A,R,G,B, so savePNG emitted G,R,A,B and saveJPG emitted G,R,A — red and green swapped, blue taken from the alpha byte. Also rejects formats no caller produces rather than misreading them, and fixes a malloc/unique_ptr-deleter mismatch.
  • linux: honour display.save()'s jpegQuality edda932
    LinuxBaseBitmap::SaveBitmap had no quality parameter, so LinuxPlatform::SaveBitmap discarded the caller’s value and passed a literal 75 into an API expecting 0–1 and saveJPG set quality before jpeg_set_defaults, which resets it.
    network fixes
  • linux: fix requestId handling in the network library f14cc67
    fRequestID was never assigned and never read, so event.requestId came back nil even though network.request() had handed the caller an id — listeners had no way to match a response to its request. The id now lands between "url" and "bytesTransferred", the slot the Apple and Windows backends use, and the field is unsigned to match the counter.
    Separately, network.cancel() fed lua_tonumber straight into the request map: a missing or invalid argument all became 0 and could cancel a request the caller never named, returning true. The argument is now validated as a finite integral number in range.

@chkuendig
chkuendig requested a review from Shchvova as a code owner August 14, 2026 16:15
@CLAassistant

CLAassistant commented Aug 14, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

The packager is already compiled in; only CORONABUILDER_HTML5 was missing, so
the binary answered that HTML5 builds are not supported on this operating
system.

A build still needs a real webtemplate.zip. It is produced by the emscripten
target and reaches a packaged build as a CI artifact, so the copy in this tree
is an empty placeholder and the packager stops at "Failed to open template".
Defines CORONABUILDER_ANDROID and adds the Rtt_AndroidSupportTools.c the
factory calls from inside that gate.

GetResourceDirectory() gains a Linux arm. Under Rtt_LINUX_ENV it matched no
branch and ran off the end of a function returning const char *.

AndroidValidation.lua is installed with it: the packager loads that script from
disk with luaL_loadfile rather than through LUA_SOURCES, and stops at "Could
not find script file" without it.

GetStartupPath() checks readlink() before terminating the buffer. On failure it
returned -1 and the terminator was written one byte before the buffer.

Android builds also need android-template.zip and Corona.aar. Both are built by
platform/android/app/build.gradle.kts as part of Corona Native, which the Linux
cmake build does not invoke; a packaged build receives them as a CI artifact.
The four sandbox paths start as empty strings, so appending alone pointed them
at /Documents, /TemporaryFiles, /CachedFiles and /.system - the root of the
filesystem, which only root can write.

Each run now gets its own directory under TMPDIR, the same shape as the
per-process temporary directory the Windows builder creates, and it is removed
when the run finishes. mkdtemp makes it atomically with mode 0700; on Linux
TMPDIR is usually the shared /tmp.
On Linux a captured frame is tagged kBGRA. Both writers read that name as the
order the bytes sit in, so savePNG turned a captured pixel into G,R,A,B and
saveJPG into G,R,A: red and green swapped, blue taken from the alpha byte.
That is every display.save() on Linux.

The capture is read back as GL_BGRA with a packed 8_8_8_8 type, so its bytes
are A,R,G,B.

kRGB and kRGBA keep their existing byte order: the shared loaders and the GLES
capture Emscripten uses fill those buffers literally, so reordering them would
corrupt what they hand over. The format names are not used consistently across
the engine, so each case states the layout it is actually given.

Formats no caller produces are rejected rather than misread. saveJPG in
particular used to run a mask or luminance-alpha buffer through a three-byte
row stride.

saveJPG's conversion buffer came from malloc but was held in a std::unique_ptr
whose deleter calls delete. Both writers now allocate with new[] and let the
pointer free it.
NetworkRequestState carried an fRequestID that nothing ever assigned and
pushToLuaState never read, so event.requestId came back nil on Linux while
network.request() had already handed the caller an id. Listeners that key
their bookkeeping off the id — the pattern the mac and win32 backends
support — had no way to match a response to its request.

The id now travels from the request parameters onto the state the event is
built from, and lands in the event table between "url" and "bytesTransferred",
the slot AppleNetworkSupport.mm and WindowsNetworkSupport.cpp put it in. Those
two push a RequestCanceller object; Linux keeps the plain number it already
returns from network.request() and accepts in network.cancel().

fRequestID becomes unsigned to match the counter it now carries: the ids
originate as unsigned int in NetworkRequestParameters and are keyed that way
in the request map, so a signed field would have made the event disagree with
the returned value once the counter passed INT_MAX.

cancel() converted argument 1 with lua_tonumber and used the result straight
away as a key into the request map. That function answers 0 for a missing
argument, nil, false, a table or a non-numeric string, and truncates a
fractional one, so network.cancel(), network.cancel(nil) and
network.cancel(1.9) all named a request the caller never meant to cancel — and
returned true once they found it. Negative, infinite and out-of-range values
reached an undefined conversion to unsigned. The argument is now required to
be a Lua number holding a finite, integral value inside the range the ids are
allocated from, and only then converted. Anything else is reported through
paramValidationFailure and nothing is pushed, matching what the win32, apple
and android backends do with a handle they do not recognise.

paramValidationFailure's declaration is corrected to const char * so callers
outside NetworkSupport.cpp can reach the definition that is already written
that way.
display.save() clamps a jpegQuality between 0 and 1 and hands it to
SaveBitmap, but the Linux path dropped it: LinuxBaseBitmap::SaveBitmap took no
such parameter, so LinuxPlatform::SaveBitmap discarded the argument it was
given and a literal 75 went to saveJPG. That 75 is scaled by 100 and clamped,
so every save asked for quality 100 regardless of what the caller wanted.

saveJPG then set the quality before jpeg_set_defaults(), which resets it to
75. The value is now applied after the defaults, so it survives to the
encoder.
@chkuendig chkuendig changed the title Linux: enable HTML5 and Android builds, fix display.save() colours Linux: enable HTML5 and Android builds, fix display.save() and network.requestId Sep 2, 2026
@chkuendig
chkuendig marked this pull request as ready for review September 2, 2026 21:42
@chkuendig

Copy link
Copy Markdown
Author

@Shchvova Updated this as the initial version had some issues. Let me know if there's anything to change/clarify. I have a feeling the linux path has not been been very much exercised in the past, but having it for automations (Agentic AI or just CI/CD) it's super useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants