From 2a40a997ec2202fc003443ffadd5a4f179721503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=BCndig?= Date: Wed, 2 Sep 2026 16:20:58 +0200 Subject: [PATCH 1/6] linux: enable the HTML5 builder in Solar2DBuilder 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". --- platform/linux/CMakeList.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/linux/CMakeList.txt b/platform/linux/CMakeList.txt index ee1677998..a4043d7c3 100644 --- a/platform/linux/CMakeList.txt +++ b/platform/linux/CMakeList.txt @@ -1028,7 +1028,7 @@ target_compile_definitions( Solar2DSimulator PUBLIC target_compile_definitions( Solar2DBuilder PUBLIC Rtt_BUILD_REVISION=${BUILD_NUMBER} Rtt_BUILD_YEAR=${YEAR} - LUA_USE_POPEN Rtt_LUA_COMPILER Rtt_SIMULATOR CORONABUILDER_LINUX LUA_DL_DLOPEN + LUA_USE_POPEN Rtt_LUA_COMPILER Rtt_SIMULATOR CORONABUILDER_LINUX CORONABUILDER_HTML5 LUA_DL_DLOPEN Rtt_LINUX_ENV ALMIXER_COMPILE_WITHOUT_SDL SOUND_SUPPORTS_WAV SOUND_SUPPORTS_MPG123 SOUND_SUPPORTS_OGG OPT_GENERIC HAVE_STRERROR NO_REAL ENABLE_ALMIXER_THREADS LINUX_LIB) From 36cb5ce3f3584e71eb5732a28e67ca08ccc00fbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=BCndig?= Date: Wed, 2 Sep 2026 15:55:30 +0200 Subject: [PATCH 2/6] linux: enable Android builds in Solar2DBuilder 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. --- platform/linux/CMakeList.txt | 3 ++- platform/linux/CMakeResources.txt | 1 + platform/linux/src/Rtt_LinuxUtils.cpp | 5 +++++ .../Rtt_AppPackagerAndroidFactory.cpp | 2 ++ tools/CoronaBuilder/Rtt_AppPackagerFactory.cpp | 14 ++++++++++++++ 5 files changed, 24 insertions(+), 1 deletion(-) diff --git a/platform/linux/CMakeList.txt b/platform/linux/CMakeList.txt index a4043d7c3..d16e7995d 100644 --- a/platform/linux/CMakeList.txt +++ b/platform/linux/CMakeList.txt @@ -959,6 +959,7 @@ add_executable( Solar2DBuilder ${CORONA_ROOT}/tools/CoronaBuilder/main.cpp ${CORONA_ROOT}/tools/CoronaBuilder/Rtt_CoronaBuilder.cpp ${CORONA_ROOT}/tools/CoronaBuilder/Rtt_AppPackagerFactory.cpp + ${CORONA_ROOT}/platform/shared/Rtt_AndroidSupportTools.c ${CORONA_ROOT}/tools/CoronaBuilder/Rtt_AppPackagerAndroidFactory.cpp ${CORONA_ROOT}/tools/CoronaBuilder/Rtt_AppPackagerHTML5Factory.cpp ${CORONA_ROOT}/tools/CoronaBuilder/Rtt_AppPackagerLinuxFactory.cpp @@ -1028,7 +1029,7 @@ target_compile_definitions( Solar2DSimulator PUBLIC target_compile_definitions( Solar2DBuilder PUBLIC Rtt_BUILD_REVISION=${BUILD_NUMBER} Rtt_BUILD_YEAR=${YEAR} - LUA_USE_POPEN Rtt_LUA_COMPILER Rtt_SIMULATOR CORONABUILDER_LINUX CORONABUILDER_HTML5 LUA_DL_DLOPEN + LUA_USE_POPEN Rtt_LUA_COMPILER Rtt_SIMULATOR CORONABUILDER_LINUX CORONABUILDER_HTML5 CORONABUILDER_ANDROID LUA_DL_DLOPEN Rtt_LINUX_ENV ALMIXER_COMPILE_WITHOUT_SDL SOUND_SUPPORTS_WAV SOUND_SUPPORTS_MPG123 SOUND_SUPPORTS_OGG OPT_GENERIC HAVE_STRERROR NO_REAL ENABLE_ALMIXER_THREADS LINUX_LIB) diff --git a/platform/linux/CMakeResources.txt b/platform/linux/CMakeResources.txt index 0909be542..9d7fa718a 100644 --- a/platform/linux/CMakeResources.txt +++ b/platform/linux/CMakeResources.txt @@ -21,6 +21,7 @@ file(COPY "${CORONA_ROOT}/platform/resources/json.lua" DESTINATION ${RESOURCES} file(COPY "${CORONA_ROOT}/platform/resources/dkjson.lua" DESTINATION ${RESOURCES} FILE_PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) file(COPY "${CORONA_ROOT}/platform/resources/CoronaPListSupport.lua" DESTINATION ${RESOURCES} FILE_PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) file(COPY "${CORONA_ROOT}/platform/resources/AppSettings.lua" DESTINATION ${RESOURCES} FILE_PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) +file(COPY "${CORONA_ROOT}/platform/resources/AndroidValidation.lua" DESTINATION ${RESOURCES} FILE_PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) file(COPY "${CORONA_ROOT}/platform/android/create_build_properties.lua" DESTINATION ${RESOURCES} FILE_PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) file(COPY "${CORONA_ROOT}/platform/android/resources/build.xml" DESTINATION ${RESOURCES} FILE_PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) diff --git a/platform/linux/src/Rtt_LinuxUtils.cpp b/platform/linux/src/Rtt_LinuxUtils.cpp index c5077647f..74194ec0b 100644 --- a/platform/linux/src/Rtt_LinuxUtils.cpp +++ b/platform/linux/src/Rtt_LinuxUtils.cpp @@ -75,6 +75,11 @@ const char* GetStartupPath(string* exeFileName) { static char buf[PATH_MAX + 1]; ssize_t count = readlink("/proc/self/exe", buf, PATH_MAX); + if (count < 0) + { + // readlink() failed + count = 0; + } buf[count] = 0; // remove file name diff --git a/tools/CoronaBuilder/Rtt_AppPackagerAndroidFactory.cpp b/tools/CoronaBuilder/Rtt_AppPackagerAndroidFactory.cpp index 33bf5e06b..af077cc8c 100644 --- a/tools/CoronaBuilder/Rtt_AppPackagerAndroidFactory.cpp +++ b/tools/CoronaBuilder/Rtt_AppPackagerAndroidFactory.cpp @@ -138,6 +138,8 @@ AppPackagerFactory::CreatePackagerParamsAndroid( scriptPathStr.Set(GetResourceDirectory()); #if defined(Rtt_MAC_ENV) scriptPathStr.Append("/AndroidValidation.lu"); +#elif defined(Rtt_LINUX_ENV) + scriptPathStr.Append("/AndroidValidation.lua"); #elif defined(Rtt_WIN_ENV) scriptPathStr.Append("/AndroidValidation.lua"); #endif diff --git a/tools/CoronaBuilder/Rtt_AppPackagerFactory.cpp b/tools/CoronaBuilder/Rtt_AppPackagerFactory.cpp index 2c2c61317..ca7fdf737 100644 --- a/tools/CoronaBuilder/Rtt_AppPackagerFactory.cpp +++ b/tools/CoronaBuilder/Rtt_AppPackagerFactory.cpp @@ -32,6 +32,10 @@ #if defined(CORONABUILDER_LINUX) #include "Rtt_LinuxAppPackager.h" #endif +#if defined(Rtt_LINUX_ENV) +#include "Rtt_LinuxUtils.h" +#include +#endif #ifdef Rtt_WIN_ENV #include "Rtt_JavaHost.h" @@ -394,6 +398,16 @@ AppPackagerFactory::GetResourceDirectory() const return GetResourceDirectoryOSX(); #elif defined(Rtt_WIN_ENV) return GetResourceDirectoryWin(); +#elif defined(Rtt_LINUX_ENV) + // Resources live beside the executable in the Linux layout. + static std::string resourceDir; + if (resourceDir.empty()) + { + resourceDir = std::string(GetStartupPath(NULL)) + "/Resources"; + } + return resourceDir.c_str(); +#else + return NULL; #endif } From 7608f1f7f2bf87e40f046bea0718db865b353cfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=BCndig?= Date: Wed, 2 Sep 2026 15:55:39 +0200 Subject: [PATCH 3/6] linux: move builder runs to a temp directory 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. --- tools/CoronaBuilder/main.cpp | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/tools/CoronaBuilder/main.cpp b/tools/CoronaBuilder/main.cpp index cd6474979..177668f70 100644 --- a/tools/CoronaBuilder/main.cpp +++ b/tools/CoronaBuilder/main.cpp @@ -13,6 +13,11 @@ #include "Rtt_LinuxSimulatorView.h" #include "Rtt_LinuxUtils.h" #include +#include +#include +#include + +#include "Core/Rtt_FileSystem.h" using namespace std; @@ -27,11 +32,28 @@ int main(int argc, const char *argv[]) string systemCachesDir; string skinDir; - // setup directory paths - documentsDir.append("/Documents"); - temporaryDir.append("/TemporaryFiles"); - cachesDir.append("/CachedFiles"); - systemCachesDir.append("/.system"); + // create unique temporary dir + const char *tmpDir = getenv("TMPDIR"); + if (tmpDir == NULL || tmpDir[0] == '\0') + { + tmpDir = "/tmp"; + } + + string sandboxTemplate = string(tmpDir) + "/Solar2DBuilder-XXXXXX"; + vector sandboxPath(sandboxTemplate.begin(), sandboxTemplate.end()); + sandboxPath.push_back('\0'); + + if (Rtt_MakeTempDirectory(sandboxPath.data()) == NULL) + { + fprintf(stderr, "ERROR: failed to create a temporary directory under %s\n", tmpDir); + return 1; + } + + const string sandboxDir(sandboxPath.data()); + documentsDir = sandboxDir + "/Documents"; + temporaryDir = sandboxDir + "/TemporaryFiles"; + cachesDir = sandboxDir + "/CachedFiles"; + systemCachesDir = sandboxDir + "/.system"; Rtt::LinuxConsolePlatform *platform = new Rtt::LinuxConsolePlatform(pathToApp.c_str(), documentsDir.c_str(), temporaryDir.c_str(), cachesDir.c_str(), systemCachesDir.c_str(), skinDir.c_str(), GetStartupPath(NULL)); Rtt::LinuxPlatformServices services(platform); @@ -39,5 +61,8 @@ int main(int argc, const char *argv[]) Rtt::CoronaBuilder builder(*platform, services); result = builder.Main(argc, argv); + // remove this run's temporary dir + Rtt_DeleteDirectory(sandboxDir.c_str()); + return result; } From efd41a5c4ccbed0f64a241259cd96a1712858cb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=BCndig?= Date: Wed, 2 Sep 2026 22:51:38 +0200 Subject: [PATCH 4/6] linux: fix the channel order of saved framebuffer captures 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. --- platform/shared/Rtt_BitmapUtils.cpp | 83 +++++++++++++---------------- 1 file changed, 36 insertions(+), 47 deletions(-) diff --git a/platform/shared/Rtt_BitmapUtils.cpp b/platform/shared/Rtt_BitmapUtils.cpp index c969ce6fd..7bf0b3cf6 100644 --- a/platform/shared/Rtt_BitmapUtils.cpp +++ b/platform/shared/Rtt_BitmapUtils.cpp @@ -146,49 +146,39 @@ namespace bitmapUtil jpeg_set_defaults(&cinfo); jpeg_start_compress(&cinfo, TRUE); - std::unique_ptr rgb; + std::unique_ptr rgb; switch (format) { case Rtt::PlatformBitmap::Format::kRGB: break; case Rtt::PlatformBitmap::Format::kRGBA: - { - // convert to RGB - rgb.reset((uint8_t*)malloc(width * height * 3)); - uint8_t* src = data; - uint8_t* dst = rgb.get(); - for (int i = 0; i < width * height; i++) - { - *dst++ = *src++; - *dst++ = *src++; - *dst++ = *src++; - src++; - } - data = rgb.get(); - break; - } - case Rtt::PlatformBitmap::Format::kABGR: - case Rtt::PlatformBitmap::Format::kARGB: - Rtt_ASSERT(0); //todo - break; - case Rtt::PlatformBitmap::Format::kBGRA: { + // a kBGRA capture is A,R,G,B, so RGB starts one byte in + const int rgbOffset = (format == Rtt::PlatformBitmap::Format::kBGRA) ? 1 : 0; + // convert to RGB - rgb.reset((uint8_t*)malloc(width * height * 3)); - uint8_t* src = data; + rgb.reset(new uint8_t[(size_t)width * height * 3]); + const uint8_t* src = data; uint8_t* dst = rgb.get(); for (int i = 0; i < width * height; i++) { - *dst++ = src[2]; - *dst++ = src[1]; - *dst++ = src[0]; + dst[0] = src[rgbOffset]; + dst[1] = src[rgbOffset + 1]; + dst[2] = src[rgbOffset + 2]; + dst += 3; src += 4; } data = rgb.get(); break; } + + default: + Rtt_LogException("jpeg writer: unsupported pixel format\n"); + jpeg_destroy_compress(&cinfo); + fclose(outfile); + return false; } while (cinfo.next_scanline < cinfo.image_height) @@ -347,16 +337,21 @@ namespace bitmapUtil } bool savePNG(const char* filename, uint8_t* data, int width, int height, Rtt::PlatformBitmap::Format format) - // Writes a 24 or 32-bit color image in .png format, to the - // given output stream. Data should be in [RGB or RGBA...] byte order. { - int bpp = Rtt::PlatformBitmap::BytesPerPixel(format); - if (bpp != 3 && bpp != 4) + switch (format) { - Rtt_LogException("png writer: bpp must be 3 or 4\n"); + case Rtt::PlatformBitmap::Format::kRGB: + case Rtt::PlatformBitmap::Format::kRGBA: + case Rtt::PlatformBitmap::Format::kBGRA: + break; + + default: + Rtt_LogException("png writer: unsupported pixel format\n"); return false; } + int bpp = Rtt::PlatformBitmap::BytesPerPixel(format); + png_structp png_ptr; png_infop info_ptr; @@ -381,24 +376,23 @@ namespace bitmapUtil png_set_IHDR(png_ptr, info_ptr, width, height, 8, bpp == 3 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_write_info(png_ptr, info_ptr); - bool free_data = false; + std::unique_ptr rgba; if (format == Rtt::PlatformBitmap::Format::kBGRA) { - // BGRA ==> RGBA - U8* rgba = (U8*)malloc(width * height * 4); - U8* src = data; - U8* dst = rgba; + // a capture is A,R,G,B + rgba.reset(new U8[(size_t)width * height * 4]); + const U8* src = data; + U8* dst = rgba.get(); for (int i = 0; i < width * height; i++) { - dst[0] = src[2]; - dst[1] = src[1]; - dst[2] = src[0]; - dst[3] = src[3]; + dst[0] = src[1]; + dst[1] = src[2]; + dst[2] = src[3]; + dst[3] = src[0]; dst += 4; src += 4; } - data = rgba; - free_data = true; + data = rgba.get(); } for (int y = 0; y < height; y++) @@ -409,11 +403,6 @@ namespace bitmapUtil png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); - if (free_data) - { - free(data); - } - size_t bytes = 0; FILE* out = fopen(filename, "wb"); if (out) From f14cc672bd28b772c7b6d07300924e6e24abf0fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=BCndig?= Date: Wed, 2 Sep 2026 22:54:49 +0200 Subject: [PATCH 5/6] linux: fix requestId handling in the network library MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- platform/linux/src/NetworkLibrary.cpp | 28 ++++++++++++++++++++++++++- platform/linux/src/NetworkSupport.cpp | 4 ++++ platform/linux/src/NetworkSupport.h | 5 +++-- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/platform/linux/src/NetworkLibrary.cpp b/platform/linux/src/NetworkLibrary.cpp index daa4964fe..8639d5ab1 100644 --- a/platform/linux/src/NetworkLibrary.cpp +++ b/platform/linux/src/NetworkLibrary.cpp @@ -16,6 +16,7 @@ #include "Rtt_PlatformNotifier.h" #include "NetworkLibrary.h" #include "NetworkSupport.h" +#include int luaload_network(lua_State *L); @@ -345,6 +346,7 @@ int NetworkLibrary::sendRequest(lua_State *L) requestState->fResponseBody.bodyType = TYPE_NONE; requestState->fResponseBody.bodyBytes = NULL; requestState->setURL(requestParams->getRequestUrl()); + requestState->setRequestID(requestParams->getID()); requestState->setStatus(status); requestState->setPhase("ended"); requestState->setBytesEstimated(requestParams->fResponse.size()); @@ -370,8 +372,32 @@ int NetworkLibrary::sendRequest(lua_State *L) int NetworkLibrary::cancel(lua_State *L) { Self* thiz = NetworkLibrary::ToLibrary(L); + + // The id arrives as a plain number, and lua_tonumber() answers 0 for a missing + // argument, nil, false, a table or a non-numeric string, and truncates a + // fractional one. Converting before checking would let any of those name a + // request the caller never meant to cancel, and report success for it. + lua_Number requestIDValue = 0; + bool isValidRequestID = (lua_type(L, 1) == LUA_TNUMBER); + if (isValidRequestID) + { + requestIDValue = lua_tonumber(L, 1); + + // Ordered so that a NaN fails the range test instead of reaching the cast. + isValidRequestID = (requestIDValue >= 0 && requestIDValue <= (lua_Number)UINT_MAX); + isValidRequestID = isValidRequestID && ((lua_Number)(unsigned int)requestIDValue == requestIDValue); + } + + if (!isValidRequestID) + { + // The other backends log and return nothing rather than raising, so a bad + // argument stays a diagnosable no-op instead of a Lua error. + paramValidationFailure(L, "network.cancel() expects a requestId returned from a call to network.request()"); + return 0; + } + bool rc = false; - unsigned int requestID = lua_tonumber(L, 1); + unsigned int requestID = (unsigned int)requestIDValue; auto it = thiz->fRequests.find(requestID); if (it != thiz->fRequests.end()) { diff --git a/platform/linux/src/NetworkSupport.cpp b/platform/linux/src/NetworkSupport.cpp index dd422db7b..451cf8f3c 100644 --- a/platform/linux/src/NetworkSupport.cpp +++ b/platform/linux/src/NetworkSupport.cpp @@ -428,6 +428,10 @@ int NetworkRequestState::pushToLuaState( lua_State *luaState ) lua_setfield( luaState, luaTableStackIndex, "url" ); nPushed++; + lua_pushnumber( luaState, (lua_Number)fRequestID ); + lua_setfield( luaState, luaTableStackIndex, "requestId" ); + nPushed++; + lua_pushnumber( luaState, (lua_Number)fBytesTransferred ); lua_setfield( luaState, luaTableStackIndex, "bytesTransferred" ); nPushed++; diff --git a/platform/linux/src/NetworkSupport.h b/platform/linux/src/NetworkSupport.h index c465d98b5..626d289b9 100644 --- a/platform/linux/src/NetworkSupport.h +++ b/platform/linux/src/NetworkSupport.h @@ -24,7 +24,7 @@ typedef std::map StringMap; typedef std::vector ByteVector; typedef std::string UTF8String; -void paramValidationFailure(lua_State *luaState, char *message, ...); +void paramValidationFailure(lua_State *luaState, const char *message, ...); bool isudatatype(lua_State *L, int idx, const char *name); UTF8String pathForTemporaryFileWithPrefix(const char *prefix, UTF8String pathDir); @@ -153,6 +153,7 @@ class NetworkRequestState: public ref_counted void setDebugValue(char *debugValue, char *debugKey); void setURL(const UTF8String& url) { fRequestURL = url; } void setStatus(int status) { fStatus = status; } + void setRequestID(unsigned int requestID) { fRequestID = requestID; } bool isError(); StringMap getResponseHeaders(); @@ -177,7 +178,7 @@ class NetworkRequestState: public ref_counted long long fBytesTransferred; StringMap fDebugValues; - int fRequestID; + unsigned int fRequestID; }; // ---------------------------------------------------------------------------- From edda9326e34a6beaa4112583cc124aa0e52dcd6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=BCndig?= Date: Wed, 2 Sep 2026 23:18:05 +0200 Subject: [PATCH 6/6] linux: honour display.save()'s jpegQuality 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. --- platform/linux/src/Rtt_LinuxBitmap.cpp | 4 ++-- platform/linux/src/Rtt_LinuxBitmap.h | 2 +- platform/linux/src/Rtt_LinuxPlatform.cpp | 2 +- platform/shared/Rtt_BitmapUtils.cpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/platform/linux/src/Rtt_LinuxBitmap.cpp b/platform/linux/src/Rtt_LinuxBitmap.cpp index 0a2aa7807..3a35caae6 100644 --- a/platform/linux/src/Rtt_LinuxBitmap.cpp +++ b/platform/linux/src/Rtt_LinuxBitmap.cpp @@ -179,7 +179,7 @@ namespace Rtt return fData != NULL; } - bool LinuxBaseBitmap::SaveBitmap(Rtt_Allocator *context, PlatformBitmap *bitmap, const char *filePath) + bool LinuxBaseBitmap::SaveBitmap(Rtt_Allocator *context, PlatformBitmap *bitmap, const char *filePath, float jpegQuality) { // Validate. if ((NULL == bitmap) || (NULL == filePath)) @@ -205,7 +205,7 @@ namespace Rtt } else if (path.rfind(".jpg") != std::string::npos) { - rc = bitmapUtil::saveJPG(filePath, bits, w, h, fmt, 75); // jpegQuality); + rc = bitmapUtil::saveJPG(filePath, bits, w, h, fmt, jpegQuality); } return rc; } diff --git a/platform/linux/src/Rtt_LinuxBitmap.h b/platform/linux/src/Rtt_LinuxBitmap.h index 07cd30242..c1ce0a8e2 100644 --- a/platform/linux/src/Rtt_LinuxBitmap.h +++ b/platform/linux/src/Rtt_LinuxBitmap.h @@ -41,7 +41,7 @@ namespace Rtt virtual bool IsProperty(PropertyMask mask) const; virtual void SetProperty(PropertyMask mask, bool newValue); bool LoadFileBitmap(Rtt_Allocator &context, const char *path); - static bool SaveBitmap(Rtt_Allocator *context, PlatformBitmap *bitmap, const char *filePath); + static bool SaveBitmap(Rtt_Allocator *context, PlatformBitmap *bitmap, const char *filePath, float jpegQuality); protected: Rtt_INLINE bool IsPropertyInternal(PropertyMask mask) const { return (fProperties & mask) ? true : false; } diff --git a/platform/linux/src/Rtt_LinuxPlatform.cpp b/platform/linux/src/Rtt_LinuxPlatform.cpp index caa793a44..582e10745 100644 --- a/platform/linux/src/Rtt_LinuxPlatform.cpp +++ b/platform/linux/src/Rtt_LinuxPlatform.cpp @@ -439,7 +439,7 @@ namespace Rtt bool LinuxPlatform::SaveBitmap(PlatformBitmap* bitmap, const char* filePath, float jpegQuality) const { - return LinuxBaseBitmap::SaveBitmap(fAllocator, bitmap, filePath); + return LinuxBaseBitmap::SaveBitmap(fAllocator, bitmap, filePath, jpegQuality); } bool LinuxPlatform::AddBitmapToPhotoLibrary(PlatformBitmap* bitmap) const diff --git a/platform/shared/Rtt_BitmapUtils.cpp b/platform/shared/Rtt_BitmapUtils.cpp index 7bf0b3cf6..5c816412b 100644 --- a/platform/shared/Rtt_BitmapUtils.cpp +++ b/platform/shared/Rtt_BitmapUtils.cpp @@ -137,13 +137,13 @@ namespace bitmapUtil jpeg_stdio_dest(&cinfo, outfile); cinfo.image_width = width; cinfo.image_height = height; - jpeg_set_quality(&cinfo, Rtt::Clamp((int)(jpegQuality * 100), 1, 100), TRUE); row_stride = width * 3; // JSAMPLEs per row in image_buffer cinfo.input_components = 3; // # of color components per pixel cinfo.in_color_space = JCS_RGB; // colorspace of input image jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, Rtt::Clamp((int)(jpegQuality * 100), 1, 100), TRUE); jpeg_start_compress(&cinfo, TRUE); std::unique_ptr rgb;