Merging changes for the v6.5.1.202602a hotfix release - #183

Merged
fdesbiens merged 8 commits into
masterfrom
dev
Aug 27, 2026
Merged

Merging changes for the v6.5.1.202602a hotfix release#183
fdesbiens merged 8 commits into
masterfrom
dev

Conversation

@fdesbiens

Copy link
Copy Markdown
Contributor

No description provided.

fdesbiensand others added 7 commits August 7, 2026 11:58
When GX_DISABLE_DEPRECATED_STRING_API is not defined (i.e., the
pre-5.6 GX_CHAR* API is still enabled), gx_api.h now emits a
#pragma message directing developers to define
GX_DISABLE_DEPRECATED_STRING_API and migrate to the GX_STRING-based
replacement functions.
The deprecated functions omit a string length and cannot safely handle
non-NUL-terminated strings or prevent buffer overruns. All new
applications should use the _ext() replacement variants.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fixed Screen Flow code generation with GX_ANIMATION_POOL_SIZE = 0
GUIX Studio emitted five static helper functions in the generated
specification file as soon as a project used Screen Flow. Two of them,
gx_studio_action_parent_find() and gx_studio_animation_execute(), are
reachable only from the GX_ACTION_TYPE_ANIMATION handler and reference
the GUIX animation API, which is compiled out when the animation pool
is empty:
- gx_system_animation_get() is only defined under
"#if (GX_ANIMATION_POOL_SIZE > 0)" in gx_api.h.
- The body of _gx_animation_start() is guarded the same way in
gx_animation_start.c, so the reference does not resolve at link
time either.
A project that defined GX_ANIMATION_POOL_SIZE as 0 in gx_user.h to save
resources therefore failed to build even when it used no animation
action at all. GCC 14 rejects the implicit declaration outright, and
MSVC fails on the unresolved external.
The screen generator now wraps both helpers and the two
GX_ACTION_TYPE_ANIMATION dispatch sites in
"#if (GX_ANIMATION_POOL_SIZE > 0)". Every other action type keeps
working with an empty pool, and an animation action simply becomes a
no-op at runtime.
The generator also emits forward prototypes for the four static screen
flow helpers, which addresses the secondary request in the issue. Note
that MISRA C:2012 Rule 8.1 covers explicitly specified types and Rule
8.4 applies to objects and functions with external linkage, so static
helpers without a visible prototype were not a deviation. The
prototypes are still worth having and match the declaration the
generator already emits for gx_studio_nested_widget_create().
All the checked-in generated specification files were regenerated so
that they stay in sync with the generator, including the golden files
of the Studio view test.
Added test/guix_studio_test/test_demo/test_animation_pool_size.py as a
regression test, registered in the demo compile CTest project. It
verifies that every checked-in Screen Flow specification file guards
the animation helpers, then compiles them all with
GX_ANIMATION_POOL_SIZE set to 0, promoting the MSVC implicit
declaration warning to an error so the failure mode above is caught.
Fixes#172
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Added hotfix letter support to the release version scripts
The release tooling could not express a hotfix version. Two gaps:
- Get-StudioReleaseVersion rejected anything that was not
major.minor.patch.build, and update_studio_release_version.ps1
hard-coded GUIX_HOTFIX_VERSION as ' '. The documented path to build
a hotfix installer was therefore unusable, and running the script
after a manual edit would have reset the hotfix constant.
- prepare_release.sh looked for gx_port.h under ports only, so it
never updated the version string of the GUIX Studio Win32 port in
guix_studio/ports/gx_port.h.
Get-StudioReleaseVersion now accepts an optional trailing hotfix letter
and exposes it as Hotfix and HotfixDefine. The Win32 and MSIX version
formats are strictly numeric and cannot carry the letter, so the
revision is offset by the position of the letter in the alphabet
instead: 6.5.1.202602 keeps revision 2, and 6.5.1.202602a becomes
revision 3. That keeps the hotfix installer recognizable as the newer
build for Windows upgrade detection and for the Store. Versions without
a hotfix letter resolve exactly as before.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Updated version number constants to 6.5.1.202602a
Prepares the emergency hotfix release that carries the Screen Flow fix
for GX_ANIMATION_POOL_SIZE = 0.
Produced by scripts/update_studio_release_version.ps1 -Version
6.5.1.202602a:
- gx_api.h: GUIX_HOTFIX_VERSION is now 'a'. The major, minor, patch
and build constants are unchanged, as is the 6.5.1 banner.
- studiox.rc: FileVersion and ProductVersion strings are
6.5.1.202602a, FILEVERSION and PRODUCTVERSION are 6,5,1,3.
- guix_installer_release.iss: StudioFullVersion is 6.5.1.202602a and
StudioVersionInfoVersion is 6.5.1.3, so the installer is named
guix_studio_setup_version_6.5.1.202602a.exe.
- Package.appxmanifest: the MSIX identity version is 6.5.1.03.
STUDIOX_VERSION_NUMBER stays at 202602. It stamps the Studio version
into .gxp project files, and a hotfix does not change the project file
format.
Verified against a rebuilt guix_studio.exe, which reports FileVersion
6.5.1.202602a with a numeric file version of 6.5.1.3, matching what
verify_studio_installer.ps1 expects.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Updated port version strings to 6.5.1.202602a
Updated the _gx_version_id string in all 47 gx_port.h files for the
emergency hotfix release, using the same substitution that
prepare_release.sh performs.
This includes guix_studio/ports/gx_port.h, the GUIX Studio Win32 port,
which the release script previously missed because it only searched the
ports directory. That gap is fixed in the preceding commit, so a future
release picks the file up automatically.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…#174)
* Revived the Windows workflows, which had reached no runner since 2025
The four GUIX Studio workflows requested `runs-on: windows-2019`, an
image GitHub retired on 2025-06-30. Nothing can match a retired label,
so every run queued for the full 24 hours and was then cancelled without
ever starting a job:
run 28477318708 created 2026-06-30T21:33:40Z
cancelled 2026-07-01T21:33:41Z
Every run of these workflows since 2025-09-29 has that same
created-plus-24h-cancelled shape. The Studio demo, demo compile, view and
MSIX package tests have gated nothing for about a year, and a manual
workflow_dispatch on 2026-08-25 queued the same way.
Three more defects were in the path behind that one, so fixing the image
alone would not have produced a green run.
- actions/upload-artifact was pinned at v3.1.3 in all four workflows.
GitHub began auto-failing every request that used v3 on 2025-01-30,
and both upload steps run under `if: success() || failure()`, so
every run would have ended red even with the tests passing.
- The scripts hard-coded a Visual Studio edition path. build_guix.cmd,
test_studio_demo.cmd, test_studio_demo_compile.cmd and
test_studio_view.cmd all call VS 2022 *Enterprise*, which no
developer machine has, and build_guix_studio.cmd calls VS 2022
*Community*, which no GitHub runner has. So the demo test's build
step would have failed on the first line. All five now locate the
installation with vswhere, matching what
build_guix_studio_msix_package.cmd already did, and check the result
instead of assuming the call succeeded. Verified on a Community
install, where the Enterprise path does not exist.
- studio_msix_package.yml uploaded
guix_studio/build/vs_2019/msix_package_project/AppPackages. That
directory has not existed since the solution moved to vs_2022, which
is where build_guix_studio_msix_package.cmd builds, so the artifact
was always empty.
Every action reference is now a 40-character commit SHA with the version
in a trailing comment, following the pattern eclipse-threadx/threadx
adopted in August 2026. A tag can be repointed at any commit; a SHA
cannot, which is what makes "which code ran in our CI" answerable from
the repository. Versions moved with the pinning:
actions/checkout v4 -> v7.0.1
actions/upload-artifact v3.1.3 -> v7.0.1
EnricoMi/publish-unit-test-result-action v2 -> v2.24.0
Compatibility was checked against each new action.yml rather than
assumed, for every input these workflows actually pass: checkout keeps
`submodules`; upload-artifact keeps `name` and `path`, and the one
upload per run means the v4 rule against uploading a name twice does not
bite; the publish action keeps `check_name` and `files`, and the
`composite` variant still exists at v2.24.0, which is the one a Windows
runner needs. All three tags were confirmed to resolve to the SHAs
pinned here, and all three are the current latest.
The runner image is pinned rather than tracking windows-latest, on the
same reasoning threadx applies to ubuntu-24.04: which image the build
ran on should be a reviewable commit, not something that changes
underneath it. Floating would have hidden this failure differently
rather than preventing it.
Also removed the dead `C:\Program Files\Python36` PATH lines. No
supported runner image has shipped that Python for years, python is
already on PATH there, and the lines embedded literal quote characters
into PATH.
The trigger branches are deliberately left alone in this commit. These
workflows fire on master only, so they still gate no pull request to dev
-- the same defect threadx fixed in its own suites. That wants a
dispatch run to go green first, on evidence rather than hope, and is a
separate change.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Added the Dependabot configuration the pinned actions need
Follow-up to the pinning in the preceding commit.
A SHA pin with nothing moving it is worse than a floating tag: it holds
CI on whatever was current the day it was written. This repository is the
case study for that. Nothing had ever reported that an action moved, so
actions/upload-artifact sat at v3.1.3 in all four Windows workflows --
four majors behind, and a version GitHub began auto-failing every request
for on 2025-01-30 -- while actions/checkout sat on an unpinned @v4, three
majors behind.
This adds .github/dependabot.yml: weekly, github-actions only, and closes
the reference to that path which the pinning comment in each workflow now
makes. Dependabot understands the SHA form and rewrites the trailing
version comment together with the pin, so the comment cannot drift away
from the SHA it describes.
It follows the configuration eclipse-threadx/threadx added in August
2026, with three differences that are specific to this repository and
documented in the file:
- Only three distinct actions are in use, so the default limit of five
would be enough. Ten is set anyway, to match the sibling repositories
and to leave room for a wave of majors after a long gap.
- There is no .github/CODEOWNERS here, so no reviewer routing exists
for Dependabot to honour. Worth adding, but not by this file.
- The "dependencies" label does not exist in this repository yet.
Dependabot creates it on the first pull request.
Two choices worth stating rather than leaving to be rediscovered.
target-branch is dev. Dependabot reads this file from the default
branch, which is master -- but master is deliberately kept behind dev,
and pull requests belong on dev. So this arms on merge without firing:
nothing happens until a release merge carries the file to master.
Setting target-branch also opts out of Dependabot security updates,
which only ever run against the default branch. For this ecosystem the
cost is small, since an action advisory arrives as an ordinary bump on
the weekly run, but it is a real trade.
Patch and minor are grouped into a single pull request, because a queue
reviewed one item at a time is a queue that gets ignored -- which is the
failure mode this file exists to prevent. Majors stay ungrouped, one
pull request each, because every breaking change met in an action here
has been a major: upload-artifact v4 stopped allowing an artifact name to
be uploaded twice in a run, and v6 requires a runner of 2.327.1 or newer.
Dependabot reports drift, not silence. The Windows workflows rotted
because they never reached a runner, not because nobody was told, and
the runner image fix in the preceding commit is the cure for that half.
The cross-repository reusable workflow in regression_test.yml is
correctly left alone: it references a branch, so it carries no version to
move.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Renamed the Studio IMAGE_INFO struct, which the Windows SDK now defines
With the Windows workflows reaching a runner again, the Studio build
failed immediately on both of them:
guix_studio\system_pngs.h(15,8): error C2011:
'IMAGE_INFO': 'struct' type redefinition
Windows Kits\10\Include\10.0.26100.0\um\Filter.h(340,16): error C2011:
'IMAGE_INFO': 'struct' type redefinition
plus eight C2027 "use of undefined type" errors following from it.
guix_studio declared its own two-field `IMAGE_INFO` in the global
namespace. A newer Windows SDK added a struct of the same name to
um/Filter.h, which arrives through the Windows headers the MFC sources
already include. SDK 10.0.22621.0 does not define it; 10.0.26100.0 does.
This was latent rather than new. studiox.vcxproj sets
`<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>`, the
MSBuild wildcard for "newest installed SDK", so which SDK compiles the
Studio depends entirely on the machine. A developer box with 22621 as its
newest builds; the runner image, which carries 26100, does not. The
collision was simply waiting for whichever came first.
Renamed the Studio type to STUDIO_IMAGE_INFO -- 13 references across five
files. It is private to the Studio, so nothing outside guix_studio sees
the name.
Also dropped the `-winsdk=10.0.22621.0` argument that
build_guix_studio.cmd passed to VsDevCmd. It never had the effect it
looks like it has: the wildcard in the project file governs the SDK the
compile actually uses, so the argument only set the shell environment and
left MSBuild to pick the newest SDK anyway. Keeping it would suggest the
build is pinned to an SDK when it is not. With the collision fixed there
is nothing to pin against.
Verified by rebuilding the Studio against SDK 10.0.22621.0 locally; the
runner will exercise 10.0.26100.0.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Pointed the demo test back at the guix project that exists
The demo compile test could not build the GUIX library at all:
Exception: guix.sln not found.
path=../../../ports/win32/build/vs_2022/guix.vcxproj
522aafc ("Updated scripts for VS Community 2022", February 2025) moved
three paths in test_main.py from vs_2019 to vs_2022. Two of them were
right: guix_studio/build/vs_2019 really had been renamed to vs_2022. The
third was collateral damage from the same search and replace --
ports/win32/build/vs_2019 was never renamed, and still holds the only
guix.vcxproj and guix.sln in the repository.
So --build_guix has raised this exception for eighteen months, before
compiling a single library configuration. Nothing said so, because the
workflow that calls it never reached a runner.
Restored the vs_2019 path. Verified by building the library locally:
guix.vcxproj is Win32-only and still on PlatformToolset v142, and it
produces Debug/gx.lib.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Pointed the view test at the Studio build directory that exists
The Studio view test could not start the Studio at all:
MSBUILD : error MSB1009: Project file does not exist.
Switch: ../../../guix_studio/build/vs_2019/studiox.sln
Exception: Unable to locate Studio executable.
522aafc ("Updated scripts for VS Community 2022", February 2025) renamed
guix_studio/build/vs_2019 to vs_2022 and updated test_demo/test_main.py
for it, but never touched the test_view directory. Three references there
still named the old directory:
- test_view/test_main.py project_sln_path
- test_view/test_utils.py studio_exe_path
- test_view/test_string_import_export.py studio_release
So this suite has been unable to locate the Studio for eighteen months.
Like the guix.vcxproj path in the preceding commit, nothing reported it,
because the workflow that runs it never reached a runner.
The remaining build/vs_2019 references in the tree belong to
ports/win32/build/vs_2019, which is a real directory and is left alone.
This commit makes the suite able to find and launch the Studio. Whether
it then passes on a hosted runner is a separate question -- it drives the
Studio through win32 window handles, and no run has got far enough to
say. Its trigger branches are deliberately not changed here for that
reason.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Ran the Studio demo suites on dev, where the pull requests actually are
The demo and demo compile workflows trigger on master only, for both push
and pull_request. dev is the integration branch, so these suites have
gated no pull request that anybody opened -- the same defect
eclipse-threadx/threadx fixed in its own regression suites in August 2026.
Switched on only after both suites were verified green on a runner, on
this branch, rather than on the assumption that they would be:
GUIX Studio Demo Test 227 of 227 generation tests 147 s
GUIX Studio Demo Compile Test 212 of 212 compile tests 146 s
The other three workflows are deliberately left on their current
triggers, and each for its own reason:
- regression_test.yml does reach a runner, and is red. Its last real
run, 2026-06-30, was 5 failures out of 732: accordion menu,
ml_text_view_32bpp, animation_complete, animation_complete_push_stack
and one more. Adding dev here would make every pull request red on a
suite that has been broken since June. It needs fixing first, and
that is not this change.
- studio_view_test.yml can now find the Studio again, but no run has
got far enough to say whether GUI automation through win32 window
handles survives a hosted runner. Enabling gating on an unknown is
what this commit is written to avoid.
- studio_msix_package.yml is workflow_dispatch only by design. It
packages a release rather than testing a change, so there is nothing
to gate.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ng (#175)
Commit 8a35f69 (#158) made _gx_animation_start() assign the return value of
_gx_canvas_drawing_initiate() to its local "status" variable. That variable is
both the value returned to the caller and the flag that gates linking the
animation into the active list, so the drawing status leaked into the
completion status.
_gx_widget_show() releases the view list of the animation root window on the
line just above that call, so _gx_canvas_drawing_initiate() reports
GX_NO_VIEWS every time an animation canvas is used. The animation was
therefore never linked into _gx_system_animation_list, the frame timer was
never started and _gx_animation_complete() never ran: no animation frames
were produced and GX_ANIMATION_PUSH_STACK never pushed its target onto the
screen stack.
The drawing status is now held in a separate draw_status variable. The #148
guard that keeps _gx_canvas_drawing_complete() from being called after
GX_DRAW_NESTING_EXCEEDED is preserved, while the completion status returned
to the caller is left untouched.
_gx_animation_drag_tracking_start() contains the identical block but returns
GX_SUCCESS unconditionally, so it was not affected. Its variable is renamed to
draw_status as well, with no change in behaviour, so the two functions cannot
drift apart again.
Verified on Linux with the default_build_coverage regression suite. Before the
fix, matching CI run 28477318897 exactly:
447 guix_all_widgets_16bpp_canvas_animation SEGFAULT
457 guix_animation_complete golden_file_frame_id = 1, test_frame_id = 3
458 guix_animation_complete_push_stack Failed, no output
After the fix all three pass and 730 of 732 tests pass. The two remaining
failures, guix_ml_text_view_32bpp and guix_all_widgets_accordion_menu, are
unrelated to animation and are diagnosed separately.
No new regression test is added: guix_animation_complete,
guix_animation_complete_push_stack and guix_all_widgets_16bpp_canvas_animation
already cover this path and are the tests that caught the regression. No
golden file is regenerated, since the change restores the recorded behaviour
rather than altering it. guix_canvas_draw_nesting_overflow_no_output, the test
added by #158, still passes.
No documentation change is required: no public API or behaviour contract
changes.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The fix for issue #148 (#158) made every caller of
_gx_canvas_drawing_initiate() skip its draw when the call returns
GX_DRAW_NESTING_EXCEEDED. Four of those callers push a nested context on the
widget they are already drawing, for one reason only: to narrow the clipping
rectangle to the widget's client area. For them, skipping the draw turns a
correct rendering into no rendering at all, which is what made
guix_all_widgets_accordion_menu report "Frame 12 is different".
The accordion menu screen of the all_widgets demo nests widgets nine levels
deep - multi_level_accordion, menu_list, mla_menu_1_accordion, menu_list,
text_view_3 - and _gx_system_canvas_refresh() consumes two contexts before the
widget tree is walked, so the eight slots of GX_MAX_CONTEXT_NESTING are
exhausted before _gx_multi_line_text_view_text_draw() can push its own. Raising
the limit in a scratch build makes frame 12 match the existing golden file
exactly, which shows that the golden records the correct rendering and that the
text is now being lost rather than merely clipped differently.
When the stack is full there is nothing to push, but the caller's context is
still the right context to draw through: it was created for the same widget and
differs only in its clipping rectangle. These four callers now narrow the
caller's clipping rectangle, draw, and restore it, instead of dropping the draw.
_gx_canvas_drawing_complete() is still not called on overflow, so the stack
corruption that #158 fixed stays fixed.
Applied to _gx_multi_line_text_view_text_draw, _gx_multi_line_text_input_draw,
_gx_rich_text_view_text_draw and _gx_single_line_text_input_draw.
_gx_widget_block_move and _gx_radial_progress_bar_background_draw are left
alone: neither pushes a clip-only context on the widget being drawn.
guix_canvas_draw_nesting_overflow_render_no_output closes the coverage gap that
#158 left. guix_canvas_draw_nesting_overflow_no_output covers detection and
state preservation; the new test covers what a caller must render: that the text
is still drawn at maximum nesting depth, that the borrowed context is handed
back with its nesting count, context pointer and clipping rectangle unchanged,
and that the pixels match those produced when a nested context is available.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
b8bb23b (#159) fixed issue #130 by letting the overflow branch of
_gx_multi_line_text_view_display_info_get() run when an ASCII space is the
character that overflows the available width. That branch consumes the space
and any consecutive spaces without adding them to the row width, which is
correct, and then breaks. When the whitespace ran to the end of the source
line, the line terminator was left behind: the next call started on it, hit
the GX_KEY_LINE_FEED case immediately and returned a row of one byte and zero
width, which draws as a blank line that is not in the text.
The overflow branch now takes the line terminator with the whitespace when
nothing else separates them, handling a bare line feed and a carriage return /
line feed pair, and guarding the second byte on the remaining length -- unlike
the pre-existing GX_KEY_CARRIAGE_RETURN case earlier in the same loop, which
reads ch.gx_string_ptr[1] unchecked. gx_text_display_width is untouched, so the
issue #130 fix stands.
Two such rows appear in the guix_ml_text_view_32bpp fixture, at string offsets
2992 and 23988 of readme_guix_generic.txt: "...Improved internal logic." with
twelve trailing spaces, and "...cursor_pos_calculate.c" with one. Both were
confirmed with a conditional breakpoint on display_number == 1 and
display_width == 0 preceded by a space, which fires exactly twice on the
pre-fix code and never after.
The two extra rows are why 268 of that test's 300 frames and 144 of
guix_bidi_text_draw_32bpp's 429 frames have differed from their golden data
since June. Nothing inside GUIX reads gx_text_display_width -- every caller
uses only gx_text_display_number, to advance its index -- so the row count
alone governs where every row starts, and two extra rows change the
scrollbar's value-to-pixel mapping. Every scroll step then lands at a slightly
different pixel offset and the whole text block is drawn a few pixels up or
down. Of the 300 frames, 297 were a pure vertical shift of otherwise identical
text; only 3 were laid out differently, and those 3 are the two places above.
No golden data is regenerated. gx_multi_line_text_view_text_total_rows for the
long text view goes back from 2226 to 2224, and both tests pass against their
existing golden files.
guix_ml_text_view_word_wrap_no_output did not protect #159's fix: it passed on
the pre-#159 code as well. Its available_width was one pixel too generous --
a_width + space_width, so the over-wide row the old code produced measured
exactly available_width and still satisfied "width <= available_width". It is
now a_width + space_width - 1, which makes appending the space genuinely
overflow, and three cases are added: a line feed terminator, a carriage return
/ line feed terminator, and the total row count that
_gx_multi_line_text_view_string_total_rows_compute() derives from them. That
last one is the quantity the golden frames actually depend on, and the only
one of the three a unit test can pin without golden data.
The test was verified to discriminate in both directions by rebuilding against
each. Against the pre-#159 source it fails four width assertions, which is
issue #130. Against #159 as shipped it fails the line feed row (2 bytes, not
3), the carriage return / line feed row (2, not 4) and the row count (3, not
2). It passes only with both fixes in place.
Verified on Linux across all eighteen build configurations, 1847 tests, no
failures. default_build_coverage 733/733, dynamic_bidi_text_build 3/3
including guix_bidi_text_draw_32bpp, no_utf8_build_coverage 135/135.
One coverage boundary worth naming: the unit test builds against the
all_widgets demo, which is not in NO_UTF8_DEMOS, so it runs in
default_build_coverage and disable_error_check_build but not in the
GX_UTF8_SUPPORT-off configurations. The changed code is common to both paths
-- only the surrounding character advance differs -- and no_utf8_build_coverage
covers it through its 135 golden tests.
No documentation change is required. _gx_multi_line_text_view_display_info_get
is internal, and the GUIX documentation does not describe multi-line text view
wrapping behaviour for trailing whitespace.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
)
_gx_multi_line_text_view_display_info_get() decided whether a line terminator
was "\r" or "\r\n" by examining ch.gx_string_ptr[1], the byte after the current
character, without checking that a byte remained. When the carriage return is
the last byte of the range, that access is outside the caller's buffer.
A GX_STRING carries its own length and need not be NUL terminated -- that is
why the _ext string API exists, and it is what the deprecation warning added in
#167 says about the older char * API. _gx_multi_line_text_view_text_set_ext()
stores the caller's GX_STRING verbatim unless GX_STYLE_TEXT_COPY is set, and
callers of this function pass end_index = gx_string_length, so in the default
configuration the read lands one byte past what the application supplied.
The consequence is not only the read. If that byte happens to be 0x0A, the
function reports a two-byte terminator for a one-byte remainder, so every
caller advances its index by one more than the string holds. In
_gx_multi_line_text_view_string_total_rows_compute() the loop then exits with
index == gx_string_length + 1 and evaluates string.gx_string_ptr[index - 1],
one byte past the end as well, and can add a row that is not in the text. The
same over-advance reaches the line index cache in
gx_multi_line_text_view_line_cache_update.c and the cursor arithmetic in
gx_multi_line_text_input_cursor_pos_update.c.
Both reads now go through string rather than ch. string has already been
advanced past the current character and its length is exactly the number of
bytes still readable there, so guarding on it bounds the access to what the
caller supplied.
The same idiom in _gx_multi_line_text_input_new_line_character_get() is
corrected as well. That one is not currently reachable as a read past the end:
its only caller, _gx_multi_line_text_input_text_set_ext(), writes a NUL
immediately after the copied text. It is fixed anyway because it depends on an
invariant established in a different function and documented nowhere, and
because the byte it reads decides whether the widget inserts a one- or two-byte
terminator on every subsequent Enter.
The rest of the codebase was audited for the same shape. Five other sites
handle a carriage return followed by a possible line feed; all five are already
correct. gx_rich_text_view_line_info_get.c, gx_multi_line_text_input_char_insert.c
and gx_utility_bidi_paragraph_reorder.c guard on the remaining length, the
preprocessed-line-break branch at the top of the changed function guards on it
too, and gx_multi_line_text_button_line_pointers_set.c walks a NUL-terminated
char * buffer where the byte after a carriage return is at worst the
terminator.
guix_ml_text_line_terminator_bounds_no_output covers this. It places a line
feed immediately after the string under test but outside the length the widget
is given, so the over-read is observable without a sanitizer: the unfixed code
counts the out-of-bounds byte and reports a 4-byte row for a 3-byte string,
which the test catches as "Expected: 3, Got: 4". Two controls pin the cases
that must not change -- a "\r\n" pair genuinely inside the string still counts
as two bytes, and a carriage return followed by ordinary text still counts as
one.
The test also asserts the terminator a multi-line text input adopts from its
text, for a lone carriage return and for a "\r\n" pair. Those two cases pass
both before and after the change, for the reason given above; they are there to
pin the behaviour, not to reproduce a fault.
Verified on Linux across all eighteen build configurations, no failures.
No documentation change is required: no public API or behaviour contract
changes, and the corrected behaviour is what the documented terminator handling
already describes.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX Studio Demo

227 tests 227 ✅ 23m 30s ⏱️
1 suites 0 💤
1 files 0 ❌

Results for commit e2d28f5.

♻️ This comment has been updated with latest results.

@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX Studio Demo Compile

213 tests 213 ✅ 18m 59s ⏱️
1 suites 0 💤
1 files 0 ❌

Results for commit e2d28f5.

♻️ This comment has been updated with latest results.

@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX

1 849 tests +1 117 1 849 ✔️ +1 122 31m 11s ⏱️ + 18m 15s
18 suites + 17 0 💤 ± 0 
18 files + 17 0 - 5 

Results for commit e2d28f5. ± Comparison against base commit 826487c.

♻️ This comment has been updated with latest results.

@github-actions

Copy link
Copy Markdown

Code Coverage

PackageLine RateBranch RateHealth
default_build_coverage Package 1100%99%
Summary100% (44377 / 44475)99% (24284 / 24434)

@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX Studio View

40 tests 40 ✅ 1h 21m 55s ⏱️
1 suites 0 💤
1 files 0 ❌

Results for commit e2d28f5.

♻️ This comment has been updated with latest results.

compare_file() locates the line where comparison should start, so that the
banner GUIX Studio writes into every generated file -- which carries a revision
string and a generation timestamp -- is skipped rather than compared. It looked
that line up correctly in the first file and then looked it up in the first file
again for the second:
for line in list_2:
if compare_start_string in line:
start_row_2 = list_1.index(line)
So the golden was sliced at the *generated* file's offset. While both files
happened to reach "#include" on the same line the mistake was invisible, which
is why it survived since the tests were added in #84.
The offsets diverged when 6ba13db added a ten-line MIT licence header to all
111 golden .c and .h files. GUIX Studio does not emit that header, so every
golden now reaches its first #include ten lines later than the file it is
compared against. The golden was therefore sliced ten lines early and compared
banner text against source.
That made every one of the 107 .c and .h comparisons in the suite fail -- all 29
reported test failures -- while the .csv, .xliff and .xml comparisons passed.
Those use skip_line instead of compare_start_string and never reach this code,
which is exactly the split the failure log shows.
The reported mismatch is the same in all 29: a generated "#include" line against
a golden banner comment. Confirmed arithmetically -- in every case the golden
line reported sits exactly ten lines before that golden's own first #include:
generic_16bpp_resources.c reported idx 13, #include at 23, delta 10
generic_16bpp_resources.h reported idx 16, #include at 26, delta 10
generic_16bpp_specifications.c reported idx 14, #include at 24, delta 10
generic_16bpp_specifications.h reported idx 16, #include at 26, delta 10
compare_output_file() is the only caller that passes compare_start_string, so
the change cannot affect the xliff, xml or plain-file comparisons.
Verified by running the suite locally against a Studio built from this tree.
Seven suites that fail in CI now pass with no mismatches at all, each gaining
exactly the tests it had been failing:
CI (broken) local (fixed)
Font 9 / 1 10 / 0
Multi-Themes 18 / 1 19 / 0
Project Import 10 / 2 12 / 0
Trigger Edit 6 / 1 7 / 0
Trigger Target Rename 1 / 1 2 / 0
Bidi Text 3 / 1 4 / 0
Widget Name 3 / 1 4 / 0
Project Import compares a generated specifications file, so this also shows that
the screen flow prototype block added by #173 does not disturb these goldens:
that block is only emitted for projects that use Screen Flow, and these do not.
No golden file is regenerated. The goldens still carry "GUIX Studio Revision
6.1.12.0" and a 2022 timestamp in their banners, and still name Azure RTOS
rather than Eclipse ThreadX, but the banner is what the comparison is supposed
to skip -- so none of that needs to be touched to make the suite correct.
This workflow had never actually executed before 2026-08-27. Every earlier run
was cancelled after queueing 24 hours for the retired windows-2019 image, or
sat awaiting approval on a fork pull request, including the run for the v6.5.1
release itself. #174 gave it a runner again; this is the first result it has
ever produced, and the defect it found is in the test harness rather than in
GUIX or in Studio.
Note that studio_view_test.yml triggers only on pull requests targeting master,
so this change is not exercised by its own pull request into dev. It is
validated by the release pull request that carries dev to master.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@fdesbiens
fdesbiens merged commit 52c1bb4 into masterAug 27, 2026
13 checks passed
Sign up for freeto 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.

1 participant

@fdesbiens
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Merging changes for the v6.5.1.202602a hotfix release - #183

Merged
fdesbiens merged 8 commits into
masterfrom
dev
Aug 27, 2026
Merged

Merging changes for the v6.5.1.202602a hotfix release#183
fdesbiens merged 8 commits into
masterfrom
dev

Conversation

@fdesbiens

Copy link
Copy Markdown
Contributor

No description provided.

fdesbiensand others added 7 commits August 7, 2026 11:58
When GX_DISABLE_DEPRECATED_STRING_API is not defined (i.e., the
pre-5.6 GX_CHAR* API is still enabled), gx_api.h now emits a
#pragma message directing developers to define
GX_DISABLE_DEPRECATED_STRING_API and migrate to the GX_STRING-based
replacement functions.
The deprecated functions omit a string length and cannot safely handle
non-NUL-terminated strings or prevent buffer overruns. All new
applications should use the _ext() replacement variants.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fixed Screen Flow code generation with GX_ANIMATION_POOL_SIZE = 0
GUIX Studio emitted five static helper functions in the generated
specification file as soon as a project used Screen Flow. Two of them,
gx_studio_action_parent_find() and gx_studio_animation_execute(), are
reachable only from the GX_ACTION_TYPE_ANIMATION handler and reference
the GUIX animation API, which is compiled out when the animation pool
is empty:
- gx_system_animation_get() is only defined under
"#if (GX_ANIMATION_POOL_SIZE > 0)" in gx_api.h.
- The body of _gx_animation_start() is guarded the same way in
gx_animation_start.c, so the reference does not resolve at link
time either.
A project that defined GX_ANIMATION_POOL_SIZE as 0 in gx_user.h to save
resources therefore failed to build even when it used no animation
action at all. GCC 14 rejects the implicit declaration outright, and
MSVC fails on the unresolved external.
The screen generator now wraps both helpers and the two
GX_ACTION_TYPE_ANIMATION dispatch sites in
"#if (GX_ANIMATION_POOL_SIZE > 0)". Every other action type keeps
working with an empty pool, and an animation action simply becomes a
no-op at runtime.
The generator also emits forward prototypes for the four static screen
flow helpers, which addresses the secondary request in the issue. Note
that MISRA C:2012 Rule 8.1 covers explicitly specified types and Rule
8.4 applies to objects and functions with external linkage, so static
helpers without a visible prototype were not a deviation. The
prototypes are still worth having and match the declaration the
generator already emits for gx_studio_nested_widget_create().
All the checked-in generated specification files were regenerated so
that they stay in sync with the generator, including the golden files
of the Studio view test.
Added test/guix_studio_test/test_demo/test_animation_pool_size.py as a
regression test, registered in the demo compile CTest project. It
verifies that every checked-in Screen Flow specification file guards
the animation helpers, then compiles them all with
GX_ANIMATION_POOL_SIZE set to 0, promoting the MSVC implicit
declaration warning to an error so the failure mode above is caught.
Fixes#172
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Added hotfix letter support to the release version scripts
The release tooling could not express a hotfix version. Two gaps:
- Get-StudioReleaseVersion rejected anything that was not
major.minor.patch.build, and update_studio_release_version.ps1
hard-coded GUIX_HOTFIX_VERSION as ' '. The documented path to build
a hotfix installer was therefore unusable, and running the script
after a manual edit would have reset the hotfix constant.
- prepare_release.sh looked for gx_port.h under ports only, so it
never updated the version string of the GUIX Studio Win32 port in
guix_studio/ports/gx_port.h.
Get-StudioReleaseVersion now accepts an optional trailing hotfix letter
and exposes it as Hotfix and HotfixDefine. The Win32 and MSIX version
formats are strictly numeric and cannot carry the letter, so the
revision is offset by the position of the letter in the alphabet
instead: 6.5.1.202602 keeps revision 2, and 6.5.1.202602a becomes
revision 3. That keeps the hotfix installer recognizable as the newer
build for Windows upgrade detection and for the Store. Versions without
a hotfix letter resolve exactly as before.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Updated version number constants to 6.5.1.202602a
Prepares the emergency hotfix release that carries the Screen Flow fix
for GX_ANIMATION_POOL_SIZE = 0.
Produced by scripts/update_studio_release_version.ps1 -Version
6.5.1.202602a:
- gx_api.h: GUIX_HOTFIX_VERSION is now 'a'. The major, minor, patch
and build constants are unchanged, as is the 6.5.1 banner.
- studiox.rc: FileVersion and ProductVersion strings are
6.5.1.202602a, FILEVERSION and PRODUCTVERSION are 6,5,1,3.
- guix_installer_release.iss: StudioFullVersion is 6.5.1.202602a and
StudioVersionInfoVersion is 6.5.1.3, so the installer is named
guix_studio_setup_version_6.5.1.202602a.exe.
- Package.appxmanifest: the MSIX identity version is 6.5.1.03.
STUDIOX_VERSION_NUMBER stays at 202602. It stamps the Studio version
into .gxp project files, and a hotfix does not change the project file
format.
Verified against a rebuilt guix_studio.exe, which reports FileVersion
6.5.1.202602a with a numeric file version of 6.5.1.3, matching what
verify_studio_installer.ps1 expects.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Updated port version strings to 6.5.1.202602a
Updated the _gx_version_id string in all 47 gx_port.h files for the
emergency hotfix release, using the same substitution that
prepare_release.sh performs.
This includes guix_studio/ports/gx_port.h, the GUIX Studio Win32 port,
which the release script previously missed because it only searched the
ports directory. That gap is fixed in the preceding commit, so a future
release picks the file up automatically.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…#174)
* Revived the Windows workflows, which had reached no runner since 2025
The four GUIX Studio workflows requested `runs-on: windows-2019`, an
image GitHub retired on 2025-06-30. Nothing can match a retired label,
so every run queued for the full 24 hours and was then cancelled without
ever starting a job:
run 28477318708 created 2026-06-30T21:33:40Z
cancelled 2026-07-01T21:33:41Z
Every run of these workflows since 2025-09-29 has that same
created-plus-24h-cancelled shape. The Studio demo, demo compile, view and
MSIX package tests have gated nothing for about a year, and a manual
workflow_dispatch on 2026-08-25 queued the same way.
Three more defects were in the path behind that one, so fixing the image
alone would not have produced a green run.
- actions/upload-artifact was pinned at v3.1.3 in all four workflows.
GitHub began auto-failing every request that used v3 on 2025-01-30,
and both upload steps run under `if: success() || failure()`, so
every run would have ended red even with the tests passing.
- The scripts hard-coded a Visual Studio edition path. build_guix.cmd,
test_studio_demo.cmd, test_studio_demo_compile.cmd and
test_studio_view.cmd all call VS 2022 *Enterprise*, which no
developer machine has, and build_guix_studio.cmd calls VS 2022
*Community*, which no GitHub runner has. So the demo test's build
step would have failed on the first line. All five now locate the
installation with vswhere, matching what
build_guix_studio_msix_package.cmd already did, and check the result
instead of assuming the call succeeded. Verified on a Community
install, where the Enterprise path does not exist.
- studio_msix_package.yml uploaded
guix_studio/build/vs_2019/msix_package_project/AppPackages. That
directory has not existed since the solution moved to vs_2022, which
is where build_guix_studio_msix_package.cmd builds, so the artifact
was always empty.
Every action reference is now a 40-character commit SHA with the version
in a trailing comment, following the pattern eclipse-threadx/threadx
adopted in August 2026. A tag can be repointed at any commit; a SHA
cannot, which is what makes "which code ran in our CI" answerable from
the repository. Versions moved with the pinning:
actions/checkout v4 -> v7.0.1
actions/upload-artifact v3.1.3 -> v7.0.1
EnricoMi/publish-unit-test-result-action v2 -> v2.24.0
Compatibility was checked against each new action.yml rather than
assumed, for every input these workflows actually pass: checkout keeps
`submodules`; upload-artifact keeps `name` and `path`, and the one
upload per run means the v4 rule against uploading a name twice does not
bite; the publish action keeps `check_name` and `files`, and the
`composite` variant still exists at v2.24.0, which is the one a Windows
runner needs. All three tags were confirmed to resolve to the SHAs
pinned here, and all three are the current latest.
The runner image is pinned rather than tracking windows-latest, on the
same reasoning threadx applies to ubuntu-24.04: which image the build
ran on should be a reviewable commit, not something that changes
underneath it. Floating would have hidden this failure differently
rather than preventing it.
Also removed the dead `C:\Program Files\Python36` PATH lines. No
supported runner image has shipped that Python for years, python is
already on PATH there, and the lines embedded literal quote characters
into PATH.
The trigger branches are deliberately left alone in this commit. These
workflows fire on master only, so they still gate no pull request to dev
-- the same defect threadx fixed in its own suites. That wants a
dispatch run to go green first, on evidence rather than hope, and is a
separate change.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Added the Dependabot configuration the pinned actions need
Follow-up to the pinning in the preceding commit.
A SHA pin with nothing moving it is worse than a floating tag: it holds
CI on whatever was current the day it was written. This repository is the
case study for that. Nothing had ever reported that an action moved, so
actions/upload-artifact sat at v3.1.3 in all four Windows workflows --
four majors behind, and a version GitHub began auto-failing every request
for on 2025-01-30 -- while actions/checkout sat on an unpinned @v4, three
majors behind.
This adds .github/dependabot.yml: weekly, github-actions only, and closes
the reference to that path which the pinning comment in each workflow now
makes. Dependabot understands the SHA form and rewrites the trailing
version comment together with the pin, so the comment cannot drift away
from the SHA it describes.
It follows the configuration eclipse-threadx/threadx added in August
2026, with three differences that are specific to this repository and
documented in the file:
- Only three distinct actions are in use, so the default limit of five
would be enough. Ten is set anyway, to match the sibling repositories
and to leave room for a wave of majors after a long gap.
- There is no .github/CODEOWNERS here, so no reviewer routing exists
for Dependabot to honour. Worth adding, but not by this file.
- The "dependencies" label does not exist in this repository yet.
Dependabot creates it on the first pull request.
Two choices worth stating rather than leaving to be rediscovered.
target-branch is dev. Dependabot reads this file from the default
branch, which is master -- but master is deliberately kept behind dev,
and pull requests belong on dev. So this arms on merge without firing:
nothing happens until a release merge carries the file to master.
Setting target-branch also opts out of Dependabot security updates,
which only ever run against the default branch. For this ecosystem the
cost is small, since an action advisory arrives as an ordinary bump on
the weekly run, but it is a real trade.
Patch and minor are grouped into a single pull request, because a queue
reviewed one item at a time is a queue that gets ignored -- which is the
failure mode this file exists to prevent. Majors stay ungrouped, one
pull request each, because every breaking change met in an action here
has been a major: upload-artifact v4 stopped allowing an artifact name to
be uploaded twice in a run, and v6 requires a runner of 2.327.1 or newer.
Dependabot reports drift, not silence. The Windows workflows rotted
because they never reached a runner, not because nobody was told, and
the runner image fix in the preceding commit is the cure for that half.
The cross-repository reusable workflow in regression_test.yml is
correctly left alone: it references a branch, so it carries no version to
move.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Renamed the Studio IMAGE_INFO struct, which the Windows SDK now defines
With the Windows workflows reaching a runner again, the Studio build
failed immediately on both of them:
guix_studio\system_pngs.h(15,8): error C2011:
'IMAGE_INFO': 'struct' type redefinition
Windows Kits\10\Include\10.0.26100.0\um\Filter.h(340,16): error C2011:
'IMAGE_INFO': 'struct' type redefinition
plus eight C2027 "use of undefined type" errors following from it.
guix_studio declared its own two-field `IMAGE_INFO` in the global
namespace. A newer Windows SDK added a struct of the same name to
um/Filter.h, which arrives through the Windows headers the MFC sources
already include. SDK 10.0.22621.0 does not define it; 10.0.26100.0 does.
This was latent rather than new. studiox.vcxproj sets
`<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>`, the
MSBuild wildcard for "newest installed SDK", so which SDK compiles the
Studio depends entirely on the machine. A developer box with 22621 as its
newest builds; the runner image, which carries 26100, does not. The
collision was simply waiting for whichever came first.
Renamed the Studio type to STUDIO_IMAGE_INFO -- 13 references across five
files. It is private to the Studio, so nothing outside guix_studio sees
the name.
Also dropped the `-winsdk=10.0.22621.0` argument that
build_guix_studio.cmd passed to VsDevCmd. It never had the effect it
looks like it has: the wildcard in the project file governs the SDK the
compile actually uses, so the argument only set the shell environment and
left MSBuild to pick the newest SDK anyway. Keeping it would suggest the
build is pinned to an SDK when it is not. With the collision fixed there
is nothing to pin against.
Verified by rebuilding the Studio against SDK 10.0.22621.0 locally; the
runner will exercise 10.0.26100.0.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Pointed the demo test back at the guix project that exists
The demo compile test could not build the GUIX library at all:
Exception: guix.sln not found.
path=../../../ports/win32/build/vs_2022/guix.vcxproj
522aafc ("Updated scripts for VS Community 2022", February 2025) moved
three paths in test_main.py from vs_2019 to vs_2022. Two of them were
right: guix_studio/build/vs_2019 really had been renamed to vs_2022. The
third was collateral damage from the same search and replace --
ports/win32/build/vs_2019 was never renamed, and still holds the only
guix.vcxproj and guix.sln in the repository.
So --build_guix has raised this exception for eighteen months, before
compiling a single library configuration. Nothing said so, because the
workflow that calls it never reached a runner.
Restored the vs_2019 path. Verified by building the library locally:
guix.vcxproj is Win32-only and still on PlatformToolset v142, and it
produces Debug/gx.lib.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Pointed the view test at the Studio build directory that exists
The Studio view test could not start the Studio at all:
MSBUILD : error MSB1009: Project file does not exist.
Switch: ../../../guix_studio/build/vs_2019/studiox.sln
Exception: Unable to locate Studio executable.
522aafc ("Updated scripts for VS Community 2022", February 2025) renamed
guix_studio/build/vs_2019 to vs_2022 and updated test_demo/test_main.py
for it, but never touched the test_view directory. Three references there
still named the old directory:
- test_view/test_main.py project_sln_path
- test_view/test_utils.py studio_exe_path
- test_view/test_string_import_export.py studio_release
So this suite has been unable to locate the Studio for eighteen months.
Like the guix.vcxproj path in the preceding commit, nothing reported it,
because the workflow that runs it never reached a runner.
The remaining build/vs_2019 references in the tree belong to
ports/win32/build/vs_2019, which is a real directory and is left alone.
This commit makes the suite able to find and launch the Studio. Whether
it then passes on a hosted runner is a separate question -- it drives the
Studio through win32 window handles, and no run has got far enough to
say. Its trigger branches are deliberately not changed here for that
reason.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Ran the Studio demo suites on dev, where the pull requests actually are
The demo and demo compile workflows trigger on master only, for both push
and pull_request. dev is the integration branch, so these suites have
gated no pull request that anybody opened -- the same defect
eclipse-threadx/threadx fixed in its own regression suites in August 2026.
Switched on only after both suites were verified green on a runner, on
this branch, rather than on the assumption that they would be:
GUIX Studio Demo Test 227 of 227 generation tests 147 s
GUIX Studio Demo Compile Test 212 of 212 compile tests 146 s
The other three workflows are deliberately left on their current
triggers, and each for its own reason:
- regression_test.yml does reach a runner, and is red. Its last real
run, 2026-06-30, was 5 failures out of 732: accordion menu,
ml_text_view_32bpp, animation_complete, animation_complete_push_stack
and one more. Adding dev here would make every pull request red on a
suite that has been broken since June. It needs fixing first, and
that is not this change.
- studio_view_test.yml can now find the Studio again, but no run has
got far enough to say whether GUI automation through win32 window
handles survives a hosted runner. Enabling gating on an unknown is
what this commit is written to avoid.
- studio_msix_package.yml is workflow_dispatch only by design. It
packages a release rather than testing a change, so there is nothing
to gate.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ng (#175)
Commit 8a35f69 (#158) made _gx_animation_start() assign the return value of
_gx_canvas_drawing_initiate() to its local "status" variable. That variable is
both the value returned to the caller and the flag that gates linking the
animation into the active list, so the drawing status leaked into the
completion status.
_gx_widget_show() releases the view list of the animation root window on the
line just above that call, so _gx_canvas_drawing_initiate() reports
GX_NO_VIEWS every time an animation canvas is used. The animation was
therefore never linked into _gx_system_animation_list, the frame timer was
never started and _gx_animation_complete() never ran: no animation frames
were produced and GX_ANIMATION_PUSH_STACK never pushed its target onto the
screen stack.
The drawing status is now held in a separate draw_status variable. The #148
guard that keeps _gx_canvas_drawing_complete() from being called after
GX_DRAW_NESTING_EXCEEDED is preserved, while the completion status returned
to the caller is left untouched.
_gx_animation_drag_tracking_start() contains the identical block but returns
GX_SUCCESS unconditionally, so it was not affected. Its variable is renamed to
draw_status as well, with no change in behaviour, so the two functions cannot
drift apart again.
Verified on Linux with the default_build_coverage regression suite. Before the
fix, matching CI run 28477318897 exactly:
447 guix_all_widgets_16bpp_canvas_animation SEGFAULT
457 guix_animation_complete golden_file_frame_id = 1, test_frame_id = 3
458 guix_animation_complete_push_stack Failed, no output
After the fix all three pass and 730 of 732 tests pass. The two remaining
failures, guix_ml_text_view_32bpp and guix_all_widgets_accordion_menu, are
unrelated to animation and are diagnosed separately.
No new regression test is added: guix_animation_complete,
guix_animation_complete_push_stack and guix_all_widgets_16bpp_canvas_animation
already cover this path and are the tests that caught the regression. No
golden file is regenerated, since the change restores the recorded behaviour
rather than altering it. guix_canvas_draw_nesting_overflow_no_output, the test
added by #158, still passes.
No documentation change is required: no public API or behaviour contract
changes.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The fix for issue #148 (#158) made every caller of
_gx_canvas_drawing_initiate() skip its draw when the call returns
GX_DRAW_NESTING_EXCEEDED. Four of those callers push a nested context on the
widget they are already drawing, for one reason only: to narrow the clipping
rectangle to the widget's client area. For them, skipping the draw turns a
correct rendering into no rendering at all, which is what made
guix_all_widgets_accordion_menu report "Frame 12 is different".
The accordion menu screen of the all_widgets demo nests widgets nine levels
deep - multi_level_accordion, menu_list, mla_menu_1_accordion, menu_list,
text_view_3 - and _gx_system_canvas_refresh() consumes two contexts before the
widget tree is walked, so the eight slots of GX_MAX_CONTEXT_NESTING are
exhausted before _gx_multi_line_text_view_text_draw() can push its own. Raising
the limit in a scratch build makes frame 12 match the existing golden file
exactly, which shows that the golden records the correct rendering and that the
text is now being lost rather than merely clipped differently.
When the stack is full there is nothing to push, but the caller's context is
still the right context to draw through: it was created for the same widget and
differs only in its clipping rectangle. These four callers now narrow the
caller's clipping rectangle, draw, and restore it, instead of dropping the draw.
_gx_canvas_drawing_complete() is still not called on overflow, so the stack
corruption that #158 fixed stays fixed.
Applied to _gx_multi_line_text_view_text_draw, _gx_multi_line_text_input_draw,
_gx_rich_text_view_text_draw and _gx_single_line_text_input_draw.
_gx_widget_block_move and _gx_radial_progress_bar_background_draw are left
alone: neither pushes a clip-only context on the widget being drawn.
guix_canvas_draw_nesting_overflow_render_no_output closes the coverage gap that
#158 left. guix_canvas_draw_nesting_overflow_no_output covers detection and
state preservation; the new test covers what a caller must render: that the text
is still drawn at maximum nesting depth, that the borrowed context is handed
back with its nesting count, context pointer and clipping rectangle unchanged,
and that the pixels match those produced when a nested context is available.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
b8bb23b (#159) fixed issue #130 by letting the overflow branch of
_gx_multi_line_text_view_display_info_get() run when an ASCII space is the
character that overflows the available width. That branch consumes the space
and any consecutive spaces without adding them to the row width, which is
correct, and then breaks. When the whitespace ran to the end of the source
line, the line terminator was left behind: the next call started on it, hit
the GX_KEY_LINE_FEED case immediately and returned a row of one byte and zero
width, which draws as a blank line that is not in the text.
The overflow branch now takes the line terminator with the whitespace when
nothing else separates them, handling a bare line feed and a carriage return /
line feed pair, and guarding the second byte on the remaining length -- unlike
the pre-existing GX_KEY_CARRIAGE_RETURN case earlier in the same loop, which
reads ch.gx_string_ptr[1] unchecked. gx_text_display_width is untouched, so the
issue #130 fix stands.
Two such rows appear in the guix_ml_text_view_32bpp fixture, at string offsets
2992 and 23988 of readme_guix_generic.txt: "...Improved internal logic." with
twelve trailing spaces, and "...cursor_pos_calculate.c" with one. Both were
confirmed with a conditional breakpoint on display_number == 1 and
display_width == 0 preceded by a space, which fires exactly twice on the
pre-fix code and never after.
The two extra rows are why 268 of that test's 300 frames and 144 of
guix_bidi_text_draw_32bpp's 429 frames have differed from their golden data
since June. Nothing inside GUIX reads gx_text_display_width -- every caller
uses only gx_text_display_number, to advance its index -- so the row count
alone governs where every row starts, and two extra rows change the
scrollbar's value-to-pixel mapping. Every scroll step then lands at a slightly
different pixel offset and the whole text block is drawn a few pixels up or
down. Of the 300 frames, 297 were a pure vertical shift of otherwise identical
text; only 3 were laid out differently, and those 3 are the two places above.
No golden data is regenerated. gx_multi_line_text_view_text_total_rows for the
long text view goes back from 2226 to 2224, and both tests pass against their
existing golden files.
guix_ml_text_view_word_wrap_no_output did not protect #159's fix: it passed on
the pre-#159 code as well. Its available_width was one pixel too generous --
a_width + space_width, so the over-wide row the old code produced measured
exactly available_width and still satisfied "width <= available_width". It is
now a_width + space_width - 1, which makes appending the space genuinely
overflow, and three cases are added: a line feed terminator, a carriage return
/ line feed terminator, and the total row count that
_gx_multi_line_text_view_string_total_rows_compute() derives from them. That
last one is the quantity the golden frames actually depend on, and the only
one of the three a unit test can pin without golden data.
The test was verified to discriminate in both directions by rebuilding against
each. Against the pre-#159 source it fails four width assertions, which is
issue #130. Against #159 as shipped it fails the line feed row (2 bytes, not
3), the carriage return / line feed row (2, not 4) and the row count (3, not
2). It passes only with both fixes in place.
Verified on Linux across all eighteen build configurations, 1847 tests, no
failures. default_build_coverage 733/733, dynamic_bidi_text_build 3/3
including guix_bidi_text_draw_32bpp, no_utf8_build_coverage 135/135.
One coverage boundary worth naming: the unit test builds against the
all_widgets demo, which is not in NO_UTF8_DEMOS, so it runs in
default_build_coverage and disable_error_check_build but not in the
GX_UTF8_SUPPORT-off configurations. The changed code is common to both paths
-- only the surrounding character advance differs -- and no_utf8_build_coverage
covers it through its 135 golden tests.
No documentation change is required. _gx_multi_line_text_view_display_info_get
is internal, and the GUIX documentation does not describe multi-line text view
wrapping behaviour for trailing whitespace.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
)
_gx_multi_line_text_view_display_info_get() decided whether a line terminator
was "\r" or "\r\n" by examining ch.gx_string_ptr[1], the byte after the current
character, without checking that a byte remained. When the carriage return is
the last byte of the range, that access is outside the caller's buffer.
A GX_STRING carries its own length and need not be NUL terminated -- that is
why the _ext string API exists, and it is what the deprecation warning added in
#167 says about the older char * API. _gx_multi_line_text_view_text_set_ext()
stores the caller's GX_STRING verbatim unless GX_STYLE_TEXT_COPY is set, and
callers of this function pass end_index = gx_string_length, so in the default
configuration the read lands one byte past what the application supplied.
The consequence is not only the read. If that byte happens to be 0x0A, the
function reports a two-byte terminator for a one-byte remainder, so every
caller advances its index by one more than the string holds. In
_gx_multi_line_text_view_string_total_rows_compute() the loop then exits with
index == gx_string_length + 1 and evaluates string.gx_string_ptr[index - 1],
one byte past the end as well, and can add a row that is not in the text. The
same over-advance reaches the line index cache in
gx_multi_line_text_view_line_cache_update.c and the cursor arithmetic in
gx_multi_line_text_input_cursor_pos_update.c.
Both reads now go through string rather than ch. string has already been
advanced past the current character and its length is exactly the number of
bytes still readable there, so guarding on it bounds the access to what the
caller supplied.
The same idiom in _gx_multi_line_text_input_new_line_character_get() is
corrected as well. That one is not currently reachable as a read past the end:
its only caller, _gx_multi_line_text_input_text_set_ext(), writes a NUL
immediately after the copied text. It is fixed anyway because it depends on an
invariant established in a different function and documented nowhere, and
because the byte it reads decides whether the widget inserts a one- or two-byte
terminator on every subsequent Enter.
The rest of the codebase was audited for the same shape. Five other sites
handle a carriage return followed by a possible line feed; all five are already
correct. gx_rich_text_view_line_info_get.c, gx_multi_line_text_input_char_insert.c
and gx_utility_bidi_paragraph_reorder.c guard on the remaining length, the
preprocessed-line-break branch at the top of the changed function guards on it
too, and gx_multi_line_text_button_line_pointers_set.c walks a NUL-terminated
char * buffer where the byte after a carriage return is at worst the
terminator.
guix_ml_text_line_terminator_bounds_no_output covers this. It places a line
feed immediately after the string under test but outside the length the widget
is given, so the over-read is observable without a sanitizer: the unfixed code
counts the out-of-bounds byte and reports a 4-byte row for a 3-byte string,
which the test catches as "Expected: 3, Got: 4". Two controls pin the cases
that must not change -- a "\r\n" pair genuinely inside the string still counts
as two bytes, and a carriage return followed by ordinary text still counts as
one.
The test also asserts the terminator a multi-line text input adopts from its
text, for a lone carriage return and for a "\r\n" pair. Those two cases pass
both before and after the change, for the reason given above; they are there to
pin the behaviour, not to reproduce a fault.
Verified on Linux across all eighteen build configurations, no failures.
No documentation change is required: no public API or behaviour contract
changes, and the corrected behaviour is what the documented terminator handling
already describes.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX Studio Demo

227 tests 227 ✅ 23m 30s ⏱️
1 suites 0 💤
1 files 0 ❌

Results for commit e2d28f5.

♻️ This comment has been updated with latest results.

@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX Studio Demo Compile

213 tests 213 ✅ 18m 59s ⏱️
1 suites 0 💤
1 files 0 ❌

Results for commit e2d28f5.

♻️ This comment has been updated with latest results.

@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX

1 849 tests +1 117 1 849 ✔️ +1 122 31m 11s ⏱️ + 18m 15s
18 suites + 17 0 💤 ± 0 
18 files + 17 0 - 5 

Results for commit e2d28f5. ± Comparison against base commit 826487c.

♻️ This comment has been updated with latest results.

@github-actions

Copy link
Copy Markdown

Code Coverage

PackageLine RateBranch RateHealth
default_build_coverage Package 1100%99%
Summary100% (44377 / 44475)99% (24284 / 24434)

@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX Studio View

40 tests 40 ✅ 1h 21m 55s ⏱️
1 suites 0 💤
1 files 0 ❌

Results for commit e2d28f5.

♻️ This comment has been updated with latest results.

compare_file() locates the line where comparison should start, so that the
banner GUIX Studio writes into every generated file -- which carries a revision
string and a generation timestamp -- is skipped rather than compared. It looked
that line up correctly in the first file and then looked it up in the first file
again for the second:
for line in list_2:
if compare_start_string in line:
start_row_2 = list_1.index(line)
So the golden was sliced at the *generated* file's offset. While both files
happened to reach "#include" on the same line the mistake was invisible, which
is why it survived since the tests were added in #84.
The offsets diverged when 6ba13db added a ten-line MIT licence header to all
111 golden .c and .h files. GUIX Studio does not emit that header, so every
golden now reaches its first #include ten lines later than the file it is
compared against. The golden was therefore sliced ten lines early and compared
banner text against source.
That made every one of the 107 .c and .h comparisons in the suite fail -- all 29
reported test failures -- while the .csv, .xliff and .xml comparisons passed.
Those use skip_line instead of compare_start_string and never reach this code,
which is exactly the split the failure log shows.
The reported mismatch is the same in all 29: a generated "#include" line against
a golden banner comment. Confirmed arithmetically -- in every case the golden
line reported sits exactly ten lines before that golden's own first #include:
generic_16bpp_resources.c reported idx 13, #include at 23, delta 10
generic_16bpp_resources.h reported idx 16, #include at 26, delta 10
generic_16bpp_specifications.c reported idx 14, #include at 24, delta 10
generic_16bpp_specifications.h reported idx 16, #include at 26, delta 10
compare_output_file() is the only caller that passes compare_start_string, so
the change cannot affect the xliff, xml or plain-file comparisons.
Verified by running the suite locally against a Studio built from this tree.
Seven suites that fail in CI now pass with no mismatches at all, each gaining
exactly the tests it had been failing:
CI (broken) local (fixed)
Font 9 / 1 10 / 0
Multi-Themes 18 / 1 19 / 0
Project Import 10 / 2 12 / 0
Trigger Edit 6 / 1 7 / 0
Trigger Target Rename 1 / 1 2 / 0
Bidi Text 3 / 1 4 / 0
Widget Name 3 / 1 4 / 0
Project Import compares a generated specifications file, so this also shows that
the screen flow prototype block added by #173 does not disturb these goldens:
that block is only emitted for projects that use Screen Flow, and these do not.
No golden file is regenerated. The goldens still carry "GUIX Studio Revision
6.1.12.0" and a 2022 timestamp in their banners, and still name Azure RTOS
rather than Eclipse ThreadX, but the banner is what the comparison is supposed
to skip -- so none of that needs to be touched to make the suite correct.
This workflow had never actually executed before 2026-08-27. Every earlier run
was cancelled after queueing 24 hours for the retired windows-2019 image, or
sat awaiting approval on a fork pull request, including the run for the v6.5.1
release itself. #174 gave it a runner again; this is the first result it has
ever produced, and the defect it found is in the test harness rather than in
GUIX or in Studio.
Note that studio_view_test.yml triggers only on pull requests targeting master,
so this change is not exercised by its own pull request into dev. It is
validated by the release pull request that carries dev to master.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@fdesbiens
fdesbiens merged commit 52c1bb4 into masterAug 27, 2026
13 checks passed
Sign up for freeto 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.

1 participant

@fdesbiens
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Merging changes for the v6.5.1.202602a hotfix release - #183

Merged
fdesbiens merged 8 commits into
masterfrom
dev
Aug 27, 2026
Merged

Merging changes for the v6.5.1.202602a hotfix release#183
fdesbiens merged 8 commits into
masterfrom
dev

Conversation

@fdesbiens

Copy link
Copy Markdown
Contributor

No description provided.

fdesbiensand others added 7 commits August 7, 2026 11:58
When GX_DISABLE_DEPRECATED_STRING_API is not defined (i.e., the
pre-5.6 GX_CHAR* API is still enabled), gx_api.h now emits a
#pragma message directing developers to define
GX_DISABLE_DEPRECATED_STRING_API and migrate to the GX_STRING-based
replacement functions.
The deprecated functions omit a string length and cannot safely handle
non-NUL-terminated strings or prevent buffer overruns. All new
applications should use the _ext() replacement variants.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fixed Screen Flow code generation with GX_ANIMATION_POOL_SIZE = 0
GUIX Studio emitted five static helper functions in the generated
specification file as soon as a project used Screen Flow. Two of them,
gx_studio_action_parent_find() and gx_studio_animation_execute(), are
reachable only from the GX_ACTION_TYPE_ANIMATION handler and reference
the GUIX animation API, which is compiled out when the animation pool
is empty:
- gx_system_animation_get() is only defined under
"#if (GX_ANIMATION_POOL_SIZE > 0)" in gx_api.h.
- The body of _gx_animation_start() is guarded the same way in
gx_animation_start.c, so the reference does not resolve at link
time either.
A project that defined GX_ANIMATION_POOL_SIZE as 0 in gx_user.h to save
resources therefore failed to build even when it used no animation
action at all. GCC 14 rejects the implicit declaration outright, and
MSVC fails on the unresolved external.
The screen generator now wraps both helpers and the two
GX_ACTION_TYPE_ANIMATION dispatch sites in
"#if (GX_ANIMATION_POOL_SIZE > 0)". Every other action type keeps
working with an empty pool, and an animation action simply becomes a
no-op at runtime.
The generator also emits forward prototypes for the four static screen
flow helpers, which addresses the secondary request in the issue. Note
that MISRA C:2012 Rule 8.1 covers explicitly specified types and Rule
8.4 applies to objects and functions with external linkage, so static
helpers without a visible prototype were not a deviation. The
prototypes are still worth having and match the declaration the
generator already emits for gx_studio_nested_widget_create().
All the checked-in generated specification files were regenerated so
that they stay in sync with the generator, including the golden files
of the Studio view test.
Added test/guix_studio_test/test_demo/test_animation_pool_size.py as a
regression test, registered in the demo compile CTest project. It
verifies that every checked-in Screen Flow specification file guards
the animation helpers, then compiles them all with
GX_ANIMATION_POOL_SIZE set to 0, promoting the MSVC implicit
declaration warning to an error so the failure mode above is caught.
Fixes#172
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Added hotfix letter support to the release version scripts
The release tooling could not express a hotfix version. Two gaps:
- Get-StudioReleaseVersion rejected anything that was not
major.minor.patch.build, and update_studio_release_version.ps1
hard-coded GUIX_HOTFIX_VERSION as ' '. The documented path to build
a hotfix installer was therefore unusable, and running the script
after a manual edit would have reset the hotfix constant.
- prepare_release.sh looked for gx_port.h under ports only, so it
never updated the version string of the GUIX Studio Win32 port in
guix_studio/ports/gx_port.h.
Get-StudioReleaseVersion now accepts an optional trailing hotfix letter
and exposes it as Hotfix and HotfixDefine. The Win32 and MSIX version
formats are strictly numeric and cannot carry the letter, so the
revision is offset by the position of the letter in the alphabet
instead: 6.5.1.202602 keeps revision 2, and 6.5.1.202602a becomes
revision 3. That keeps the hotfix installer recognizable as the newer
build for Windows upgrade detection and for the Store. Versions without
a hotfix letter resolve exactly as before.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Updated version number constants to 6.5.1.202602a
Prepares the emergency hotfix release that carries the Screen Flow fix
for GX_ANIMATION_POOL_SIZE = 0.
Produced by scripts/update_studio_release_version.ps1 -Version
6.5.1.202602a:
- gx_api.h: GUIX_HOTFIX_VERSION is now 'a'. The major, minor, patch
and build constants are unchanged, as is the 6.5.1 banner.
- studiox.rc: FileVersion and ProductVersion strings are
6.5.1.202602a, FILEVERSION and PRODUCTVERSION are 6,5,1,3.
- guix_installer_release.iss: StudioFullVersion is 6.5.1.202602a and
StudioVersionInfoVersion is 6.5.1.3, so the installer is named
guix_studio_setup_version_6.5.1.202602a.exe.
- Package.appxmanifest: the MSIX identity version is 6.5.1.03.
STUDIOX_VERSION_NUMBER stays at 202602. It stamps the Studio version
into .gxp project files, and a hotfix does not change the project file
format.
Verified against a rebuilt guix_studio.exe, which reports FileVersion
6.5.1.202602a with a numeric file version of 6.5.1.3, matching what
verify_studio_installer.ps1 expects.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Updated port version strings to 6.5.1.202602a
Updated the _gx_version_id string in all 47 gx_port.h files for the
emergency hotfix release, using the same substitution that
prepare_release.sh performs.
This includes guix_studio/ports/gx_port.h, the GUIX Studio Win32 port,
which the release script previously missed because it only searched the
ports directory. That gap is fixed in the preceding commit, so a future
release picks the file up automatically.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…#174)
* Revived the Windows workflows, which had reached no runner since 2025
The four GUIX Studio workflows requested `runs-on: windows-2019`, an
image GitHub retired on 2025-06-30. Nothing can match a retired label,
so every run queued for the full 24 hours and was then cancelled without
ever starting a job:
run 28477318708 created 2026-06-30T21:33:40Z
cancelled 2026-07-01T21:33:41Z
Every run of these workflows since 2025-09-29 has that same
created-plus-24h-cancelled shape. The Studio demo, demo compile, view and
MSIX package tests have gated nothing for about a year, and a manual
workflow_dispatch on 2026-08-25 queued the same way.
Three more defects were in the path behind that one, so fixing the image
alone would not have produced a green run.
- actions/upload-artifact was pinned at v3.1.3 in all four workflows.
GitHub began auto-failing every request that used v3 on 2025-01-30,
and both upload steps run under `if: success() || failure()`, so
every run would have ended red even with the tests passing.
- The scripts hard-coded a Visual Studio edition path. build_guix.cmd,
test_studio_demo.cmd, test_studio_demo_compile.cmd and
test_studio_view.cmd all call VS 2022 *Enterprise*, which no
developer machine has, and build_guix_studio.cmd calls VS 2022
*Community*, which no GitHub runner has. So the demo test's build
step would have failed on the first line. All five now locate the
installation with vswhere, matching what
build_guix_studio_msix_package.cmd already did, and check the result
instead of assuming the call succeeded. Verified on a Community
install, where the Enterprise path does not exist.
- studio_msix_package.yml uploaded
guix_studio/build/vs_2019/msix_package_project/AppPackages. That
directory has not existed since the solution moved to vs_2022, which
is where build_guix_studio_msix_package.cmd builds, so the artifact
was always empty.
Every action reference is now a 40-character commit SHA with the version
in a trailing comment, following the pattern eclipse-threadx/threadx
adopted in August 2026. A tag can be repointed at any commit; a SHA
cannot, which is what makes "which code ran in our CI" answerable from
the repository. Versions moved with the pinning:
actions/checkout v4 -> v7.0.1
actions/upload-artifact v3.1.3 -> v7.0.1
EnricoMi/publish-unit-test-result-action v2 -> v2.24.0
Compatibility was checked against each new action.yml rather than
assumed, for every input these workflows actually pass: checkout keeps
`submodules`; upload-artifact keeps `name` and `path`, and the one
upload per run means the v4 rule against uploading a name twice does not
bite; the publish action keeps `check_name` and `files`, and the
`composite` variant still exists at v2.24.0, which is the one a Windows
runner needs. All three tags were confirmed to resolve to the SHAs
pinned here, and all three are the current latest.
The runner image is pinned rather than tracking windows-latest, on the
same reasoning threadx applies to ubuntu-24.04: which image the build
ran on should be a reviewable commit, not something that changes
underneath it. Floating would have hidden this failure differently
rather than preventing it.
Also removed the dead `C:\Program Files\Python36` PATH lines. No
supported runner image has shipped that Python for years, python is
already on PATH there, and the lines embedded literal quote characters
into PATH.
The trigger branches are deliberately left alone in this commit. These
workflows fire on master only, so they still gate no pull request to dev
-- the same defect threadx fixed in its own suites. That wants a
dispatch run to go green first, on evidence rather than hope, and is a
separate change.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Added the Dependabot configuration the pinned actions need
Follow-up to the pinning in the preceding commit.
A SHA pin with nothing moving it is worse than a floating tag: it holds
CI on whatever was current the day it was written. This repository is the
case study for that. Nothing had ever reported that an action moved, so
actions/upload-artifact sat at v3.1.3 in all four Windows workflows --
four majors behind, and a version GitHub began auto-failing every request
for on 2025-01-30 -- while actions/checkout sat on an unpinned @v4, three
majors behind.
This adds .github/dependabot.yml: weekly, github-actions only, and closes
the reference to that path which the pinning comment in each workflow now
makes. Dependabot understands the SHA form and rewrites the trailing
version comment together with the pin, so the comment cannot drift away
from the SHA it describes.
It follows the configuration eclipse-threadx/threadx added in August
2026, with three differences that are specific to this repository and
documented in the file:
- Only three distinct actions are in use, so the default limit of five
would be enough. Ten is set anyway, to match the sibling repositories
and to leave room for a wave of majors after a long gap.
- There is no .github/CODEOWNERS here, so no reviewer routing exists
for Dependabot to honour. Worth adding, but not by this file.
- The "dependencies" label does not exist in this repository yet.
Dependabot creates it on the first pull request.
Two choices worth stating rather than leaving to be rediscovered.
target-branch is dev. Dependabot reads this file from the default
branch, which is master -- but master is deliberately kept behind dev,
and pull requests belong on dev. So this arms on merge without firing:
nothing happens until a release merge carries the file to master.
Setting target-branch also opts out of Dependabot security updates,
which only ever run against the default branch. For this ecosystem the
cost is small, since an action advisory arrives as an ordinary bump on
the weekly run, but it is a real trade.
Patch and minor are grouped into a single pull request, because a queue
reviewed one item at a time is a queue that gets ignored -- which is the
failure mode this file exists to prevent. Majors stay ungrouped, one
pull request each, because every breaking change met in an action here
has been a major: upload-artifact v4 stopped allowing an artifact name to
be uploaded twice in a run, and v6 requires a runner of 2.327.1 or newer.
Dependabot reports drift, not silence. The Windows workflows rotted
because they never reached a runner, not because nobody was told, and
the runner image fix in the preceding commit is the cure for that half.
The cross-repository reusable workflow in regression_test.yml is
correctly left alone: it references a branch, so it carries no version to
move.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Renamed the Studio IMAGE_INFO struct, which the Windows SDK now defines
With the Windows workflows reaching a runner again, the Studio build
failed immediately on both of them:
guix_studio\system_pngs.h(15,8): error C2011:
'IMAGE_INFO': 'struct' type redefinition
Windows Kits\10\Include\10.0.26100.0\um\Filter.h(340,16): error C2011:
'IMAGE_INFO': 'struct' type redefinition
plus eight C2027 "use of undefined type" errors following from it.
guix_studio declared its own two-field `IMAGE_INFO` in the global
namespace. A newer Windows SDK added a struct of the same name to
um/Filter.h, which arrives through the Windows headers the MFC sources
already include. SDK 10.0.22621.0 does not define it; 10.0.26100.0 does.
This was latent rather than new. studiox.vcxproj sets
`<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>`, the
MSBuild wildcard for "newest installed SDK", so which SDK compiles the
Studio depends entirely on the machine. A developer box with 22621 as its
newest builds; the runner image, which carries 26100, does not. The
collision was simply waiting for whichever came first.
Renamed the Studio type to STUDIO_IMAGE_INFO -- 13 references across five
files. It is private to the Studio, so nothing outside guix_studio sees
the name.
Also dropped the `-winsdk=10.0.22621.0` argument that
build_guix_studio.cmd passed to VsDevCmd. It never had the effect it
looks like it has: the wildcard in the project file governs the SDK the
compile actually uses, so the argument only set the shell environment and
left MSBuild to pick the newest SDK anyway. Keeping it would suggest the
build is pinned to an SDK when it is not. With the collision fixed there
is nothing to pin against.
Verified by rebuilding the Studio against SDK 10.0.22621.0 locally; the
runner will exercise 10.0.26100.0.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Pointed the demo test back at the guix project that exists
The demo compile test could not build the GUIX library at all:
Exception: guix.sln not found.
path=../../../ports/win32/build/vs_2022/guix.vcxproj
522aafc ("Updated scripts for VS Community 2022", February 2025) moved
three paths in test_main.py from vs_2019 to vs_2022. Two of them were
right: guix_studio/build/vs_2019 really had been renamed to vs_2022. The
third was collateral damage from the same search and replace --
ports/win32/build/vs_2019 was never renamed, and still holds the only
guix.vcxproj and guix.sln in the repository.
So --build_guix has raised this exception for eighteen months, before
compiling a single library configuration. Nothing said so, because the
workflow that calls it never reached a runner.
Restored the vs_2019 path. Verified by building the library locally:
guix.vcxproj is Win32-only and still on PlatformToolset v142, and it
produces Debug/gx.lib.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Pointed the view test at the Studio build directory that exists
The Studio view test could not start the Studio at all:
MSBUILD : error MSB1009: Project file does not exist.
Switch: ../../../guix_studio/build/vs_2019/studiox.sln
Exception: Unable to locate Studio executable.
522aafc ("Updated scripts for VS Community 2022", February 2025) renamed
guix_studio/build/vs_2019 to vs_2022 and updated test_demo/test_main.py
for it, but never touched the test_view directory. Three references there
still named the old directory:
- test_view/test_main.py project_sln_path
- test_view/test_utils.py studio_exe_path
- test_view/test_string_import_export.py studio_release
So this suite has been unable to locate the Studio for eighteen months.
Like the guix.vcxproj path in the preceding commit, nothing reported it,
because the workflow that runs it never reached a runner.
The remaining build/vs_2019 references in the tree belong to
ports/win32/build/vs_2019, which is a real directory and is left alone.
This commit makes the suite able to find and launch the Studio. Whether
it then passes on a hosted runner is a separate question -- it drives the
Studio through win32 window handles, and no run has got far enough to
say. Its trigger branches are deliberately not changed here for that
reason.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Ran the Studio demo suites on dev, where the pull requests actually are
The demo and demo compile workflows trigger on master only, for both push
and pull_request. dev is the integration branch, so these suites have
gated no pull request that anybody opened -- the same defect
eclipse-threadx/threadx fixed in its own regression suites in August 2026.
Switched on only after both suites were verified green on a runner, on
this branch, rather than on the assumption that they would be:
GUIX Studio Demo Test 227 of 227 generation tests 147 s
GUIX Studio Demo Compile Test 212 of 212 compile tests 146 s
The other three workflows are deliberately left on their current
triggers, and each for its own reason:
- regression_test.yml does reach a runner, and is red. Its last real
run, 2026-06-30, was 5 failures out of 732: accordion menu,
ml_text_view_32bpp, animation_complete, animation_complete_push_stack
and one more. Adding dev here would make every pull request red on a
suite that has been broken since June. It needs fixing first, and
that is not this change.
- studio_view_test.yml can now find the Studio again, but no run has
got far enough to say whether GUI automation through win32 window
handles survives a hosted runner. Enabling gating on an unknown is
what this commit is written to avoid.
- studio_msix_package.yml is workflow_dispatch only by design. It
packages a release rather than testing a change, so there is nothing
to gate.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ng (#175)
Commit 8a35f69 (#158) made _gx_animation_start() assign the return value of
_gx_canvas_drawing_initiate() to its local "status" variable. That variable is
both the value returned to the caller and the flag that gates linking the
animation into the active list, so the drawing status leaked into the
completion status.
_gx_widget_show() releases the view list of the animation root window on the
line just above that call, so _gx_canvas_drawing_initiate() reports
GX_NO_VIEWS every time an animation canvas is used. The animation was
therefore never linked into _gx_system_animation_list, the frame timer was
never started and _gx_animation_complete() never ran: no animation frames
were produced and GX_ANIMATION_PUSH_STACK never pushed its target onto the
screen stack.
The drawing status is now held in a separate draw_status variable. The #148
guard that keeps _gx_canvas_drawing_complete() from being called after
GX_DRAW_NESTING_EXCEEDED is preserved, while the completion status returned
to the caller is left untouched.
_gx_animation_drag_tracking_start() contains the identical block but returns
GX_SUCCESS unconditionally, so it was not affected. Its variable is renamed to
draw_status as well, with no change in behaviour, so the two functions cannot
drift apart again.
Verified on Linux with the default_build_coverage regression suite. Before the
fix, matching CI run 28477318897 exactly:
447 guix_all_widgets_16bpp_canvas_animation SEGFAULT
457 guix_animation_complete golden_file_frame_id = 1, test_frame_id = 3
458 guix_animation_complete_push_stack Failed, no output
After the fix all three pass and 730 of 732 tests pass. The two remaining
failures, guix_ml_text_view_32bpp and guix_all_widgets_accordion_menu, are
unrelated to animation and are diagnosed separately.
No new regression test is added: guix_animation_complete,
guix_animation_complete_push_stack and guix_all_widgets_16bpp_canvas_animation
already cover this path and are the tests that caught the regression. No
golden file is regenerated, since the change restores the recorded behaviour
rather than altering it. guix_canvas_draw_nesting_overflow_no_output, the test
added by #158, still passes.
No documentation change is required: no public API or behaviour contract
changes.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The fix for issue #148 (#158) made every caller of
_gx_canvas_drawing_initiate() skip its draw when the call returns
GX_DRAW_NESTING_EXCEEDED. Four of those callers push a nested context on the
widget they are already drawing, for one reason only: to narrow the clipping
rectangle to the widget's client area. For them, skipping the draw turns a
correct rendering into no rendering at all, which is what made
guix_all_widgets_accordion_menu report "Frame 12 is different".
The accordion menu screen of the all_widgets demo nests widgets nine levels
deep - multi_level_accordion, menu_list, mla_menu_1_accordion, menu_list,
text_view_3 - and _gx_system_canvas_refresh() consumes two contexts before the
widget tree is walked, so the eight slots of GX_MAX_CONTEXT_NESTING are
exhausted before _gx_multi_line_text_view_text_draw() can push its own. Raising
the limit in a scratch build makes frame 12 match the existing golden file
exactly, which shows that the golden records the correct rendering and that the
text is now being lost rather than merely clipped differently.
When the stack is full there is nothing to push, but the caller's context is
still the right context to draw through: it was created for the same widget and
differs only in its clipping rectangle. These four callers now narrow the
caller's clipping rectangle, draw, and restore it, instead of dropping the draw.
_gx_canvas_drawing_complete() is still not called on overflow, so the stack
corruption that #158 fixed stays fixed.
Applied to _gx_multi_line_text_view_text_draw, _gx_multi_line_text_input_draw,
_gx_rich_text_view_text_draw and _gx_single_line_text_input_draw.
_gx_widget_block_move and _gx_radial_progress_bar_background_draw are left
alone: neither pushes a clip-only context on the widget being drawn.
guix_canvas_draw_nesting_overflow_render_no_output closes the coverage gap that
#158 left. guix_canvas_draw_nesting_overflow_no_output covers detection and
state preservation; the new test covers what a caller must render: that the text
is still drawn at maximum nesting depth, that the borrowed context is handed
back with its nesting count, context pointer and clipping rectangle unchanged,
and that the pixels match those produced when a nested context is available.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
b8bb23b (#159) fixed issue #130 by letting the overflow branch of
_gx_multi_line_text_view_display_info_get() run when an ASCII space is the
character that overflows the available width. That branch consumes the space
and any consecutive spaces without adding them to the row width, which is
correct, and then breaks. When the whitespace ran to the end of the source
line, the line terminator was left behind: the next call started on it, hit
the GX_KEY_LINE_FEED case immediately and returned a row of one byte and zero
width, which draws as a blank line that is not in the text.
The overflow branch now takes the line terminator with the whitespace when
nothing else separates them, handling a bare line feed and a carriage return /
line feed pair, and guarding the second byte on the remaining length -- unlike
the pre-existing GX_KEY_CARRIAGE_RETURN case earlier in the same loop, which
reads ch.gx_string_ptr[1] unchecked. gx_text_display_width is untouched, so the
issue #130 fix stands.
Two such rows appear in the guix_ml_text_view_32bpp fixture, at string offsets
2992 and 23988 of readme_guix_generic.txt: "...Improved internal logic." with
twelve trailing spaces, and "...cursor_pos_calculate.c" with one. Both were
confirmed with a conditional breakpoint on display_number == 1 and
display_width == 0 preceded by a space, which fires exactly twice on the
pre-fix code and never after.
The two extra rows are why 268 of that test's 300 frames and 144 of
guix_bidi_text_draw_32bpp's 429 frames have differed from their golden data
since June. Nothing inside GUIX reads gx_text_display_width -- every caller
uses only gx_text_display_number, to advance its index -- so the row count
alone governs where every row starts, and two extra rows change the
scrollbar's value-to-pixel mapping. Every scroll step then lands at a slightly
different pixel offset and the whole text block is drawn a few pixels up or
down. Of the 300 frames, 297 were a pure vertical shift of otherwise identical
text; only 3 were laid out differently, and those 3 are the two places above.
No golden data is regenerated. gx_multi_line_text_view_text_total_rows for the
long text view goes back from 2226 to 2224, and both tests pass against their
existing golden files.
guix_ml_text_view_word_wrap_no_output did not protect #159's fix: it passed on
the pre-#159 code as well. Its available_width was one pixel too generous --
a_width + space_width, so the over-wide row the old code produced measured
exactly available_width and still satisfied "width <= available_width". It is
now a_width + space_width - 1, which makes appending the space genuinely
overflow, and three cases are added: a line feed terminator, a carriage return
/ line feed terminator, and the total row count that
_gx_multi_line_text_view_string_total_rows_compute() derives from them. That
last one is the quantity the golden frames actually depend on, and the only
one of the three a unit test can pin without golden data.
The test was verified to discriminate in both directions by rebuilding against
each. Against the pre-#159 source it fails four width assertions, which is
issue #130. Against #159 as shipped it fails the line feed row (2 bytes, not
3), the carriage return / line feed row (2, not 4) and the row count (3, not
2). It passes only with both fixes in place.
Verified on Linux across all eighteen build configurations, 1847 tests, no
failures. default_build_coverage 733/733, dynamic_bidi_text_build 3/3
including guix_bidi_text_draw_32bpp, no_utf8_build_coverage 135/135.
One coverage boundary worth naming: the unit test builds against the
all_widgets demo, which is not in NO_UTF8_DEMOS, so it runs in
default_build_coverage and disable_error_check_build but not in the
GX_UTF8_SUPPORT-off configurations. The changed code is common to both paths
-- only the surrounding character advance differs -- and no_utf8_build_coverage
covers it through its 135 golden tests.
No documentation change is required. _gx_multi_line_text_view_display_info_get
is internal, and the GUIX documentation does not describe multi-line text view
wrapping behaviour for trailing whitespace.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
)
_gx_multi_line_text_view_display_info_get() decided whether a line terminator
was "\r" or "\r\n" by examining ch.gx_string_ptr[1], the byte after the current
character, without checking that a byte remained. When the carriage return is
the last byte of the range, that access is outside the caller's buffer.
A GX_STRING carries its own length and need not be NUL terminated -- that is
why the _ext string API exists, and it is what the deprecation warning added in
#167 says about the older char * API. _gx_multi_line_text_view_text_set_ext()
stores the caller's GX_STRING verbatim unless GX_STYLE_TEXT_COPY is set, and
callers of this function pass end_index = gx_string_length, so in the default
configuration the read lands one byte past what the application supplied.
The consequence is not only the read. If that byte happens to be 0x0A, the
function reports a two-byte terminator for a one-byte remainder, so every
caller advances its index by one more than the string holds. In
_gx_multi_line_text_view_string_total_rows_compute() the loop then exits with
index == gx_string_length + 1 and evaluates string.gx_string_ptr[index - 1],
one byte past the end as well, and can add a row that is not in the text. The
same over-advance reaches the line index cache in
gx_multi_line_text_view_line_cache_update.c and the cursor arithmetic in
gx_multi_line_text_input_cursor_pos_update.c.
Both reads now go through string rather than ch. string has already been
advanced past the current character and its length is exactly the number of
bytes still readable there, so guarding on it bounds the access to what the
caller supplied.
The same idiom in _gx_multi_line_text_input_new_line_character_get() is
corrected as well. That one is not currently reachable as a read past the end:
its only caller, _gx_multi_line_text_input_text_set_ext(), writes a NUL
immediately after the copied text. It is fixed anyway because it depends on an
invariant established in a different function and documented nowhere, and
because the byte it reads decides whether the widget inserts a one- or two-byte
terminator on every subsequent Enter.
The rest of the codebase was audited for the same shape. Five other sites
handle a carriage return followed by a possible line feed; all five are already
correct. gx_rich_text_view_line_info_get.c, gx_multi_line_text_input_char_insert.c
and gx_utility_bidi_paragraph_reorder.c guard on the remaining length, the
preprocessed-line-break branch at the top of the changed function guards on it
too, and gx_multi_line_text_button_line_pointers_set.c walks a NUL-terminated
char * buffer where the byte after a carriage return is at worst the
terminator.
guix_ml_text_line_terminator_bounds_no_output covers this. It places a line
feed immediately after the string under test but outside the length the widget
is given, so the over-read is observable without a sanitizer: the unfixed code
counts the out-of-bounds byte and reports a 4-byte row for a 3-byte string,
which the test catches as "Expected: 3, Got: 4". Two controls pin the cases
that must not change -- a "\r\n" pair genuinely inside the string still counts
as two bytes, and a carriage return followed by ordinary text still counts as
one.
The test also asserts the terminator a multi-line text input adopts from its
text, for a lone carriage return and for a "\r\n" pair. Those two cases pass
both before and after the change, for the reason given above; they are there to
pin the behaviour, not to reproduce a fault.
Verified on Linux across all eighteen build configurations, no failures.
No documentation change is required: no public API or behaviour contract
changes, and the corrected behaviour is what the documented terminator handling
already describes.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX Studio Demo

227 tests 227 ✅ 23m 30s ⏱️
1 suites 0 💤
1 files 0 ❌

Results for commit e2d28f5.

♻️ This comment has been updated with latest results.

@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX Studio Demo Compile

213 tests 213 ✅ 18m 59s ⏱️
1 suites 0 💤
1 files 0 ❌

Results for commit e2d28f5.

♻️ This comment has been updated with latest results.

@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX

1 849 tests +1 117 1 849 ✔️ +1 122 31m 11s ⏱️ + 18m 15s
18 suites + 17 0 💤 ± 0 
18 files + 17 0 - 5 

Results for commit e2d28f5. ± Comparison against base commit 826487c.

♻️ This comment has been updated with latest results.

@github-actions

Copy link
Copy Markdown

Code Coverage

PackageLine RateBranch RateHealth
default_build_coverage Package 1100%99%
Summary100% (44377 / 44475)99% (24284 / 24434)

@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX Studio View

40 tests 40 ✅ 1h 21m 55s ⏱️
1 suites 0 💤
1 files 0 ❌

Results for commit e2d28f5.

♻️ This comment has been updated with latest results.

compare_file() locates the line where comparison should start, so that the
banner GUIX Studio writes into every generated file -- which carries a revision
string and a generation timestamp -- is skipped rather than compared. It looked
that line up correctly in the first file and then looked it up in the first file
again for the second:
for line in list_2:
if compare_start_string in line:
start_row_2 = list_1.index(line)
So the golden was sliced at the *generated* file's offset. While both files
happened to reach "#include" on the same line the mistake was invisible, which
is why it survived since the tests were added in #84.
The offsets diverged when 6ba13db added a ten-line MIT licence header to all
111 golden .c and .h files. GUIX Studio does not emit that header, so every
golden now reaches its first #include ten lines later than the file it is
compared against. The golden was therefore sliced ten lines early and compared
banner text against source.
That made every one of the 107 .c and .h comparisons in the suite fail -- all 29
reported test failures -- while the .csv, .xliff and .xml comparisons passed.
Those use skip_line instead of compare_start_string and never reach this code,
which is exactly the split the failure log shows.
The reported mismatch is the same in all 29: a generated "#include" line against
a golden banner comment. Confirmed arithmetically -- in every case the golden
line reported sits exactly ten lines before that golden's own first #include:
generic_16bpp_resources.c reported idx 13, #include at 23, delta 10
generic_16bpp_resources.h reported idx 16, #include at 26, delta 10
generic_16bpp_specifications.c reported idx 14, #include at 24, delta 10
generic_16bpp_specifications.h reported idx 16, #include at 26, delta 10
compare_output_file() is the only caller that passes compare_start_string, so
the change cannot affect the xliff, xml or plain-file comparisons.
Verified by running the suite locally against a Studio built from this tree.
Seven suites that fail in CI now pass with no mismatches at all, each gaining
exactly the tests it had been failing:
CI (broken) local (fixed)
Font 9 / 1 10 / 0
Multi-Themes 18 / 1 19 / 0
Project Import 10 / 2 12 / 0
Trigger Edit 6 / 1 7 / 0
Trigger Target Rename 1 / 1 2 / 0
Bidi Text 3 / 1 4 / 0
Widget Name 3 / 1 4 / 0
Project Import compares a generated specifications file, so this also shows that
the screen flow prototype block added by #173 does not disturb these goldens:
that block is only emitted for projects that use Screen Flow, and these do not.
No golden file is regenerated. The goldens still carry "GUIX Studio Revision
6.1.12.0" and a 2022 timestamp in their banners, and still name Azure RTOS
rather than Eclipse ThreadX, but the banner is what the comparison is supposed
to skip -- so none of that needs to be touched to make the suite correct.
This workflow had never actually executed before 2026-08-27. Every earlier run
was cancelled after queueing 24 hours for the retired windows-2019 image, or
sat awaiting approval on a fork pull request, including the run for the v6.5.1
release itself. #174 gave it a runner again; this is the first result it has
ever produced, and the defect it found is in the test harness rather than in
GUIX or in Studio.
Note that studio_view_test.yml triggers only on pull requests targeting master,
so this change is not exercised by its own pull request into dev. It is
validated by the release pull request that carries dev to master.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@fdesbiens
fdesbiens merged commit 52c1bb4 into masterAug 27, 2026
13 checks passed
Sign up for freeto 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.

1 participant

@fdesbiens
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Merging changes for the v6.5.1.202602a hotfix release - #183

Merged
fdesbiens merged 8 commits into
masterfrom
dev
Aug 27, 2026
Merged

Merging changes for the v6.5.1.202602a hotfix release#183
fdesbiens merged 8 commits into
masterfrom
dev

Conversation

@fdesbiens

Copy link
Copy Markdown
Contributor

No description provided.

fdesbiensand others added 7 commits August 7, 2026 11:58
When GX_DISABLE_DEPRECATED_STRING_API is not defined (i.e., the
pre-5.6 GX_CHAR* API is still enabled), gx_api.h now emits a
#pragma message directing developers to define
GX_DISABLE_DEPRECATED_STRING_API and migrate to the GX_STRING-based
replacement functions.
The deprecated functions omit a string length and cannot safely handle
non-NUL-terminated strings or prevent buffer overruns. All new
applications should use the _ext() replacement variants.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fixed Screen Flow code generation with GX_ANIMATION_POOL_SIZE = 0
GUIX Studio emitted five static helper functions in the generated
specification file as soon as a project used Screen Flow. Two of them,
gx_studio_action_parent_find() and gx_studio_animation_execute(), are
reachable only from the GX_ACTION_TYPE_ANIMATION handler and reference
the GUIX animation API, which is compiled out when the animation pool
is empty:
- gx_system_animation_get() is only defined under
"#if (GX_ANIMATION_POOL_SIZE > 0)" in gx_api.h.
- The body of _gx_animation_start() is guarded the same way in
gx_animation_start.c, so the reference does not resolve at link
time either.
A project that defined GX_ANIMATION_POOL_SIZE as 0 in gx_user.h to save
resources therefore failed to build even when it used no animation
action at all. GCC 14 rejects the implicit declaration outright, and
MSVC fails on the unresolved external.
The screen generator now wraps both helpers and the two
GX_ACTION_TYPE_ANIMATION dispatch sites in
"#if (GX_ANIMATION_POOL_SIZE > 0)". Every other action type keeps
working with an empty pool, and an animation action simply becomes a
no-op at runtime.
The generator also emits forward prototypes for the four static screen
flow helpers, which addresses the secondary request in the issue. Note
that MISRA C:2012 Rule 8.1 covers explicitly specified types and Rule
8.4 applies to objects and functions with external linkage, so static
helpers without a visible prototype were not a deviation. The
prototypes are still worth having and match the declaration the
generator already emits for gx_studio_nested_widget_create().
All the checked-in generated specification files were regenerated so
that they stay in sync with the generator, including the golden files
of the Studio view test.
Added test/guix_studio_test/test_demo/test_animation_pool_size.py as a
regression test, registered in the demo compile CTest project. It
verifies that every checked-in Screen Flow specification file guards
the animation helpers, then compiles them all with
GX_ANIMATION_POOL_SIZE set to 0, promoting the MSVC implicit
declaration warning to an error so the failure mode above is caught.
Fixes#172
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Added hotfix letter support to the release version scripts
The release tooling could not express a hotfix version. Two gaps:
- Get-StudioReleaseVersion rejected anything that was not
major.minor.patch.build, and update_studio_release_version.ps1
hard-coded GUIX_HOTFIX_VERSION as ' '. The documented path to build
a hotfix installer was therefore unusable, and running the script
after a manual edit would have reset the hotfix constant.
- prepare_release.sh looked for gx_port.h under ports only, so it
never updated the version string of the GUIX Studio Win32 port in
guix_studio/ports/gx_port.h.
Get-StudioReleaseVersion now accepts an optional trailing hotfix letter
and exposes it as Hotfix and HotfixDefine. The Win32 and MSIX version
formats are strictly numeric and cannot carry the letter, so the
revision is offset by the position of the letter in the alphabet
instead: 6.5.1.202602 keeps revision 2, and 6.5.1.202602a becomes
revision 3. That keeps the hotfix installer recognizable as the newer
build for Windows upgrade detection and for the Store. Versions without
a hotfix letter resolve exactly as before.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Updated version number constants to 6.5.1.202602a
Prepares the emergency hotfix release that carries the Screen Flow fix
for GX_ANIMATION_POOL_SIZE = 0.
Produced by scripts/update_studio_release_version.ps1 -Version
6.5.1.202602a:
- gx_api.h: GUIX_HOTFIX_VERSION is now 'a'. The major, minor, patch
and build constants are unchanged, as is the 6.5.1 banner.
- studiox.rc: FileVersion and ProductVersion strings are
6.5.1.202602a, FILEVERSION and PRODUCTVERSION are 6,5,1,3.
- guix_installer_release.iss: StudioFullVersion is 6.5.1.202602a and
StudioVersionInfoVersion is 6.5.1.3, so the installer is named
guix_studio_setup_version_6.5.1.202602a.exe.
- Package.appxmanifest: the MSIX identity version is 6.5.1.03.
STUDIOX_VERSION_NUMBER stays at 202602. It stamps the Studio version
into .gxp project files, and a hotfix does not change the project file
format.
Verified against a rebuilt guix_studio.exe, which reports FileVersion
6.5.1.202602a with a numeric file version of 6.5.1.3, matching what
verify_studio_installer.ps1 expects.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Updated port version strings to 6.5.1.202602a
Updated the _gx_version_id string in all 47 gx_port.h files for the
emergency hotfix release, using the same substitution that
prepare_release.sh performs.
This includes guix_studio/ports/gx_port.h, the GUIX Studio Win32 port,
which the release script previously missed because it only searched the
ports directory. That gap is fixed in the preceding commit, so a future
release picks the file up automatically.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…#174)
* Revived the Windows workflows, which had reached no runner since 2025
The four GUIX Studio workflows requested `runs-on: windows-2019`, an
image GitHub retired on 2025-06-30. Nothing can match a retired label,
so every run queued for the full 24 hours and was then cancelled without
ever starting a job:
run 28477318708 created 2026-06-30T21:33:40Z
cancelled 2026-07-01T21:33:41Z
Every run of these workflows since 2025-09-29 has that same
created-plus-24h-cancelled shape. The Studio demo, demo compile, view and
MSIX package tests have gated nothing for about a year, and a manual
workflow_dispatch on 2026-08-25 queued the same way.
Three more defects were in the path behind that one, so fixing the image
alone would not have produced a green run.
- actions/upload-artifact was pinned at v3.1.3 in all four workflows.
GitHub began auto-failing every request that used v3 on 2025-01-30,
and both upload steps run under `if: success() || failure()`, so
every run would have ended red even with the tests passing.
- The scripts hard-coded a Visual Studio edition path. build_guix.cmd,
test_studio_demo.cmd, test_studio_demo_compile.cmd and
test_studio_view.cmd all call VS 2022 *Enterprise*, which no
developer machine has, and build_guix_studio.cmd calls VS 2022
*Community*, which no GitHub runner has. So the demo test's build
step would have failed on the first line. All five now locate the
installation with vswhere, matching what
build_guix_studio_msix_package.cmd already did, and check the result
instead of assuming the call succeeded. Verified on a Community
install, where the Enterprise path does not exist.
- studio_msix_package.yml uploaded
guix_studio/build/vs_2019/msix_package_project/AppPackages. That
directory has not existed since the solution moved to vs_2022, which
is where build_guix_studio_msix_package.cmd builds, so the artifact
was always empty.
Every action reference is now a 40-character commit SHA with the version
in a trailing comment, following the pattern eclipse-threadx/threadx
adopted in August 2026. A tag can be repointed at any commit; a SHA
cannot, which is what makes "which code ran in our CI" answerable from
the repository. Versions moved with the pinning:
actions/checkout v4 -> v7.0.1
actions/upload-artifact v3.1.3 -> v7.0.1
EnricoMi/publish-unit-test-result-action v2 -> v2.24.0
Compatibility was checked against each new action.yml rather than
assumed, for every input these workflows actually pass: checkout keeps
`submodules`; upload-artifact keeps `name` and `path`, and the one
upload per run means the v4 rule against uploading a name twice does not
bite; the publish action keeps `check_name` and `files`, and the
`composite` variant still exists at v2.24.0, which is the one a Windows
runner needs. All three tags were confirmed to resolve to the SHAs
pinned here, and all three are the current latest.
The runner image is pinned rather than tracking windows-latest, on the
same reasoning threadx applies to ubuntu-24.04: which image the build
ran on should be a reviewable commit, not something that changes
underneath it. Floating would have hidden this failure differently
rather than preventing it.
Also removed the dead `C:\Program Files\Python36` PATH lines. No
supported runner image has shipped that Python for years, python is
already on PATH there, and the lines embedded literal quote characters
into PATH.
The trigger branches are deliberately left alone in this commit. These
workflows fire on master only, so they still gate no pull request to dev
-- the same defect threadx fixed in its own suites. That wants a
dispatch run to go green first, on evidence rather than hope, and is a
separate change.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Added the Dependabot configuration the pinned actions need
Follow-up to the pinning in the preceding commit.
A SHA pin with nothing moving it is worse than a floating tag: it holds
CI on whatever was current the day it was written. This repository is the
case study for that. Nothing had ever reported that an action moved, so
actions/upload-artifact sat at v3.1.3 in all four Windows workflows --
four majors behind, and a version GitHub began auto-failing every request
for on 2025-01-30 -- while actions/checkout sat on an unpinned @v4, three
majors behind.
This adds .github/dependabot.yml: weekly, github-actions only, and closes
the reference to that path which the pinning comment in each workflow now
makes. Dependabot understands the SHA form and rewrites the trailing
version comment together with the pin, so the comment cannot drift away
from the SHA it describes.
It follows the configuration eclipse-threadx/threadx added in August
2026, with three differences that are specific to this repository and
documented in the file:
- Only three distinct actions are in use, so the default limit of five
would be enough. Ten is set anyway, to match the sibling repositories
and to leave room for a wave of majors after a long gap.
- There is no .github/CODEOWNERS here, so no reviewer routing exists
for Dependabot to honour. Worth adding, but not by this file.
- The "dependencies" label does not exist in this repository yet.
Dependabot creates it on the first pull request.
Two choices worth stating rather than leaving to be rediscovered.
target-branch is dev. Dependabot reads this file from the default
branch, which is master -- but master is deliberately kept behind dev,
and pull requests belong on dev. So this arms on merge without firing:
nothing happens until a release merge carries the file to master.
Setting target-branch also opts out of Dependabot security updates,
which only ever run against the default branch. For this ecosystem the
cost is small, since an action advisory arrives as an ordinary bump on
the weekly run, but it is a real trade.
Patch and minor are grouped into a single pull request, because a queue
reviewed one item at a time is a queue that gets ignored -- which is the
failure mode this file exists to prevent. Majors stay ungrouped, one
pull request each, because every breaking change met in an action here
has been a major: upload-artifact v4 stopped allowing an artifact name to
be uploaded twice in a run, and v6 requires a runner of 2.327.1 or newer.
Dependabot reports drift, not silence. The Windows workflows rotted
because they never reached a runner, not because nobody was told, and
the runner image fix in the preceding commit is the cure for that half.
The cross-repository reusable workflow in regression_test.yml is
correctly left alone: it references a branch, so it carries no version to
move.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Renamed the Studio IMAGE_INFO struct, which the Windows SDK now defines
With the Windows workflows reaching a runner again, the Studio build
failed immediately on both of them:
guix_studio\system_pngs.h(15,8): error C2011:
'IMAGE_INFO': 'struct' type redefinition
Windows Kits\10\Include\10.0.26100.0\um\Filter.h(340,16): error C2011:
'IMAGE_INFO': 'struct' type redefinition
plus eight C2027 "use of undefined type" errors following from it.
guix_studio declared its own two-field `IMAGE_INFO` in the global
namespace. A newer Windows SDK added a struct of the same name to
um/Filter.h, which arrives through the Windows headers the MFC sources
already include. SDK 10.0.22621.0 does not define it; 10.0.26100.0 does.
This was latent rather than new. studiox.vcxproj sets
`<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>`, the
MSBuild wildcard for "newest installed SDK", so which SDK compiles the
Studio depends entirely on the machine. A developer box with 22621 as its
newest builds; the runner image, which carries 26100, does not. The
collision was simply waiting for whichever came first.
Renamed the Studio type to STUDIO_IMAGE_INFO -- 13 references across five
files. It is private to the Studio, so nothing outside guix_studio sees
the name.
Also dropped the `-winsdk=10.0.22621.0` argument that
build_guix_studio.cmd passed to VsDevCmd. It never had the effect it
looks like it has: the wildcard in the project file governs the SDK the
compile actually uses, so the argument only set the shell environment and
left MSBuild to pick the newest SDK anyway. Keeping it would suggest the
build is pinned to an SDK when it is not. With the collision fixed there
is nothing to pin against.
Verified by rebuilding the Studio against SDK 10.0.22621.0 locally; the
runner will exercise 10.0.26100.0.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Pointed the demo test back at the guix project that exists
The demo compile test could not build the GUIX library at all:
Exception: guix.sln not found.
path=../../../ports/win32/build/vs_2022/guix.vcxproj
522aafc ("Updated scripts for VS Community 2022", February 2025) moved
three paths in test_main.py from vs_2019 to vs_2022. Two of them were
right: guix_studio/build/vs_2019 really had been renamed to vs_2022. The
third was collateral damage from the same search and replace --
ports/win32/build/vs_2019 was never renamed, and still holds the only
guix.vcxproj and guix.sln in the repository.
So --build_guix has raised this exception for eighteen months, before
compiling a single library configuration. Nothing said so, because the
workflow that calls it never reached a runner.
Restored the vs_2019 path. Verified by building the library locally:
guix.vcxproj is Win32-only and still on PlatformToolset v142, and it
produces Debug/gx.lib.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Pointed the view test at the Studio build directory that exists
The Studio view test could not start the Studio at all:
MSBUILD : error MSB1009: Project file does not exist.
Switch: ../../../guix_studio/build/vs_2019/studiox.sln
Exception: Unable to locate Studio executable.
522aafc ("Updated scripts for VS Community 2022", February 2025) renamed
guix_studio/build/vs_2019 to vs_2022 and updated test_demo/test_main.py
for it, but never touched the test_view directory. Three references there
still named the old directory:
- test_view/test_main.py project_sln_path
- test_view/test_utils.py studio_exe_path
- test_view/test_string_import_export.py studio_release
So this suite has been unable to locate the Studio for eighteen months.
Like the guix.vcxproj path in the preceding commit, nothing reported it,
because the workflow that runs it never reached a runner.
The remaining build/vs_2019 references in the tree belong to
ports/win32/build/vs_2019, which is a real directory and is left alone.
This commit makes the suite able to find and launch the Studio. Whether
it then passes on a hosted runner is a separate question -- it drives the
Studio through win32 window handles, and no run has got far enough to
say. Its trigger branches are deliberately not changed here for that
reason.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Ran the Studio demo suites on dev, where the pull requests actually are
The demo and demo compile workflows trigger on master only, for both push
and pull_request. dev is the integration branch, so these suites have
gated no pull request that anybody opened -- the same defect
eclipse-threadx/threadx fixed in its own regression suites in August 2026.
Switched on only after both suites were verified green on a runner, on
this branch, rather than on the assumption that they would be:
GUIX Studio Demo Test 227 of 227 generation tests 147 s
GUIX Studio Demo Compile Test 212 of 212 compile tests 146 s
The other three workflows are deliberately left on their current
triggers, and each for its own reason:
- regression_test.yml does reach a runner, and is red. Its last real
run, 2026-06-30, was 5 failures out of 732: accordion menu,
ml_text_view_32bpp, animation_complete, animation_complete_push_stack
and one more. Adding dev here would make every pull request red on a
suite that has been broken since June. It needs fixing first, and
that is not this change.
- studio_view_test.yml can now find the Studio again, but no run has
got far enough to say whether GUI automation through win32 window
handles survives a hosted runner. Enabling gating on an unknown is
what this commit is written to avoid.
- studio_msix_package.yml is workflow_dispatch only by design. It
packages a release rather than testing a change, so there is nothing
to gate.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ng (#175)
Commit 8a35f69 (#158) made _gx_animation_start() assign the return value of
_gx_canvas_drawing_initiate() to its local "status" variable. That variable is
both the value returned to the caller and the flag that gates linking the
animation into the active list, so the drawing status leaked into the
completion status.
_gx_widget_show() releases the view list of the animation root window on the
line just above that call, so _gx_canvas_drawing_initiate() reports
GX_NO_VIEWS every time an animation canvas is used. The animation was
therefore never linked into _gx_system_animation_list, the frame timer was
never started and _gx_animation_complete() never ran: no animation frames
were produced and GX_ANIMATION_PUSH_STACK never pushed its target onto the
screen stack.
The drawing status is now held in a separate draw_status variable. The #148
guard that keeps _gx_canvas_drawing_complete() from being called after
GX_DRAW_NESTING_EXCEEDED is preserved, while the completion status returned
to the caller is left untouched.
_gx_animation_drag_tracking_start() contains the identical block but returns
GX_SUCCESS unconditionally, so it was not affected. Its variable is renamed to
draw_status as well, with no change in behaviour, so the two functions cannot
drift apart again.
Verified on Linux with the default_build_coverage regression suite. Before the
fix, matching CI run 28477318897 exactly:
447 guix_all_widgets_16bpp_canvas_animation SEGFAULT
457 guix_animation_complete golden_file_frame_id = 1, test_frame_id = 3
458 guix_animation_complete_push_stack Failed, no output
After the fix all three pass and 730 of 732 tests pass. The two remaining
failures, guix_ml_text_view_32bpp and guix_all_widgets_accordion_menu, are
unrelated to animation and are diagnosed separately.
No new regression test is added: guix_animation_complete,
guix_animation_complete_push_stack and guix_all_widgets_16bpp_canvas_animation
already cover this path and are the tests that caught the regression. No
golden file is regenerated, since the change restores the recorded behaviour
rather than altering it. guix_canvas_draw_nesting_overflow_no_output, the test
added by #158, still passes.
No documentation change is required: no public API or behaviour contract
changes.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The fix for issue #148 (#158) made every caller of
_gx_canvas_drawing_initiate() skip its draw when the call returns
GX_DRAW_NESTING_EXCEEDED. Four of those callers push a nested context on the
widget they are already drawing, for one reason only: to narrow the clipping
rectangle to the widget's client area. For them, skipping the draw turns a
correct rendering into no rendering at all, which is what made
guix_all_widgets_accordion_menu report "Frame 12 is different".
The accordion menu screen of the all_widgets demo nests widgets nine levels
deep - multi_level_accordion, menu_list, mla_menu_1_accordion, menu_list,
text_view_3 - and _gx_system_canvas_refresh() consumes two contexts before the
widget tree is walked, so the eight slots of GX_MAX_CONTEXT_NESTING are
exhausted before _gx_multi_line_text_view_text_draw() can push its own. Raising
the limit in a scratch build makes frame 12 match the existing golden file
exactly, which shows that the golden records the correct rendering and that the
text is now being lost rather than merely clipped differently.
When the stack is full there is nothing to push, but the caller's context is
still the right context to draw through: it was created for the same widget and
differs only in its clipping rectangle. These four callers now narrow the
caller's clipping rectangle, draw, and restore it, instead of dropping the draw.
_gx_canvas_drawing_complete() is still not called on overflow, so the stack
corruption that #158 fixed stays fixed.
Applied to _gx_multi_line_text_view_text_draw, _gx_multi_line_text_input_draw,
_gx_rich_text_view_text_draw and _gx_single_line_text_input_draw.
_gx_widget_block_move and _gx_radial_progress_bar_background_draw are left
alone: neither pushes a clip-only context on the widget being drawn.
guix_canvas_draw_nesting_overflow_render_no_output closes the coverage gap that
#158 left. guix_canvas_draw_nesting_overflow_no_output covers detection and
state preservation; the new test covers what a caller must render: that the text
is still drawn at maximum nesting depth, that the borrowed context is handed
back with its nesting count, context pointer and clipping rectangle unchanged,
and that the pixels match those produced when a nested context is available.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
b8bb23b (#159) fixed issue #130 by letting the overflow branch of
_gx_multi_line_text_view_display_info_get() run when an ASCII space is the
character that overflows the available width. That branch consumes the space
and any consecutive spaces without adding them to the row width, which is
correct, and then breaks. When the whitespace ran to the end of the source
line, the line terminator was left behind: the next call started on it, hit
the GX_KEY_LINE_FEED case immediately and returned a row of one byte and zero
width, which draws as a blank line that is not in the text.
The overflow branch now takes the line terminator with the whitespace when
nothing else separates them, handling a bare line feed and a carriage return /
line feed pair, and guarding the second byte on the remaining length -- unlike
the pre-existing GX_KEY_CARRIAGE_RETURN case earlier in the same loop, which
reads ch.gx_string_ptr[1] unchecked. gx_text_display_width is untouched, so the
issue #130 fix stands.
Two such rows appear in the guix_ml_text_view_32bpp fixture, at string offsets
2992 and 23988 of readme_guix_generic.txt: "...Improved internal logic." with
twelve trailing spaces, and "...cursor_pos_calculate.c" with one. Both were
confirmed with a conditional breakpoint on display_number == 1 and
display_width == 0 preceded by a space, which fires exactly twice on the
pre-fix code and never after.
The two extra rows are why 268 of that test's 300 frames and 144 of
guix_bidi_text_draw_32bpp's 429 frames have differed from their golden data
since June. Nothing inside GUIX reads gx_text_display_width -- every caller
uses only gx_text_display_number, to advance its index -- so the row count
alone governs where every row starts, and two extra rows change the
scrollbar's value-to-pixel mapping. Every scroll step then lands at a slightly
different pixel offset and the whole text block is drawn a few pixels up or
down. Of the 300 frames, 297 were a pure vertical shift of otherwise identical
text; only 3 were laid out differently, and those 3 are the two places above.
No golden data is regenerated. gx_multi_line_text_view_text_total_rows for the
long text view goes back from 2226 to 2224, and both tests pass against their
existing golden files.
guix_ml_text_view_word_wrap_no_output did not protect #159's fix: it passed on
the pre-#159 code as well. Its available_width was one pixel too generous --
a_width + space_width, so the over-wide row the old code produced measured
exactly available_width and still satisfied "width <= available_width". It is
now a_width + space_width - 1, which makes appending the space genuinely
overflow, and three cases are added: a line feed terminator, a carriage return
/ line feed terminator, and the total row count that
_gx_multi_line_text_view_string_total_rows_compute() derives from them. That
last one is the quantity the golden frames actually depend on, and the only
one of the three a unit test can pin without golden data.
The test was verified to discriminate in both directions by rebuilding against
each. Against the pre-#159 source it fails four width assertions, which is
issue #130. Against #159 as shipped it fails the line feed row (2 bytes, not
3), the carriage return / line feed row (2, not 4) and the row count (3, not
2). It passes only with both fixes in place.
Verified on Linux across all eighteen build configurations, 1847 tests, no
failures. default_build_coverage 733/733, dynamic_bidi_text_build 3/3
including guix_bidi_text_draw_32bpp, no_utf8_build_coverage 135/135.
One coverage boundary worth naming: the unit test builds against the
all_widgets demo, which is not in NO_UTF8_DEMOS, so it runs in
default_build_coverage and disable_error_check_build but not in the
GX_UTF8_SUPPORT-off configurations. The changed code is common to both paths
-- only the surrounding character advance differs -- and no_utf8_build_coverage
covers it through its 135 golden tests.
No documentation change is required. _gx_multi_line_text_view_display_info_get
is internal, and the GUIX documentation does not describe multi-line text view
wrapping behaviour for trailing whitespace.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
)
_gx_multi_line_text_view_display_info_get() decided whether a line terminator
was "\r" or "\r\n" by examining ch.gx_string_ptr[1], the byte after the current
character, without checking that a byte remained. When the carriage return is
the last byte of the range, that access is outside the caller's buffer.
A GX_STRING carries its own length and need not be NUL terminated -- that is
why the _ext string API exists, and it is what the deprecation warning added in
#167 says about the older char * API. _gx_multi_line_text_view_text_set_ext()
stores the caller's GX_STRING verbatim unless GX_STYLE_TEXT_COPY is set, and
callers of this function pass end_index = gx_string_length, so in the default
configuration the read lands one byte past what the application supplied.
The consequence is not only the read. If that byte happens to be 0x0A, the
function reports a two-byte terminator for a one-byte remainder, so every
caller advances its index by one more than the string holds. In
_gx_multi_line_text_view_string_total_rows_compute() the loop then exits with
index == gx_string_length + 1 and evaluates string.gx_string_ptr[index - 1],
one byte past the end as well, and can add a row that is not in the text. The
same over-advance reaches the line index cache in
gx_multi_line_text_view_line_cache_update.c and the cursor arithmetic in
gx_multi_line_text_input_cursor_pos_update.c.
Both reads now go through string rather than ch. string has already been
advanced past the current character and its length is exactly the number of
bytes still readable there, so guarding on it bounds the access to what the
caller supplied.
The same idiom in _gx_multi_line_text_input_new_line_character_get() is
corrected as well. That one is not currently reachable as a read past the end:
its only caller, _gx_multi_line_text_input_text_set_ext(), writes a NUL
immediately after the copied text. It is fixed anyway because it depends on an
invariant established in a different function and documented nowhere, and
because the byte it reads decides whether the widget inserts a one- or two-byte
terminator on every subsequent Enter.
The rest of the codebase was audited for the same shape. Five other sites
handle a carriage return followed by a possible line feed; all five are already
correct. gx_rich_text_view_line_info_get.c, gx_multi_line_text_input_char_insert.c
and gx_utility_bidi_paragraph_reorder.c guard on the remaining length, the
preprocessed-line-break branch at the top of the changed function guards on it
too, and gx_multi_line_text_button_line_pointers_set.c walks a NUL-terminated
char * buffer where the byte after a carriage return is at worst the
terminator.
guix_ml_text_line_terminator_bounds_no_output covers this. It places a line
feed immediately after the string under test but outside the length the widget
is given, so the over-read is observable without a sanitizer: the unfixed code
counts the out-of-bounds byte and reports a 4-byte row for a 3-byte string,
which the test catches as "Expected: 3, Got: 4". Two controls pin the cases
that must not change -- a "\r\n" pair genuinely inside the string still counts
as two bytes, and a carriage return followed by ordinary text still counts as
one.
The test also asserts the terminator a multi-line text input adopts from its
text, for a lone carriage return and for a "\r\n" pair. Those two cases pass
both before and after the change, for the reason given above; they are there to
pin the behaviour, not to reproduce a fault.
Verified on Linux across all eighteen build configurations, no failures.
No documentation change is required: no public API or behaviour contract
changes, and the corrected behaviour is what the documented terminator handling
already describes.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX Studio Demo

227 tests 227 ✅ 23m 30s ⏱️
1 suites 0 💤
1 files 0 ❌

Results for commit e2d28f5.

♻️ This comment has been updated with latest results.

@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX Studio Demo Compile

213 tests 213 ✅ 18m 59s ⏱️
1 suites 0 💤
1 files 0 ❌

Results for commit e2d28f5.

♻️ This comment has been updated with latest results.

@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX

1 849 tests +1 117 1 849 ✔️ +1 122 31m 11s ⏱️ + 18m 15s
18 suites + 17 0 💤 ± 0 
18 files + 17 0 - 5 

Results for commit e2d28f5. ± Comparison against base commit 826487c.

♻️ This comment has been updated with latest results.

@github-actions

Copy link
Copy Markdown

Code Coverage

PackageLine RateBranch RateHealth
default_build_coverage Package 1100%99%
Summary100% (44377 / 44475)99% (24284 / 24434)

@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX Studio View

40 tests 40 ✅ 1h 21m 55s ⏱️
1 suites 0 💤
1 files 0 ❌

Results for commit e2d28f5.

♻️ This comment has been updated with latest results.

compare_file() locates the line where comparison should start, so that the
banner GUIX Studio writes into every generated file -- which carries a revision
string and a generation timestamp -- is skipped rather than compared. It looked
that line up correctly in the first file and then looked it up in the first file
again for the second:
for line in list_2:
if compare_start_string in line:
start_row_2 = list_1.index(line)
So the golden was sliced at the *generated* file's offset. While both files
happened to reach "#include" on the same line the mistake was invisible, which
is why it survived since the tests were added in #84.
The offsets diverged when 6ba13db added a ten-line MIT licence header to all
111 golden .c and .h files. GUIX Studio does not emit that header, so every
golden now reaches its first #include ten lines later than the file it is
compared against. The golden was therefore sliced ten lines early and compared
banner text against source.
That made every one of the 107 .c and .h comparisons in the suite fail -- all 29
reported test failures -- while the .csv, .xliff and .xml comparisons passed.
Those use skip_line instead of compare_start_string and never reach this code,
which is exactly the split the failure log shows.
The reported mismatch is the same in all 29: a generated "#include" line against
a golden banner comment. Confirmed arithmetically -- in every case the golden
line reported sits exactly ten lines before that golden's own first #include:
generic_16bpp_resources.c reported idx 13, #include at 23, delta 10
generic_16bpp_resources.h reported idx 16, #include at 26, delta 10
generic_16bpp_specifications.c reported idx 14, #include at 24, delta 10
generic_16bpp_specifications.h reported idx 16, #include at 26, delta 10
compare_output_file() is the only caller that passes compare_start_string, so
the change cannot affect the xliff, xml or plain-file comparisons.
Verified by running the suite locally against a Studio built from this tree.
Seven suites that fail in CI now pass with no mismatches at all, each gaining
exactly the tests it had been failing:
CI (broken) local (fixed)
Font 9 / 1 10 / 0
Multi-Themes 18 / 1 19 / 0
Project Import 10 / 2 12 / 0
Trigger Edit 6 / 1 7 / 0
Trigger Target Rename 1 / 1 2 / 0
Bidi Text 3 / 1 4 / 0
Widget Name 3 / 1 4 / 0
Project Import compares a generated specifications file, so this also shows that
the screen flow prototype block added by #173 does not disturb these goldens:
that block is only emitted for projects that use Screen Flow, and these do not.
No golden file is regenerated. The goldens still carry "GUIX Studio Revision
6.1.12.0" and a 2022 timestamp in their banners, and still name Azure RTOS
rather than Eclipse ThreadX, but the banner is what the comparison is supposed
to skip -- so none of that needs to be touched to make the suite correct.
This workflow had never actually executed before 2026-08-27. Every earlier run
was cancelled after queueing 24 hours for the retired windows-2019 image, or
sat awaiting approval on a fork pull request, including the run for the v6.5.1
release itself. #174 gave it a runner again; this is the first result it has
ever produced, and the defect it found is in the test harness rather than in
GUIX or in Studio.
Note that studio_view_test.yml triggers only on pull requests targeting master,
so this change is not exercised by its own pull request into dev. It is
validated by the release pull request that carries dev to master.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@fdesbiens
fdesbiens merged commit 52c1bb4 into masterAug 27, 2026
13 checks passed
Sign up for freeto 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.

1 participant

@fdesbiens
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Merging changes for the v6.5.1.202602a hotfix release - #183

Merged
fdesbiens merged 8 commits into
masterfrom
dev
Aug 27, 2026
Merged

Merging changes for the v6.5.1.202602a hotfix release#183
fdesbiens merged 8 commits into
masterfrom
dev

Conversation

@fdesbiens

Copy link
Copy Markdown
Contributor

No description provided.

fdesbiensand others added 7 commits August 7, 2026 11:58
When GX_DISABLE_DEPRECATED_STRING_API is not defined (i.e., the
pre-5.6 GX_CHAR* API is still enabled), gx_api.h now emits a
#pragma message directing developers to define
GX_DISABLE_DEPRECATED_STRING_API and migrate to the GX_STRING-based
replacement functions.
The deprecated functions omit a string length and cannot safely handle
non-NUL-terminated strings or prevent buffer overruns. All new
applications should use the _ext() replacement variants.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fixed Screen Flow code generation with GX_ANIMATION_POOL_SIZE = 0
GUIX Studio emitted five static helper functions in the generated
specification file as soon as a project used Screen Flow. Two of them,
gx_studio_action_parent_find() and gx_studio_animation_execute(), are
reachable only from the GX_ACTION_TYPE_ANIMATION handler and reference
the GUIX animation API, which is compiled out when the animation pool
is empty:
- gx_system_animation_get() is only defined under
"#if (GX_ANIMATION_POOL_SIZE > 0)" in gx_api.h.
- The body of _gx_animation_start() is guarded the same way in
gx_animation_start.c, so the reference does not resolve at link
time either.
A project that defined GX_ANIMATION_POOL_SIZE as 0 in gx_user.h to save
resources therefore failed to build even when it used no animation
action at all. GCC 14 rejects the implicit declaration outright, and
MSVC fails on the unresolved external.
The screen generator now wraps both helpers and the two
GX_ACTION_TYPE_ANIMATION dispatch sites in
"#if (GX_ANIMATION_POOL_SIZE > 0)". Every other action type keeps
working with an empty pool, and an animation action simply becomes a
no-op at runtime.
The generator also emits forward prototypes for the four static screen
flow helpers, which addresses the secondary request in the issue. Note
that MISRA C:2012 Rule 8.1 covers explicitly specified types and Rule
8.4 applies to objects and functions with external linkage, so static
helpers without a visible prototype were not a deviation. The
prototypes are still worth having and match the declaration the
generator already emits for gx_studio_nested_widget_create().
All the checked-in generated specification files were regenerated so
that they stay in sync with the generator, including the golden files
of the Studio view test.
Added test/guix_studio_test/test_demo/test_animation_pool_size.py as a
regression test, registered in the demo compile CTest project. It
verifies that every checked-in Screen Flow specification file guards
the animation helpers, then compiles them all with
GX_ANIMATION_POOL_SIZE set to 0, promoting the MSVC implicit
declaration warning to an error so the failure mode above is caught.
Fixes#172
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Added hotfix letter support to the release version scripts
The release tooling could not express a hotfix version. Two gaps:
- Get-StudioReleaseVersion rejected anything that was not
major.minor.patch.build, and update_studio_release_version.ps1
hard-coded GUIX_HOTFIX_VERSION as ' '. The documented path to build
a hotfix installer was therefore unusable, and running the script
after a manual edit would have reset the hotfix constant.
- prepare_release.sh looked for gx_port.h under ports only, so it
never updated the version string of the GUIX Studio Win32 port in
guix_studio/ports/gx_port.h.
Get-StudioReleaseVersion now accepts an optional trailing hotfix letter
and exposes it as Hotfix and HotfixDefine. The Win32 and MSIX version
formats are strictly numeric and cannot carry the letter, so the
revision is offset by the position of the letter in the alphabet
instead: 6.5.1.202602 keeps revision 2, and 6.5.1.202602a becomes
revision 3. That keeps the hotfix installer recognizable as the newer
build for Windows upgrade detection and for the Store. Versions without
a hotfix letter resolve exactly as before.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Updated version number constants to 6.5.1.202602a
Prepares the emergency hotfix release that carries the Screen Flow fix
for GX_ANIMATION_POOL_SIZE = 0.
Produced by scripts/update_studio_release_version.ps1 -Version
6.5.1.202602a:
- gx_api.h: GUIX_HOTFIX_VERSION is now 'a'. The major, minor, patch
and build constants are unchanged, as is the 6.5.1 banner.
- studiox.rc: FileVersion and ProductVersion strings are
6.5.1.202602a, FILEVERSION and PRODUCTVERSION are 6,5,1,3.
- guix_installer_release.iss: StudioFullVersion is 6.5.1.202602a and
StudioVersionInfoVersion is 6.5.1.3, so the installer is named
guix_studio_setup_version_6.5.1.202602a.exe.
- Package.appxmanifest: the MSIX identity version is 6.5.1.03.
STUDIOX_VERSION_NUMBER stays at 202602. It stamps the Studio version
into .gxp project files, and a hotfix does not change the project file
format.
Verified against a rebuilt guix_studio.exe, which reports FileVersion
6.5.1.202602a with a numeric file version of 6.5.1.3, matching what
verify_studio_installer.ps1 expects.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Updated port version strings to 6.5.1.202602a
Updated the _gx_version_id string in all 47 gx_port.h files for the
emergency hotfix release, using the same substitution that
prepare_release.sh performs.
This includes guix_studio/ports/gx_port.h, the GUIX Studio Win32 port,
which the release script previously missed because it only searched the
ports directory. That gap is fixed in the preceding commit, so a future
release picks the file up automatically.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…#174)
* Revived the Windows workflows, which had reached no runner since 2025
The four GUIX Studio workflows requested `runs-on: windows-2019`, an
image GitHub retired on 2025-06-30. Nothing can match a retired label,
so every run queued for the full 24 hours and was then cancelled without
ever starting a job:
run 28477318708 created 2026-06-30T21:33:40Z
cancelled 2026-07-01T21:33:41Z
Every run of these workflows since 2025-09-29 has that same
created-plus-24h-cancelled shape. The Studio demo, demo compile, view and
MSIX package tests have gated nothing for about a year, and a manual
workflow_dispatch on 2026-08-25 queued the same way.
Three more defects were in the path behind that one, so fixing the image
alone would not have produced a green run.
- actions/upload-artifact was pinned at v3.1.3 in all four workflows.
GitHub began auto-failing every request that used v3 on 2025-01-30,
and both upload steps run under `if: success() || failure()`, so
every run would have ended red even with the tests passing.
- The scripts hard-coded a Visual Studio edition path. build_guix.cmd,
test_studio_demo.cmd, test_studio_demo_compile.cmd and
test_studio_view.cmd all call VS 2022 *Enterprise*, which no
developer machine has, and build_guix_studio.cmd calls VS 2022
*Community*, which no GitHub runner has. So the demo test's build
step would have failed on the first line. All five now locate the
installation with vswhere, matching what
build_guix_studio_msix_package.cmd already did, and check the result
instead of assuming the call succeeded. Verified on a Community
install, where the Enterprise path does not exist.
- studio_msix_package.yml uploaded
guix_studio/build/vs_2019/msix_package_project/AppPackages. That
directory has not existed since the solution moved to vs_2022, which
is where build_guix_studio_msix_package.cmd builds, so the artifact
was always empty.
Every action reference is now a 40-character commit SHA with the version
in a trailing comment, following the pattern eclipse-threadx/threadx
adopted in August 2026. A tag can be repointed at any commit; a SHA
cannot, which is what makes "which code ran in our CI" answerable from
the repository. Versions moved with the pinning:
actions/checkout v4 -> v7.0.1
actions/upload-artifact v3.1.3 -> v7.0.1
EnricoMi/publish-unit-test-result-action v2 -> v2.24.0
Compatibility was checked against each new action.yml rather than
assumed, for every input these workflows actually pass: checkout keeps
`submodules`; upload-artifact keeps `name` and `path`, and the one
upload per run means the v4 rule against uploading a name twice does not
bite; the publish action keeps `check_name` and `files`, and the
`composite` variant still exists at v2.24.0, which is the one a Windows
runner needs. All three tags were confirmed to resolve to the SHAs
pinned here, and all three are the current latest.
The runner image is pinned rather than tracking windows-latest, on the
same reasoning threadx applies to ubuntu-24.04: which image the build
ran on should be a reviewable commit, not something that changes
underneath it. Floating would have hidden this failure differently
rather than preventing it.
Also removed the dead `C:\Program Files\Python36` PATH lines. No
supported runner image has shipped that Python for years, python is
already on PATH there, and the lines embedded literal quote characters
into PATH.
The trigger branches are deliberately left alone in this commit. These
workflows fire on master only, so they still gate no pull request to dev
-- the same defect threadx fixed in its own suites. That wants a
dispatch run to go green first, on evidence rather than hope, and is a
separate change.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Added the Dependabot configuration the pinned actions need
Follow-up to the pinning in the preceding commit.
A SHA pin with nothing moving it is worse than a floating tag: it holds
CI on whatever was current the day it was written. This repository is the
case study for that. Nothing had ever reported that an action moved, so
actions/upload-artifact sat at v3.1.3 in all four Windows workflows --
four majors behind, and a version GitHub began auto-failing every request
for on 2025-01-30 -- while actions/checkout sat on an unpinned @v4, three
majors behind.
This adds .github/dependabot.yml: weekly, github-actions only, and closes
the reference to that path which the pinning comment in each workflow now
makes. Dependabot understands the SHA form and rewrites the trailing
version comment together with the pin, so the comment cannot drift away
from the SHA it describes.
It follows the configuration eclipse-threadx/threadx added in August
2026, with three differences that are specific to this repository and
documented in the file:
- Only three distinct actions are in use, so the default limit of five
would be enough. Ten is set anyway, to match the sibling repositories
and to leave room for a wave of majors after a long gap.
- There is no .github/CODEOWNERS here, so no reviewer routing exists
for Dependabot to honour. Worth adding, but not by this file.
- The "dependencies" label does not exist in this repository yet.
Dependabot creates it on the first pull request.
Two choices worth stating rather than leaving to be rediscovered.
target-branch is dev. Dependabot reads this file from the default
branch, which is master -- but master is deliberately kept behind dev,
and pull requests belong on dev. So this arms on merge without firing:
nothing happens until a release merge carries the file to master.
Setting target-branch also opts out of Dependabot security updates,
which only ever run against the default branch. For this ecosystem the
cost is small, since an action advisory arrives as an ordinary bump on
the weekly run, but it is a real trade.
Patch and minor are grouped into a single pull request, because a queue
reviewed one item at a time is a queue that gets ignored -- which is the
failure mode this file exists to prevent. Majors stay ungrouped, one
pull request each, because every breaking change met in an action here
has been a major: upload-artifact v4 stopped allowing an artifact name to
be uploaded twice in a run, and v6 requires a runner of 2.327.1 or newer.
Dependabot reports drift, not silence. The Windows workflows rotted
because they never reached a runner, not because nobody was told, and
the runner image fix in the preceding commit is the cure for that half.
The cross-repository reusable workflow in regression_test.yml is
correctly left alone: it references a branch, so it carries no version to
move.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Renamed the Studio IMAGE_INFO struct, which the Windows SDK now defines
With the Windows workflows reaching a runner again, the Studio build
failed immediately on both of them:
guix_studio\system_pngs.h(15,8): error C2011:
'IMAGE_INFO': 'struct' type redefinition
Windows Kits\10\Include\10.0.26100.0\um\Filter.h(340,16): error C2011:
'IMAGE_INFO': 'struct' type redefinition
plus eight C2027 "use of undefined type" errors following from it.
guix_studio declared its own two-field `IMAGE_INFO` in the global
namespace. A newer Windows SDK added a struct of the same name to
um/Filter.h, which arrives through the Windows headers the MFC sources
already include. SDK 10.0.22621.0 does not define it; 10.0.26100.0 does.
This was latent rather than new. studiox.vcxproj sets
`<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>`, the
MSBuild wildcard for "newest installed SDK", so which SDK compiles the
Studio depends entirely on the machine. A developer box with 22621 as its
newest builds; the runner image, which carries 26100, does not. The
collision was simply waiting for whichever came first.
Renamed the Studio type to STUDIO_IMAGE_INFO -- 13 references across five
files. It is private to the Studio, so nothing outside guix_studio sees
the name.
Also dropped the `-winsdk=10.0.22621.0` argument that
build_guix_studio.cmd passed to VsDevCmd. It never had the effect it
looks like it has: the wildcard in the project file governs the SDK the
compile actually uses, so the argument only set the shell environment and
left MSBuild to pick the newest SDK anyway. Keeping it would suggest the
build is pinned to an SDK when it is not. With the collision fixed there
is nothing to pin against.
Verified by rebuilding the Studio against SDK 10.0.22621.0 locally; the
runner will exercise 10.0.26100.0.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Pointed the demo test back at the guix project that exists
The demo compile test could not build the GUIX library at all:
Exception: guix.sln not found.
path=../../../ports/win32/build/vs_2022/guix.vcxproj
522aafc ("Updated scripts for VS Community 2022", February 2025) moved
three paths in test_main.py from vs_2019 to vs_2022. Two of them were
right: guix_studio/build/vs_2019 really had been renamed to vs_2022. The
third was collateral damage from the same search and replace --
ports/win32/build/vs_2019 was never renamed, and still holds the only
guix.vcxproj and guix.sln in the repository.
So --build_guix has raised this exception for eighteen months, before
compiling a single library configuration. Nothing said so, because the
workflow that calls it never reached a runner.
Restored the vs_2019 path. Verified by building the library locally:
guix.vcxproj is Win32-only and still on PlatformToolset v142, and it
produces Debug/gx.lib.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Pointed the view test at the Studio build directory that exists
The Studio view test could not start the Studio at all:
MSBUILD : error MSB1009: Project file does not exist.
Switch: ../../../guix_studio/build/vs_2019/studiox.sln
Exception: Unable to locate Studio executable.
522aafc ("Updated scripts for VS Community 2022", February 2025) renamed
guix_studio/build/vs_2019 to vs_2022 and updated test_demo/test_main.py
for it, but never touched the test_view directory. Three references there
still named the old directory:
- test_view/test_main.py project_sln_path
- test_view/test_utils.py studio_exe_path
- test_view/test_string_import_export.py studio_release
So this suite has been unable to locate the Studio for eighteen months.
Like the guix.vcxproj path in the preceding commit, nothing reported it,
because the workflow that runs it never reached a runner.
The remaining build/vs_2019 references in the tree belong to
ports/win32/build/vs_2019, which is a real directory and is left alone.
This commit makes the suite able to find and launch the Studio. Whether
it then passes on a hosted runner is a separate question -- it drives the
Studio through win32 window handles, and no run has got far enough to
say. Its trigger branches are deliberately not changed here for that
reason.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Ran the Studio demo suites on dev, where the pull requests actually are
The demo and demo compile workflows trigger on master only, for both push
and pull_request. dev is the integration branch, so these suites have
gated no pull request that anybody opened -- the same defect
eclipse-threadx/threadx fixed in its own regression suites in August 2026.
Switched on only after both suites were verified green on a runner, on
this branch, rather than on the assumption that they would be:
GUIX Studio Demo Test 227 of 227 generation tests 147 s
GUIX Studio Demo Compile Test 212 of 212 compile tests 146 s
The other three workflows are deliberately left on their current
triggers, and each for its own reason:
- regression_test.yml does reach a runner, and is red. Its last real
run, 2026-06-30, was 5 failures out of 732: accordion menu,
ml_text_view_32bpp, animation_complete, animation_complete_push_stack
and one more. Adding dev here would make every pull request red on a
suite that has been broken since June. It needs fixing first, and
that is not this change.
- studio_view_test.yml can now find the Studio again, but no run has
got far enough to say whether GUI automation through win32 window
handles survives a hosted runner. Enabling gating on an unknown is
what this commit is written to avoid.
- studio_msix_package.yml is workflow_dispatch only by design. It
packages a release rather than testing a change, so there is nothing
to gate.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ng (#175)
Commit 8a35f69 (#158) made _gx_animation_start() assign the return value of
_gx_canvas_drawing_initiate() to its local "status" variable. That variable is
both the value returned to the caller and the flag that gates linking the
animation into the active list, so the drawing status leaked into the
completion status.
_gx_widget_show() releases the view list of the animation root window on the
line just above that call, so _gx_canvas_drawing_initiate() reports
GX_NO_VIEWS every time an animation canvas is used. The animation was
therefore never linked into _gx_system_animation_list, the frame timer was
never started and _gx_animation_complete() never ran: no animation frames
were produced and GX_ANIMATION_PUSH_STACK never pushed its target onto the
screen stack.
The drawing status is now held in a separate draw_status variable. The #148
guard that keeps _gx_canvas_drawing_complete() from being called after
GX_DRAW_NESTING_EXCEEDED is preserved, while the completion status returned
to the caller is left untouched.
_gx_animation_drag_tracking_start() contains the identical block but returns
GX_SUCCESS unconditionally, so it was not affected. Its variable is renamed to
draw_status as well, with no change in behaviour, so the two functions cannot
drift apart again.
Verified on Linux with the default_build_coverage regression suite. Before the
fix, matching CI run 28477318897 exactly:
447 guix_all_widgets_16bpp_canvas_animation SEGFAULT
457 guix_animation_complete golden_file_frame_id = 1, test_frame_id = 3
458 guix_animation_complete_push_stack Failed, no output
After the fix all three pass and 730 of 732 tests pass. The two remaining
failures, guix_ml_text_view_32bpp and guix_all_widgets_accordion_menu, are
unrelated to animation and are diagnosed separately.
No new regression test is added: guix_animation_complete,
guix_animation_complete_push_stack and guix_all_widgets_16bpp_canvas_animation
already cover this path and are the tests that caught the regression. No
golden file is regenerated, since the change restores the recorded behaviour
rather than altering it. guix_canvas_draw_nesting_overflow_no_output, the test
added by #158, still passes.
No documentation change is required: no public API or behaviour contract
changes.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The fix for issue #148 (#158) made every caller of
_gx_canvas_drawing_initiate() skip its draw when the call returns
GX_DRAW_NESTING_EXCEEDED. Four of those callers push a nested context on the
widget they are already drawing, for one reason only: to narrow the clipping
rectangle to the widget's client area. For them, skipping the draw turns a
correct rendering into no rendering at all, which is what made
guix_all_widgets_accordion_menu report "Frame 12 is different".
The accordion menu screen of the all_widgets demo nests widgets nine levels
deep - multi_level_accordion, menu_list, mla_menu_1_accordion, menu_list,
text_view_3 - and _gx_system_canvas_refresh() consumes two contexts before the
widget tree is walked, so the eight slots of GX_MAX_CONTEXT_NESTING are
exhausted before _gx_multi_line_text_view_text_draw() can push its own. Raising
the limit in a scratch build makes frame 12 match the existing golden file
exactly, which shows that the golden records the correct rendering and that the
text is now being lost rather than merely clipped differently.
When the stack is full there is nothing to push, but the caller's context is
still the right context to draw through: it was created for the same widget and
differs only in its clipping rectangle. These four callers now narrow the
caller's clipping rectangle, draw, and restore it, instead of dropping the draw.
_gx_canvas_drawing_complete() is still not called on overflow, so the stack
corruption that #158 fixed stays fixed.
Applied to _gx_multi_line_text_view_text_draw, _gx_multi_line_text_input_draw,
_gx_rich_text_view_text_draw and _gx_single_line_text_input_draw.
_gx_widget_block_move and _gx_radial_progress_bar_background_draw are left
alone: neither pushes a clip-only context on the widget being drawn.
guix_canvas_draw_nesting_overflow_render_no_output closes the coverage gap that
#158 left. guix_canvas_draw_nesting_overflow_no_output covers detection and
state preservation; the new test covers what a caller must render: that the text
is still drawn at maximum nesting depth, that the borrowed context is handed
back with its nesting count, context pointer and clipping rectangle unchanged,
and that the pixels match those produced when a nested context is available.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
b8bb23b (#159) fixed issue #130 by letting the overflow branch of
_gx_multi_line_text_view_display_info_get() run when an ASCII space is the
character that overflows the available width. That branch consumes the space
and any consecutive spaces without adding them to the row width, which is
correct, and then breaks. When the whitespace ran to the end of the source
line, the line terminator was left behind: the next call started on it, hit
the GX_KEY_LINE_FEED case immediately and returned a row of one byte and zero
width, which draws as a blank line that is not in the text.
The overflow branch now takes the line terminator with the whitespace when
nothing else separates them, handling a bare line feed and a carriage return /
line feed pair, and guarding the second byte on the remaining length -- unlike
the pre-existing GX_KEY_CARRIAGE_RETURN case earlier in the same loop, which
reads ch.gx_string_ptr[1] unchecked. gx_text_display_width is untouched, so the
issue #130 fix stands.
Two such rows appear in the guix_ml_text_view_32bpp fixture, at string offsets
2992 and 23988 of readme_guix_generic.txt: "...Improved internal logic." with
twelve trailing spaces, and "...cursor_pos_calculate.c" with one. Both were
confirmed with a conditional breakpoint on display_number == 1 and
display_width == 0 preceded by a space, which fires exactly twice on the
pre-fix code and never after.
The two extra rows are why 268 of that test's 300 frames and 144 of
guix_bidi_text_draw_32bpp's 429 frames have differed from their golden data
since June. Nothing inside GUIX reads gx_text_display_width -- every caller
uses only gx_text_display_number, to advance its index -- so the row count
alone governs where every row starts, and two extra rows change the
scrollbar's value-to-pixel mapping. Every scroll step then lands at a slightly
different pixel offset and the whole text block is drawn a few pixels up or
down. Of the 300 frames, 297 were a pure vertical shift of otherwise identical
text; only 3 were laid out differently, and those 3 are the two places above.
No golden data is regenerated. gx_multi_line_text_view_text_total_rows for the
long text view goes back from 2226 to 2224, and both tests pass against their
existing golden files.
guix_ml_text_view_word_wrap_no_output did not protect #159's fix: it passed on
the pre-#159 code as well. Its available_width was one pixel too generous --
a_width + space_width, so the over-wide row the old code produced measured
exactly available_width and still satisfied "width <= available_width". It is
now a_width + space_width - 1, which makes appending the space genuinely
overflow, and three cases are added: a line feed terminator, a carriage return
/ line feed terminator, and the total row count that
_gx_multi_line_text_view_string_total_rows_compute() derives from them. That
last one is the quantity the golden frames actually depend on, and the only
one of the three a unit test can pin without golden data.
The test was verified to discriminate in both directions by rebuilding against
each. Against the pre-#159 source it fails four width assertions, which is
issue #130. Against #159 as shipped it fails the line feed row (2 bytes, not
3), the carriage return / line feed row (2, not 4) and the row count (3, not
2). It passes only with both fixes in place.
Verified on Linux across all eighteen build configurations, 1847 tests, no
failures. default_build_coverage 733/733, dynamic_bidi_text_build 3/3
including guix_bidi_text_draw_32bpp, no_utf8_build_coverage 135/135.
One coverage boundary worth naming: the unit test builds against the
all_widgets demo, which is not in NO_UTF8_DEMOS, so it runs in
default_build_coverage and disable_error_check_build but not in the
GX_UTF8_SUPPORT-off configurations. The changed code is common to both paths
-- only the surrounding character advance differs -- and no_utf8_build_coverage
covers it through its 135 golden tests.
No documentation change is required. _gx_multi_line_text_view_display_info_get
is internal, and the GUIX documentation does not describe multi-line text view
wrapping behaviour for trailing whitespace.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
)
_gx_multi_line_text_view_display_info_get() decided whether a line terminator
was "\r" or "\r\n" by examining ch.gx_string_ptr[1], the byte after the current
character, without checking that a byte remained. When the carriage return is
the last byte of the range, that access is outside the caller's buffer.
A GX_STRING carries its own length and need not be NUL terminated -- that is
why the _ext string API exists, and it is what the deprecation warning added in
#167 says about the older char * API. _gx_multi_line_text_view_text_set_ext()
stores the caller's GX_STRING verbatim unless GX_STYLE_TEXT_COPY is set, and
callers of this function pass end_index = gx_string_length, so in the default
configuration the read lands one byte past what the application supplied.
The consequence is not only the read. If that byte happens to be 0x0A, the
function reports a two-byte terminator for a one-byte remainder, so every
caller advances its index by one more than the string holds. In
_gx_multi_line_text_view_string_total_rows_compute() the loop then exits with
index == gx_string_length + 1 and evaluates string.gx_string_ptr[index - 1],
one byte past the end as well, and can add a row that is not in the text. The
same over-advance reaches the line index cache in
gx_multi_line_text_view_line_cache_update.c and the cursor arithmetic in
gx_multi_line_text_input_cursor_pos_update.c.
Both reads now go through string rather than ch. string has already been
advanced past the current character and its length is exactly the number of
bytes still readable there, so guarding on it bounds the access to what the
caller supplied.
The same idiom in _gx_multi_line_text_input_new_line_character_get() is
corrected as well. That one is not currently reachable as a read past the end:
its only caller, _gx_multi_line_text_input_text_set_ext(), writes a NUL
immediately after the copied text. It is fixed anyway because it depends on an
invariant established in a different function and documented nowhere, and
because the byte it reads decides whether the widget inserts a one- or two-byte
terminator on every subsequent Enter.
The rest of the codebase was audited for the same shape. Five other sites
handle a carriage return followed by a possible line feed; all five are already
correct. gx_rich_text_view_line_info_get.c, gx_multi_line_text_input_char_insert.c
and gx_utility_bidi_paragraph_reorder.c guard on the remaining length, the
preprocessed-line-break branch at the top of the changed function guards on it
too, and gx_multi_line_text_button_line_pointers_set.c walks a NUL-terminated
char * buffer where the byte after a carriage return is at worst the
terminator.
guix_ml_text_line_terminator_bounds_no_output covers this. It places a line
feed immediately after the string under test but outside the length the widget
is given, so the over-read is observable without a sanitizer: the unfixed code
counts the out-of-bounds byte and reports a 4-byte row for a 3-byte string,
which the test catches as "Expected: 3, Got: 4". Two controls pin the cases
that must not change -- a "\r\n" pair genuinely inside the string still counts
as two bytes, and a carriage return followed by ordinary text still counts as
one.
The test also asserts the terminator a multi-line text input adopts from its
text, for a lone carriage return and for a "\r\n" pair. Those two cases pass
both before and after the change, for the reason given above; they are there to
pin the behaviour, not to reproduce a fault.
Verified on Linux across all eighteen build configurations, no failures.
No documentation change is required: no public API or behaviour contract
changes, and the corrected behaviour is what the documented terminator handling
already describes.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX Studio Demo

227 tests 227 ✅ 23m 30s ⏱️
1 suites 0 💤
1 files 0 ❌

Results for commit e2d28f5.

♻️ This comment has been updated with latest results.

@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX Studio Demo Compile

213 tests 213 ✅ 18m 59s ⏱️
1 suites 0 💤
1 files 0 ❌

Results for commit e2d28f5.

♻️ This comment has been updated with latest results.

@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX

1 849 tests +1 117 1 849 ✔️ +1 122 31m 11s ⏱️ + 18m 15s
18 suites + 17 0 💤 ± 0 
18 files + 17 0 - 5 

Results for commit e2d28f5. ± Comparison against base commit 826487c.

♻️ This comment has been updated with latest results.

@github-actions

Copy link
Copy Markdown

Code Coverage

PackageLine RateBranch RateHealth
default_build_coverage Package 1100%99%
Summary100% (44377 / 44475)99% (24284 / 24434)

@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX Studio View

40 tests 40 ✅ 1h 21m 55s ⏱️
1 suites 0 💤
1 files 0 ❌

Results for commit e2d28f5.

♻️ This comment has been updated with latest results.

compare_file() locates the line where comparison should start, so that the
banner GUIX Studio writes into every generated file -- which carries a revision
string and a generation timestamp -- is skipped rather than compared. It looked
that line up correctly in the first file and then looked it up in the first file
again for the second:
for line in list_2:
if compare_start_string in line:
start_row_2 = list_1.index(line)
So the golden was sliced at the *generated* file's offset. While both files
happened to reach "#include" on the same line the mistake was invisible, which
is why it survived since the tests were added in #84.
The offsets diverged when 6ba13db added a ten-line MIT licence header to all
111 golden .c and .h files. GUIX Studio does not emit that header, so every
golden now reaches its first #include ten lines later than the file it is
compared against. The golden was therefore sliced ten lines early and compared
banner text against source.
That made every one of the 107 .c and .h comparisons in the suite fail -- all 29
reported test failures -- while the .csv, .xliff and .xml comparisons passed.
Those use skip_line instead of compare_start_string and never reach this code,
which is exactly the split the failure log shows.
The reported mismatch is the same in all 29: a generated "#include" line against
a golden banner comment. Confirmed arithmetically -- in every case the golden
line reported sits exactly ten lines before that golden's own first #include:
generic_16bpp_resources.c reported idx 13, #include at 23, delta 10
generic_16bpp_resources.h reported idx 16, #include at 26, delta 10
generic_16bpp_specifications.c reported idx 14, #include at 24, delta 10
generic_16bpp_specifications.h reported idx 16, #include at 26, delta 10
compare_output_file() is the only caller that passes compare_start_string, so
the change cannot affect the xliff, xml or plain-file comparisons.
Verified by running the suite locally against a Studio built from this tree.
Seven suites that fail in CI now pass with no mismatches at all, each gaining
exactly the tests it had been failing:
CI (broken) local (fixed)
Font 9 / 1 10 / 0
Multi-Themes 18 / 1 19 / 0
Project Import 10 / 2 12 / 0
Trigger Edit 6 / 1 7 / 0
Trigger Target Rename 1 / 1 2 / 0
Bidi Text 3 / 1 4 / 0
Widget Name 3 / 1 4 / 0
Project Import compares a generated specifications file, so this also shows that
the screen flow prototype block added by #173 does not disturb these goldens:
that block is only emitted for projects that use Screen Flow, and these do not.
No golden file is regenerated. The goldens still carry "GUIX Studio Revision
6.1.12.0" and a 2022 timestamp in their banners, and still name Azure RTOS
rather than Eclipse ThreadX, but the banner is what the comparison is supposed
to skip -- so none of that needs to be touched to make the suite correct.
This workflow had never actually executed before 2026-08-27. Every earlier run
was cancelled after queueing 24 hours for the retired windows-2019 image, or
sat awaiting approval on a fork pull request, including the run for the v6.5.1
release itself. #174 gave it a runner again; this is the first result it has
ever produced, and the defect it found is in the test harness rather than in
GUIX or in Studio.
Note that studio_view_test.yml triggers only on pull requests targeting master,
so this change is not exercised by its own pull request into dev. It is
validated by the release pull request that carries dev to master.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@fdesbiens
fdesbiens merged commit 52c1bb4 into masterAug 27, 2026
13 checks passed
Sign up for freeto 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.

1 participant

@fdesbiens
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Merging changes for the v6.5.1.202602a hotfix release - #183

Merged
fdesbiens merged 8 commits into
masterfrom
dev
Aug 27, 2026
Merged

Merging changes for the v6.5.1.202602a hotfix release#183
fdesbiens merged 8 commits into
masterfrom
dev

Conversation

@fdesbiens

Copy link
Copy Markdown
Contributor

No description provided.

fdesbiensand others added 7 commits August 7, 2026 11:58
When GX_DISABLE_DEPRECATED_STRING_API is not defined (i.e., the
pre-5.6 GX_CHAR* API is still enabled), gx_api.h now emits a
#pragma message directing developers to define
GX_DISABLE_DEPRECATED_STRING_API and migrate to the GX_STRING-based
replacement functions.
The deprecated functions omit a string length and cannot safely handle
non-NUL-terminated strings or prevent buffer overruns. All new
applications should use the _ext() replacement variants.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fixed Screen Flow code generation with GX_ANIMATION_POOL_SIZE = 0
GUIX Studio emitted five static helper functions in the generated
specification file as soon as a project used Screen Flow. Two of them,
gx_studio_action_parent_find() and gx_studio_animation_execute(), are
reachable only from the GX_ACTION_TYPE_ANIMATION handler and reference
the GUIX animation API, which is compiled out when the animation pool
is empty:
- gx_system_animation_get() is only defined under
"#if (GX_ANIMATION_POOL_SIZE > 0)" in gx_api.h.
- The body of _gx_animation_start() is guarded the same way in
gx_animation_start.c, so the reference does not resolve at link
time either.
A project that defined GX_ANIMATION_POOL_SIZE as 0 in gx_user.h to save
resources therefore failed to build even when it used no animation
action at all. GCC 14 rejects the implicit declaration outright, and
MSVC fails on the unresolved external.
The screen generator now wraps both helpers and the two
GX_ACTION_TYPE_ANIMATION dispatch sites in
"#if (GX_ANIMATION_POOL_SIZE > 0)". Every other action type keeps
working with an empty pool, and an animation action simply becomes a
no-op at runtime.
The generator also emits forward prototypes for the four static screen
flow helpers, which addresses the secondary request in the issue. Note
that MISRA C:2012 Rule 8.1 covers explicitly specified types and Rule
8.4 applies to objects and functions with external linkage, so static
helpers without a visible prototype were not a deviation. The
prototypes are still worth having and match the declaration the
generator already emits for gx_studio_nested_widget_create().
All the checked-in generated specification files were regenerated so
that they stay in sync with the generator, including the golden files
of the Studio view test.
Added test/guix_studio_test/test_demo/test_animation_pool_size.py as a
regression test, registered in the demo compile CTest project. It
verifies that every checked-in Screen Flow specification file guards
the animation helpers, then compiles them all with
GX_ANIMATION_POOL_SIZE set to 0, promoting the MSVC implicit
declaration warning to an error so the failure mode above is caught.
Fixes#172
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Added hotfix letter support to the release version scripts
The release tooling could not express a hotfix version. Two gaps:
- Get-StudioReleaseVersion rejected anything that was not
major.minor.patch.build, and update_studio_release_version.ps1
hard-coded GUIX_HOTFIX_VERSION as ' '. The documented path to build
a hotfix installer was therefore unusable, and running the script
after a manual edit would have reset the hotfix constant.
- prepare_release.sh looked for gx_port.h under ports only, so it
never updated the version string of the GUIX Studio Win32 port in
guix_studio/ports/gx_port.h.
Get-StudioReleaseVersion now accepts an optional trailing hotfix letter
and exposes it as Hotfix and HotfixDefine. The Win32 and MSIX version
formats are strictly numeric and cannot carry the letter, so the
revision is offset by the position of the letter in the alphabet
instead: 6.5.1.202602 keeps revision 2, and 6.5.1.202602a becomes
revision 3. That keeps the hotfix installer recognizable as the newer
build for Windows upgrade detection and for the Store. Versions without
a hotfix letter resolve exactly as before.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Updated version number constants to 6.5.1.202602a
Prepares the emergency hotfix release that carries the Screen Flow fix
for GX_ANIMATION_POOL_SIZE = 0.
Produced by scripts/update_studio_release_version.ps1 -Version
6.5.1.202602a:
- gx_api.h: GUIX_HOTFIX_VERSION is now 'a'. The major, minor, patch
and build constants are unchanged, as is the 6.5.1 banner.
- studiox.rc: FileVersion and ProductVersion strings are
6.5.1.202602a, FILEVERSION and PRODUCTVERSION are 6,5,1,3.
- guix_installer_release.iss: StudioFullVersion is 6.5.1.202602a and
StudioVersionInfoVersion is 6.5.1.3, so the installer is named
guix_studio_setup_version_6.5.1.202602a.exe.
- Package.appxmanifest: the MSIX identity version is 6.5.1.03.
STUDIOX_VERSION_NUMBER stays at 202602. It stamps the Studio version
into .gxp project files, and a hotfix does not change the project file
format.
Verified against a rebuilt guix_studio.exe, which reports FileVersion
6.5.1.202602a with a numeric file version of 6.5.1.3, matching what
verify_studio_installer.ps1 expects.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Updated port version strings to 6.5.1.202602a
Updated the _gx_version_id string in all 47 gx_port.h files for the
emergency hotfix release, using the same substitution that
prepare_release.sh performs.
This includes guix_studio/ports/gx_port.h, the GUIX Studio Win32 port,
which the release script previously missed because it only searched the
ports directory. That gap is fixed in the preceding commit, so a future
release picks the file up automatically.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…#174)
* Revived the Windows workflows, which had reached no runner since 2025
The four GUIX Studio workflows requested `runs-on: windows-2019`, an
image GitHub retired on 2025-06-30. Nothing can match a retired label,
so every run queued for the full 24 hours and was then cancelled without
ever starting a job:
run 28477318708 created 2026-06-30T21:33:40Z
cancelled 2026-07-01T21:33:41Z
Every run of these workflows since 2025-09-29 has that same
created-plus-24h-cancelled shape. The Studio demo, demo compile, view and
MSIX package tests have gated nothing for about a year, and a manual
workflow_dispatch on 2026-08-25 queued the same way.
Three more defects were in the path behind that one, so fixing the image
alone would not have produced a green run.
- actions/upload-artifact was pinned at v3.1.3 in all four workflows.
GitHub began auto-failing every request that used v3 on 2025-01-30,
and both upload steps run under `if: success() || failure()`, so
every run would have ended red even with the tests passing.
- The scripts hard-coded a Visual Studio edition path. build_guix.cmd,
test_studio_demo.cmd, test_studio_demo_compile.cmd and
test_studio_view.cmd all call VS 2022 *Enterprise*, which no
developer machine has, and build_guix_studio.cmd calls VS 2022
*Community*, which no GitHub runner has. So the demo test's build
step would have failed on the first line. All five now locate the
installation with vswhere, matching what
build_guix_studio_msix_package.cmd already did, and check the result
instead of assuming the call succeeded. Verified on a Community
install, where the Enterprise path does not exist.
- studio_msix_package.yml uploaded
guix_studio/build/vs_2019/msix_package_project/AppPackages. That
directory has not existed since the solution moved to vs_2022, which
is where build_guix_studio_msix_package.cmd builds, so the artifact
was always empty.
Every action reference is now a 40-character commit SHA with the version
in a trailing comment, following the pattern eclipse-threadx/threadx
adopted in August 2026. A tag can be repointed at any commit; a SHA
cannot, which is what makes "which code ran in our CI" answerable from
the repository. Versions moved with the pinning:
actions/checkout v4 -> v7.0.1
actions/upload-artifact v3.1.3 -> v7.0.1
EnricoMi/publish-unit-test-result-action v2 -> v2.24.0
Compatibility was checked against each new action.yml rather than
assumed, for every input these workflows actually pass: checkout keeps
`submodules`; upload-artifact keeps `name` and `path`, and the one
upload per run means the v4 rule against uploading a name twice does not
bite; the publish action keeps `check_name` and `files`, and the
`composite` variant still exists at v2.24.0, which is the one a Windows
runner needs. All three tags were confirmed to resolve to the SHAs
pinned here, and all three are the current latest.
The runner image is pinned rather than tracking windows-latest, on the
same reasoning threadx applies to ubuntu-24.04: which image the build
ran on should be a reviewable commit, not something that changes
underneath it. Floating would have hidden this failure differently
rather than preventing it.
Also removed the dead `C:\Program Files\Python36` PATH lines. No
supported runner image has shipped that Python for years, python is
already on PATH there, and the lines embedded literal quote characters
into PATH.
The trigger branches are deliberately left alone in this commit. These
workflows fire on master only, so they still gate no pull request to dev
-- the same defect threadx fixed in its own suites. That wants a
dispatch run to go green first, on evidence rather than hope, and is a
separate change.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Added the Dependabot configuration the pinned actions need
Follow-up to the pinning in the preceding commit.
A SHA pin with nothing moving it is worse than a floating tag: it holds
CI on whatever was current the day it was written. This repository is the
case study for that. Nothing had ever reported that an action moved, so
actions/upload-artifact sat at v3.1.3 in all four Windows workflows --
four majors behind, and a version GitHub began auto-failing every request
for on 2025-01-30 -- while actions/checkout sat on an unpinned @v4, three
majors behind.
This adds .github/dependabot.yml: weekly, github-actions only, and closes
the reference to that path which the pinning comment in each workflow now
makes. Dependabot understands the SHA form and rewrites the trailing
version comment together with the pin, so the comment cannot drift away
from the SHA it describes.
It follows the configuration eclipse-threadx/threadx added in August
2026, with three differences that are specific to this repository and
documented in the file:
- Only three distinct actions are in use, so the default limit of five
would be enough. Ten is set anyway, to match the sibling repositories
and to leave room for a wave of majors after a long gap.
- There is no .github/CODEOWNERS here, so no reviewer routing exists
for Dependabot to honour. Worth adding, but not by this file.
- The "dependencies" label does not exist in this repository yet.
Dependabot creates it on the first pull request.
Two choices worth stating rather than leaving to be rediscovered.
target-branch is dev. Dependabot reads this file from the default
branch, which is master -- but master is deliberately kept behind dev,
and pull requests belong on dev. So this arms on merge without firing:
nothing happens until a release merge carries the file to master.
Setting target-branch also opts out of Dependabot security updates,
which only ever run against the default branch. For this ecosystem the
cost is small, since an action advisory arrives as an ordinary bump on
the weekly run, but it is a real trade.
Patch and minor are grouped into a single pull request, because a queue
reviewed one item at a time is a queue that gets ignored -- which is the
failure mode this file exists to prevent. Majors stay ungrouped, one
pull request each, because every breaking change met in an action here
has been a major: upload-artifact v4 stopped allowing an artifact name to
be uploaded twice in a run, and v6 requires a runner of 2.327.1 or newer.
Dependabot reports drift, not silence. The Windows workflows rotted
because they never reached a runner, not because nobody was told, and
the runner image fix in the preceding commit is the cure for that half.
The cross-repository reusable workflow in regression_test.yml is
correctly left alone: it references a branch, so it carries no version to
move.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Renamed the Studio IMAGE_INFO struct, which the Windows SDK now defines
With the Windows workflows reaching a runner again, the Studio build
failed immediately on both of them:
guix_studio\system_pngs.h(15,8): error C2011:
'IMAGE_INFO': 'struct' type redefinition
Windows Kits\10\Include\10.0.26100.0\um\Filter.h(340,16): error C2011:
'IMAGE_INFO': 'struct' type redefinition
plus eight C2027 "use of undefined type" errors following from it.
guix_studio declared its own two-field `IMAGE_INFO` in the global
namespace. A newer Windows SDK added a struct of the same name to
um/Filter.h, which arrives through the Windows headers the MFC sources
already include. SDK 10.0.22621.0 does not define it; 10.0.26100.0 does.
This was latent rather than new. studiox.vcxproj sets
`<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>`, the
MSBuild wildcard for "newest installed SDK", so which SDK compiles the
Studio depends entirely on the machine. A developer box with 22621 as its
newest builds; the runner image, which carries 26100, does not. The
collision was simply waiting for whichever came first.
Renamed the Studio type to STUDIO_IMAGE_INFO -- 13 references across five
files. It is private to the Studio, so nothing outside guix_studio sees
the name.
Also dropped the `-winsdk=10.0.22621.0` argument that
build_guix_studio.cmd passed to VsDevCmd. It never had the effect it
looks like it has: the wildcard in the project file governs the SDK the
compile actually uses, so the argument only set the shell environment and
left MSBuild to pick the newest SDK anyway. Keeping it would suggest the
build is pinned to an SDK when it is not. With the collision fixed there
is nothing to pin against.
Verified by rebuilding the Studio against SDK 10.0.22621.0 locally; the
runner will exercise 10.0.26100.0.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Pointed the demo test back at the guix project that exists
The demo compile test could not build the GUIX library at all:
Exception: guix.sln not found.
path=../../../ports/win32/build/vs_2022/guix.vcxproj
522aafc ("Updated scripts for VS Community 2022", February 2025) moved
three paths in test_main.py from vs_2019 to vs_2022. Two of them were
right: guix_studio/build/vs_2019 really had been renamed to vs_2022. The
third was collateral damage from the same search and replace --
ports/win32/build/vs_2019 was never renamed, and still holds the only
guix.vcxproj and guix.sln in the repository.
So --build_guix has raised this exception for eighteen months, before
compiling a single library configuration. Nothing said so, because the
workflow that calls it never reached a runner.
Restored the vs_2019 path. Verified by building the library locally:
guix.vcxproj is Win32-only and still on PlatformToolset v142, and it
produces Debug/gx.lib.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Pointed the view test at the Studio build directory that exists
The Studio view test could not start the Studio at all:
MSBUILD : error MSB1009: Project file does not exist.
Switch: ../../../guix_studio/build/vs_2019/studiox.sln
Exception: Unable to locate Studio executable.
522aafc ("Updated scripts for VS Community 2022", February 2025) renamed
guix_studio/build/vs_2019 to vs_2022 and updated test_demo/test_main.py
for it, but never touched the test_view directory. Three references there
still named the old directory:
- test_view/test_main.py project_sln_path
- test_view/test_utils.py studio_exe_path
- test_view/test_string_import_export.py studio_release
So this suite has been unable to locate the Studio for eighteen months.
Like the guix.vcxproj path in the preceding commit, nothing reported it,
because the workflow that runs it never reached a runner.
The remaining build/vs_2019 references in the tree belong to
ports/win32/build/vs_2019, which is a real directory and is left alone.
This commit makes the suite able to find and launch the Studio. Whether
it then passes on a hosted runner is a separate question -- it drives the
Studio through win32 window handles, and no run has got far enough to
say. Its trigger branches are deliberately not changed here for that
reason.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Ran the Studio demo suites on dev, where the pull requests actually are
The demo and demo compile workflows trigger on master only, for both push
and pull_request. dev is the integration branch, so these suites have
gated no pull request that anybody opened -- the same defect
eclipse-threadx/threadx fixed in its own regression suites in August 2026.
Switched on only after both suites were verified green on a runner, on
this branch, rather than on the assumption that they would be:
GUIX Studio Demo Test 227 of 227 generation tests 147 s
GUIX Studio Demo Compile Test 212 of 212 compile tests 146 s
The other three workflows are deliberately left on their current
triggers, and each for its own reason:
- regression_test.yml does reach a runner, and is red. Its last real
run, 2026-06-30, was 5 failures out of 732: accordion menu,
ml_text_view_32bpp, animation_complete, animation_complete_push_stack
and one more. Adding dev here would make every pull request red on a
suite that has been broken since June. It needs fixing first, and
that is not this change.
- studio_view_test.yml can now find the Studio again, but no run has
got far enough to say whether GUI automation through win32 window
handles survives a hosted runner. Enabling gating on an unknown is
what this commit is written to avoid.
- studio_msix_package.yml is workflow_dispatch only by design. It
packages a release rather than testing a change, so there is nothing
to gate.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ng (#175)
Commit 8a35f69 (#158) made _gx_animation_start() assign the return value of
_gx_canvas_drawing_initiate() to its local "status" variable. That variable is
both the value returned to the caller and the flag that gates linking the
animation into the active list, so the drawing status leaked into the
completion status.
_gx_widget_show() releases the view list of the animation root window on the
line just above that call, so _gx_canvas_drawing_initiate() reports
GX_NO_VIEWS every time an animation canvas is used. The animation was
therefore never linked into _gx_system_animation_list, the frame timer was
never started and _gx_animation_complete() never ran: no animation frames
were produced and GX_ANIMATION_PUSH_STACK never pushed its target onto the
screen stack.
The drawing status is now held in a separate draw_status variable. The #148
guard that keeps _gx_canvas_drawing_complete() from being called after
GX_DRAW_NESTING_EXCEEDED is preserved, while the completion status returned
to the caller is left untouched.
_gx_animation_drag_tracking_start() contains the identical block but returns
GX_SUCCESS unconditionally, so it was not affected. Its variable is renamed to
draw_status as well, with no change in behaviour, so the two functions cannot
drift apart again.
Verified on Linux with the default_build_coverage regression suite. Before the
fix, matching CI run 28477318897 exactly:
447 guix_all_widgets_16bpp_canvas_animation SEGFAULT
457 guix_animation_complete golden_file_frame_id = 1, test_frame_id = 3
458 guix_animation_complete_push_stack Failed, no output
After the fix all three pass and 730 of 732 tests pass. The two remaining
failures, guix_ml_text_view_32bpp and guix_all_widgets_accordion_menu, are
unrelated to animation and are diagnosed separately.
No new regression test is added: guix_animation_complete,
guix_animation_complete_push_stack and guix_all_widgets_16bpp_canvas_animation
already cover this path and are the tests that caught the regression. No
golden file is regenerated, since the change restores the recorded behaviour
rather than altering it. guix_canvas_draw_nesting_overflow_no_output, the test
added by #158, still passes.
No documentation change is required: no public API or behaviour contract
changes.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The fix for issue #148 (#158) made every caller of
_gx_canvas_drawing_initiate() skip its draw when the call returns
GX_DRAW_NESTING_EXCEEDED. Four of those callers push a nested context on the
widget they are already drawing, for one reason only: to narrow the clipping
rectangle to the widget's client area. For them, skipping the draw turns a
correct rendering into no rendering at all, which is what made
guix_all_widgets_accordion_menu report "Frame 12 is different".
The accordion menu screen of the all_widgets demo nests widgets nine levels
deep - multi_level_accordion, menu_list, mla_menu_1_accordion, menu_list,
text_view_3 - and _gx_system_canvas_refresh() consumes two contexts before the
widget tree is walked, so the eight slots of GX_MAX_CONTEXT_NESTING are
exhausted before _gx_multi_line_text_view_text_draw() can push its own. Raising
the limit in a scratch build makes frame 12 match the existing golden file
exactly, which shows that the golden records the correct rendering and that the
text is now being lost rather than merely clipped differently.
When the stack is full there is nothing to push, but the caller's context is
still the right context to draw through: it was created for the same widget and
differs only in its clipping rectangle. These four callers now narrow the
caller's clipping rectangle, draw, and restore it, instead of dropping the draw.
_gx_canvas_drawing_complete() is still not called on overflow, so the stack
corruption that #158 fixed stays fixed.
Applied to _gx_multi_line_text_view_text_draw, _gx_multi_line_text_input_draw,
_gx_rich_text_view_text_draw and _gx_single_line_text_input_draw.
_gx_widget_block_move and _gx_radial_progress_bar_background_draw are left
alone: neither pushes a clip-only context on the widget being drawn.
guix_canvas_draw_nesting_overflow_render_no_output closes the coverage gap that
#158 left. guix_canvas_draw_nesting_overflow_no_output covers detection and
state preservation; the new test covers what a caller must render: that the text
is still drawn at maximum nesting depth, that the borrowed context is handed
back with its nesting count, context pointer and clipping rectangle unchanged,
and that the pixels match those produced when a nested context is available.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
b8bb23b (#159) fixed issue #130 by letting the overflow branch of
_gx_multi_line_text_view_display_info_get() run when an ASCII space is the
character that overflows the available width. That branch consumes the space
and any consecutive spaces without adding them to the row width, which is
correct, and then breaks. When the whitespace ran to the end of the source
line, the line terminator was left behind: the next call started on it, hit
the GX_KEY_LINE_FEED case immediately and returned a row of one byte and zero
width, which draws as a blank line that is not in the text.
The overflow branch now takes the line terminator with the whitespace when
nothing else separates them, handling a bare line feed and a carriage return /
line feed pair, and guarding the second byte on the remaining length -- unlike
the pre-existing GX_KEY_CARRIAGE_RETURN case earlier in the same loop, which
reads ch.gx_string_ptr[1] unchecked. gx_text_display_width is untouched, so the
issue #130 fix stands.
Two such rows appear in the guix_ml_text_view_32bpp fixture, at string offsets
2992 and 23988 of readme_guix_generic.txt: "...Improved internal logic." with
twelve trailing spaces, and "...cursor_pos_calculate.c" with one. Both were
confirmed with a conditional breakpoint on display_number == 1 and
display_width == 0 preceded by a space, which fires exactly twice on the
pre-fix code and never after.
The two extra rows are why 268 of that test's 300 frames and 144 of
guix_bidi_text_draw_32bpp's 429 frames have differed from their golden data
since June. Nothing inside GUIX reads gx_text_display_width -- every caller
uses only gx_text_display_number, to advance its index -- so the row count
alone governs where every row starts, and two extra rows change the
scrollbar's value-to-pixel mapping. Every scroll step then lands at a slightly
different pixel offset and the whole text block is drawn a few pixels up or
down. Of the 300 frames, 297 were a pure vertical shift of otherwise identical
text; only 3 were laid out differently, and those 3 are the two places above.
No golden data is regenerated. gx_multi_line_text_view_text_total_rows for the
long text view goes back from 2226 to 2224, and both tests pass against their
existing golden files.
guix_ml_text_view_word_wrap_no_output did not protect #159's fix: it passed on
the pre-#159 code as well. Its available_width was one pixel too generous --
a_width + space_width, so the over-wide row the old code produced measured
exactly available_width and still satisfied "width <= available_width". It is
now a_width + space_width - 1, which makes appending the space genuinely
overflow, and three cases are added: a line feed terminator, a carriage return
/ line feed terminator, and the total row count that
_gx_multi_line_text_view_string_total_rows_compute() derives from them. That
last one is the quantity the golden frames actually depend on, and the only
one of the three a unit test can pin without golden data.
The test was verified to discriminate in both directions by rebuilding against
each. Against the pre-#159 source it fails four width assertions, which is
issue #130. Against #159 as shipped it fails the line feed row (2 bytes, not
3), the carriage return / line feed row (2, not 4) and the row count (3, not
2). It passes only with both fixes in place.
Verified on Linux across all eighteen build configurations, 1847 tests, no
failures. default_build_coverage 733/733, dynamic_bidi_text_build 3/3
including guix_bidi_text_draw_32bpp, no_utf8_build_coverage 135/135.
One coverage boundary worth naming: the unit test builds against the
all_widgets demo, which is not in NO_UTF8_DEMOS, so it runs in
default_build_coverage and disable_error_check_build but not in the
GX_UTF8_SUPPORT-off configurations. The changed code is common to both paths
-- only the surrounding character advance differs -- and no_utf8_build_coverage
covers it through its 135 golden tests.
No documentation change is required. _gx_multi_line_text_view_display_info_get
is internal, and the GUIX documentation does not describe multi-line text view
wrapping behaviour for trailing whitespace.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
)
_gx_multi_line_text_view_display_info_get() decided whether a line terminator
was "\r" or "\r\n" by examining ch.gx_string_ptr[1], the byte after the current
character, without checking that a byte remained. When the carriage return is
the last byte of the range, that access is outside the caller's buffer.
A GX_STRING carries its own length and need not be NUL terminated -- that is
why the _ext string API exists, and it is what the deprecation warning added in
#167 says about the older char * API. _gx_multi_line_text_view_text_set_ext()
stores the caller's GX_STRING verbatim unless GX_STYLE_TEXT_COPY is set, and
callers of this function pass end_index = gx_string_length, so in the default
configuration the read lands one byte past what the application supplied.
The consequence is not only the read. If that byte happens to be 0x0A, the
function reports a two-byte terminator for a one-byte remainder, so every
caller advances its index by one more than the string holds. In
_gx_multi_line_text_view_string_total_rows_compute() the loop then exits with
index == gx_string_length + 1 and evaluates string.gx_string_ptr[index - 1],
one byte past the end as well, and can add a row that is not in the text. The
same over-advance reaches the line index cache in
gx_multi_line_text_view_line_cache_update.c and the cursor arithmetic in
gx_multi_line_text_input_cursor_pos_update.c.
Both reads now go through string rather than ch. string has already been
advanced past the current character and its length is exactly the number of
bytes still readable there, so guarding on it bounds the access to what the
caller supplied.
The same idiom in _gx_multi_line_text_input_new_line_character_get() is
corrected as well. That one is not currently reachable as a read past the end:
its only caller, _gx_multi_line_text_input_text_set_ext(), writes a NUL
immediately after the copied text. It is fixed anyway because it depends on an
invariant established in a different function and documented nowhere, and
because the byte it reads decides whether the widget inserts a one- or two-byte
terminator on every subsequent Enter.
The rest of the codebase was audited for the same shape. Five other sites
handle a carriage return followed by a possible line feed; all five are already
correct. gx_rich_text_view_line_info_get.c, gx_multi_line_text_input_char_insert.c
and gx_utility_bidi_paragraph_reorder.c guard on the remaining length, the
preprocessed-line-break branch at the top of the changed function guards on it
too, and gx_multi_line_text_button_line_pointers_set.c walks a NUL-terminated
char * buffer where the byte after a carriage return is at worst the
terminator.
guix_ml_text_line_terminator_bounds_no_output covers this. It places a line
feed immediately after the string under test but outside the length the widget
is given, so the over-read is observable without a sanitizer: the unfixed code
counts the out-of-bounds byte and reports a 4-byte row for a 3-byte string,
which the test catches as "Expected: 3, Got: 4". Two controls pin the cases
that must not change -- a "\r\n" pair genuinely inside the string still counts
as two bytes, and a carriage return followed by ordinary text still counts as
one.
The test also asserts the terminator a multi-line text input adopts from its
text, for a lone carriage return and for a "\r\n" pair. Those two cases pass
both before and after the change, for the reason given above; they are there to
pin the behaviour, not to reproduce a fault.
Verified on Linux across all eighteen build configurations, no failures.
No documentation change is required: no public API or behaviour contract
changes, and the corrected behaviour is what the documented terminator handling
already describes.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX Studio Demo

227 tests 227 ✅ 23m 30s ⏱️
1 suites 0 💤
1 files 0 ❌

Results for commit e2d28f5.

♻️ This comment has been updated with latest results.

@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX Studio Demo Compile

213 tests 213 ✅ 18m 59s ⏱️
1 suites 0 💤
1 files 0 ❌

Results for commit e2d28f5.

♻️ This comment has been updated with latest results.

@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX

1 849 tests +1 117 1 849 ✔️ +1 122 31m 11s ⏱️ + 18m 15s
18 suites + 17 0 💤 ± 0 
18 files + 17 0 - 5 

Results for commit e2d28f5. ± Comparison against base commit 826487c.

♻️ This comment has been updated with latest results.

@github-actions

Copy link
Copy Markdown

Code Coverage

PackageLine RateBranch RateHealth
default_build_coverage Package 1100%99%
Summary100% (44377 / 44475)99% (24284 / 24434)

@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX Studio View

40 tests 40 ✅ 1h 21m 55s ⏱️
1 suites 0 💤
1 files 0 ❌

Results for commit e2d28f5.

♻️ This comment has been updated with latest results.

compare_file() locates the line where comparison should start, so that the
banner GUIX Studio writes into every generated file -- which carries a revision
string and a generation timestamp -- is skipped rather than compared. It looked
that line up correctly in the first file and then looked it up in the first file
again for the second:
for line in list_2:
if compare_start_string in line:
start_row_2 = list_1.index(line)
So the golden was sliced at the *generated* file's offset. While both files
happened to reach "#include" on the same line the mistake was invisible, which
is why it survived since the tests were added in #84.
The offsets diverged when 6ba13db added a ten-line MIT licence header to all
111 golden .c and .h files. GUIX Studio does not emit that header, so every
golden now reaches its first #include ten lines later than the file it is
compared against. The golden was therefore sliced ten lines early and compared
banner text against source.
That made every one of the 107 .c and .h comparisons in the suite fail -- all 29
reported test failures -- while the .csv, .xliff and .xml comparisons passed.
Those use skip_line instead of compare_start_string and never reach this code,
which is exactly the split the failure log shows.
The reported mismatch is the same in all 29: a generated "#include" line against
a golden banner comment. Confirmed arithmetically -- in every case the golden
line reported sits exactly ten lines before that golden's own first #include:
generic_16bpp_resources.c reported idx 13, #include at 23, delta 10
generic_16bpp_resources.h reported idx 16, #include at 26, delta 10
generic_16bpp_specifications.c reported idx 14, #include at 24, delta 10
generic_16bpp_specifications.h reported idx 16, #include at 26, delta 10
compare_output_file() is the only caller that passes compare_start_string, so
the change cannot affect the xliff, xml or plain-file comparisons.
Verified by running the suite locally against a Studio built from this tree.
Seven suites that fail in CI now pass with no mismatches at all, each gaining
exactly the tests it had been failing:
CI (broken) local (fixed)
Font 9 / 1 10 / 0
Multi-Themes 18 / 1 19 / 0
Project Import 10 / 2 12 / 0
Trigger Edit 6 / 1 7 / 0
Trigger Target Rename 1 / 1 2 / 0
Bidi Text 3 / 1 4 / 0
Widget Name 3 / 1 4 / 0
Project Import compares a generated specifications file, so this also shows that
the screen flow prototype block added by #173 does not disturb these goldens:
that block is only emitted for projects that use Screen Flow, and these do not.
No golden file is regenerated. The goldens still carry "GUIX Studio Revision
6.1.12.0" and a 2022 timestamp in their banners, and still name Azure RTOS
rather than Eclipse ThreadX, but the banner is what the comparison is supposed
to skip -- so none of that needs to be touched to make the suite correct.
This workflow had never actually executed before 2026-08-27. Every earlier run
was cancelled after queueing 24 hours for the retired windows-2019 image, or
sat awaiting approval on a fork pull request, including the run for the v6.5.1
release itself. #174 gave it a runner again; this is the first result it has
ever produced, and the defect it found is in the test harness rather than in
GUIX or in Studio.
Note that studio_view_test.yml triggers only on pull requests targeting master,
so this change is not exercised by its own pull request into dev. It is
validated by the release pull request that carries dev to master.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@fdesbiens
fdesbiens merged commit 52c1bb4 into masterAug 27, 2026
13 checks passed
Sign up for freeto 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.

1 participant

@fdesbiens
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Merging changes for the v6.5.1.202602a hotfix release - #183

Merged
fdesbiens merged 8 commits into
masterfrom
dev
Aug 27, 2026
Merged

Merging changes for the v6.5.1.202602a hotfix release#183
fdesbiens merged 8 commits into
masterfrom
dev

Conversation

@fdesbiens

Copy link
Copy Markdown
Contributor

No description provided.

fdesbiensand others added 7 commits August 7, 2026 11:58
When GX_DISABLE_DEPRECATED_STRING_API is not defined (i.e., the
pre-5.6 GX_CHAR* API is still enabled), gx_api.h now emits a
#pragma message directing developers to define
GX_DISABLE_DEPRECATED_STRING_API and migrate to the GX_STRING-based
replacement functions.
The deprecated functions omit a string length and cannot safely handle
non-NUL-terminated strings or prevent buffer overruns. All new
applications should use the _ext() replacement variants.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fixed Screen Flow code generation with GX_ANIMATION_POOL_SIZE = 0
GUIX Studio emitted five static helper functions in the generated
specification file as soon as a project used Screen Flow. Two of them,
gx_studio_action_parent_find() and gx_studio_animation_execute(), are
reachable only from the GX_ACTION_TYPE_ANIMATION handler and reference
the GUIX animation API, which is compiled out when the animation pool
is empty:
- gx_system_animation_get() is only defined under
"#if (GX_ANIMATION_POOL_SIZE > 0)" in gx_api.h.
- The body of _gx_animation_start() is guarded the same way in
gx_animation_start.c, so the reference does not resolve at link
time either.
A project that defined GX_ANIMATION_POOL_SIZE as 0 in gx_user.h to save
resources therefore failed to build even when it used no animation
action at all. GCC 14 rejects the implicit declaration outright, and
MSVC fails on the unresolved external.
The screen generator now wraps both helpers and the two
GX_ACTION_TYPE_ANIMATION dispatch sites in
"#if (GX_ANIMATION_POOL_SIZE > 0)". Every other action type keeps
working with an empty pool, and an animation action simply becomes a
no-op at runtime.
The generator also emits forward prototypes for the four static screen
flow helpers, which addresses the secondary request in the issue. Note
that MISRA C:2012 Rule 8.1 covers explicitly specified types and Rule
8.4 applies to objects and functions with external linkage, so static
helpers without a visible prototype were not a deviation. The
prototypes are still worth having and match the declaration the
generator already emits for gx_studio_nested_widget_create().
All the checked-in generated specification files were regenerated so
that they stay in sync with the generator, including the golden files
of the Studio view test.
Added test/guix_studio_test/test_demo/test_animation_pool_size.py as a
regression test, registered in the demo compile CTest project. It
verifies that every checked-in Screen Flow specification file guards
the animation helpers, then compiles them all with
GX_ANIMATION_POOL_SIZE set to 0, promoting the MSVC implicit
declaration warning to an error so the failure mode above is caught.
Fixes#172
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Added hotfix letter support to the release version scripts
The release tooling could not express a hotfix version. Two gaps:
- Get-StudioReleaseVersion rejected anything that was not
major.minor.patch.build, and update_studio_release_version.ps1
hard-coded GUIX_HOTFIX_VERSION as ' '. The documented path to build
a hotfix installer was therefore unusable, and running the script
after a manual edit would have reset the hotfix constant.
- prepare_release.sh looked for gx_port.h under ports only, so it
never updated the version string of the GUIX Studio Win32 port in
guix_studio/ports/gx_port.h.
Get-StudioReleaseVersion now accepts an optional trailing hotfix letter
and exposes it as Hotfix and HotfixDefine. The Win32 and MSIX version
formats are strictly numeric and cannot carry the letter, so the
revision is offset by the position of the letter in the alphabet
instead: 6.5.1.202602 keeps revision 2, and 6.5.1.202602a becomes
revision 3. That keeps the hotfix installer recognizable as the newer
build for Windows upgrade detection and for the Store. Versions without
a hotfix letter resolve exactly as before.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Updated version number constants to 6.5.1.202602a
Prepares the emergency hotfix release that carries the Screen Flow fix
for GX_ANIMATION_POOL_SIZE = 0.
Produced by scripts/update_studio_release_version.ps1 -Version
6.5.1.202602a:
- gx_api.h: GUIX_HOTFIX_VERSION is now 'a'. The major, minor, patch
and build constants are unchanged, as is the 6.5.1 banner.
- studiox.rc: FileVersion and ProductVersion strings are
6.5.1.202602a, FILEVERSION and PRODUCTVERSION are 6,5,1,3.
- guix_installer_release.iss: StudioFullVersion is 6.5.1.202602a and
StudioVersionInfoVersion is 6.5.1.3, so the installer is named
guix_studio_setup_version_6.5.1.202602a.exe.
- Package.appxmanifest: the MSIX identity version is 6.5.1.03.
STUDIOX_VERSION_NUMBER stays at 202602. It stamps the Studio version
into .gxp project files, and a hotfix does not change the project file
format.
Verified against a rebuilt guix_studio.exe, which reports FileVersion
6.5.1.202602a with a numeric file version of 6.5.1.3, matching what
verify_studio_installer.ps1 expects.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Updated port version strings to 6.5.1.202602a
Updated the _gx_version_id string in all 47 gx_port.h files for the
emergency hotfix release, using the same substitution that
prepare_release.sh performs.
This includes guix_studio/ports/gx_port.h, the GUIX Studio Win32 port,
which the release script previously missed because it only searched the
ports directory. That gap is fixed in the preceding commit, so a future
release picks the file up automatically.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…#174)
* Revived the Windows workflows, which had reached no runner since 2025
The four GUIX Studio workflows requested `runs-on: windows-2019`, an
image GitHub retired on 2025-06-30. Nothing can match a retired label,
so every run queued for the full 24 hours and was then cancelled without
ever starting a job:
run 28477318708 created 2026-06-30T21:33:40Z
cancelled 2026-07-01T21:33:41Z
Every run of these workflows since 2025-09-29 has that same
created-plus-24h-cancelled shape. The Studio demo, demo compile, view and
MSIX package tests have gated nothing for about a year, and a manual
workflow_dispatch on 2026-08-25 queued the same way.
Three more defects were in the path behind that one, so fixing the image
alone would not have produced a green run.
- actions/upload-artifact was pinned at v3.1.3 in all four workflows.
GitHub began auto-failing every request that used v3 on 2025-01-30,
and both upload steps run under `if: success() || failure()`, so
every run would have ended red even with the tests passing.
- The scripts hard-coded a Visual Studio edition path. build_guix.cmd,
test_studio_demo.cmd, test_studio_demo_compile.cmd and
test_studio_view.cmd all call VS 2022 *Enterprise*, which no
developer machine has, and build_guix_studio.cmd calls VS 2022
*Community*, which no GitHub runner has. So the demo test's build
step would have failed on the first line. All five now locate the
installation with vswhere, matching what
build_guix_studio_msix_package.cmd already did, and check the result
instead of assuming the call succeeded. Verified on a Community
install, where the Enterprise path does not exist.
- studio_msix_package.yml uploaded
guix_studio/build/vs_2019/msix_package_project/AppPackages. That
directory has not existed since the solution moved to vs_2022, which
is where build_guix_studio_msix_package.cmd builds, so the artifact
was always empty.
Every action reference is now a 40-character commit SHA with the version
in a trailing comment, following the pattern eclipse-threadx/threadx
adopted in August 2026. A tag can be repointed at any commit; a SHA
cannot, which is what makes "which code ran in our CI" answerable from
the repository. Versions moved with the pinning:
actions/checkout v4 -> v7.0.1
actions/upload-artifact v3.1.3 -> v7.0.1
EnricoMi/publish-unit-test-result-action v2 -> v2.24.0
Compatibility was checked against each new action.yml rather than
assumed, for every input these workflows actually pass: checkout keeps
`submodules`; upload-artifact keeps `name` and `path`, and the one
upload per run means the v4 rule against uploading a name twice does not
bite; the publish action keeps `check_name` and `files`, and the
`composite` variant still exists at v2.24.0, which is the one a Windows
runner needs. All three tags were confirmed to resolve to the SHAs
pinned here, and all three are the current latest.
The runner image is pinned rather than tracking windows-latest, on the
same reasoning threadx applies to ubuntu-24.04: which image the build
ran on should be a reviewable commit, not something that changes
underneath it. Floating would have hidden this failure differently
rather than preventing it.
Also removed the dead `C:\Program Files\Python36` PATH lines. No
supported runner image has shipped that Python for years, python is
already on PATH there, and the lines embedded literal quote characters
into PATH.
The trigger branches are deliberately left alone in this commit. These
workflows fire on master only, so they still gate no pull request to dev
-- the same defect threadx fixed in its own suites. That wants a
dispatch run to go green first, on evidence rather than hope, and is a
separate change.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Added the Dependabot configuration the pinned actions need
Follow-up to the pinning in the preceding commit.
A SHA pin with nothing moving it is worse than a floating tag: it holds
CI on whatever was current the day it was written. This repository is the
case study for that. Nothing had ever reported that an action moved, so
actions/upload-artifact sat at v3.1.3 in all four Windows workflows --
four majors behind, and a version GitHub began auto-failing every request
for on 2025-01-30 -- while actions/checkout sat on an unpinned @v4, three
majors behind.
This adds .github/dependabot.yml: weekly, github-actions only, and closes
the reference to that path which the pinning comment in each workflow now
makes. Dependabot understands the SHA form and rewrites the trailing
version comment together with the pin, so the comment cannot drift away
from the SHA it describes.
It follows the configuration eclipse-threadx/threadx added in August
2026, with three differences that are specific to this repository and
documented in the file:
- Only three distinct actions are in use, so the default limit of five
would be enough. Ten is set anyway, to match the sibling repositories
and to leave room for a wave of majors after a long gap.
- There is no .github/CODEOWNERS here, so no reviewer routing exists
for Dependabot to honour. Worth adding, but not by this file.
- The "dependencies" label does not exist in this repository yet.
Dependabot creates it on the first pull request.
Two choices worth stating rather than leaving to be rediscovered.
target-branch is dev. Dependabot reads this file from the default
branch, which is master -- but master is deliberately kept behind dev,
and pull requests belong on dev. So this arms on merge without firing:
nothing happens until a release merge carries the file to master.
Setting target-branch also opts out of Dependabot security updates,
which only ever run against the default branch. For this ecosystem the
cost is small, since an action advisory arrives as an ordinary bump on
the weekly run, but it is a real trade.
Patch and minor are grouped into a single pull request, because a queue
reviewed one item at a time is a queue that gets ignored -- which is the
failure mode this file exists to prevent. Majors stay ungrouped, one
pull request each, because every breaking change met in an action here
has been a major: upload-artifact v4 stopped allowing an artifact name to
be uploaded twice in a run, and v6 requires a runner of 2.327.1 or newer.
Dependabot reports drift, not silence. The Windows workflows rotted
because they never reached a runner, not because nobody was told, and
the runner image fix in the preceding commit is the cure for that half.
The cross-repository reusable workflow in regression_test.yml is
correctly left alone: it references a branch, so it carries no version to
move.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Renamed the Studio IMAGE_INFO struct, which the Windows SDK now defines
With the Windows workflows reaching a runner again, the Studio build
failed immediately on both of them:
guix_studio\system_pngs.h(15,8): error C2011:
'IMAGE_INFO': 'struct' type redefinition
Windows Kits\10\Include\10.0.26100.0\um\Filter.h(340,16): error C2011:
'IMAGE_INFO': 'struct' type redefinition
plus eight C2027 "use of undefined type" errors following from it.
guix_studio declared its own two-field `IMAGE_INFO` in the global
namespace. A newer Windows SDK added a struct of the same name to
um/Filter.h, which arrives through the Windows headers the MFC sources
already include. SDK 10.0.22621.0 does not define it; 10.0.26100.0 does.
This was latent rather than new. studiox.vcxproj sets
`<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>`, the
MSBuild wildcard for "newest installed SDK", so which SDK compiles the
Studio depends entirely on the machine. A developer box with 22621 as its
newest builds; the runner image, which carries 26100, does not. The
collision was simply waiting for whichever came first.
Renamed the Studio type to STUDIO_IMAGE_INFO -- 13 references across five
files. It is private to the Studio, so nothing outside guix_studio sees
the name.
Also dropped the `-winsdk=10.0.22621.0` argument that
build_guix_studio.cmd passed to VsDevCmd. It never had the effect it
looks like it has: the wildcard in the project file governs the SDK the
compile actually uses, so the argument only set the shell environment and
left MSBuild to pick the newest SDK anyway. Keeping it would suggest the
build is pinned to an SDK when it is not. With the collision fixed there
is nothing to pin against.
Verified by rebuilding the Studio against SDK 10.0.22621.0 locally; the
runner will exercise 10.0.26100.0.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Pointed the demo test back at the guix project that exists
The demo compile test could not build the GUIX library at all:
Exception: guix.sln not found.
path=../../../ports/win32/build/vs_2022/guix.vcxproj
522aafc ("Updated scripts for VS Community 2022", February 2025) moved
three paths in test_main.py from vs_2019 to vs_2022. Two of them were
right: guix_studio/build/vs_2019 really had been renamed to vs_2022. The
third was collateral damage from the same search and replace --
ports/win32/build/vs_2019 was never renamed, and still holds the only
guix.vcxproj and guix.sln in the repository.
So --build_guix has raised this exception for eighteen months, before
compiling a single library configuration. Nothing said so, because the
workflow that calls it never reached a runner.
Restored the vs_2019 path. Verified by building the library locally:
guix.vcxproj is Win32-only and still on PlatformToolset v142, and it
produces Debug/gx.lib.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Pointed the view test at the Studio build directory that exists
The Studio view test could not start the Studio at all:
MSBUILD : error MSB1009: Project file does not exist.
Switch: ../../../guix_studio/build/vs_2019/studiox.sln
Exception: Unable to locate Studio executable.
522aafc ("Updated scripts for VS Community 2022", February 2025) renamed
guix_studio/build/vs_2019 to vs_2022 and updated test_demo/test_main.py
for it, but never touched the test_view directory. Three references there
still named the old directory:
- test_view/test_main.py project_sln_path
- test_view/test_utils.py studio_exe_path
- test_view/test_string_import_export.py studio_release
So this suite has been unable to locate the Studio for eighteen months.
Like the guix.vcxproj path in the preceding commit, nothing reported it,
because the workflow that runs it never reached a runner.
The remaining build/vs_2019 references in the tree belong to
ports/win32/build/vs_2019, which is a real directory and is left alone.
This commit makes the suite able to find and launch the Studio. Whether
it then passes on a hosted runner is a separate question -- it drives the
Studio through win32 window handles, and no run has got far enough to
say. Its trigger branches are deliberately not changed here for that
reason.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Ran the Studio demo suites on dev, where the pull requests actually are
The demo and demo compile workflows trigger on master only, for both push
and pull_request. dev is the integration branch, so these suites have
gated no pull request that anybody opened -- the same defect
eclipse-threadx/threadx fixed in its own regression suites in August 2026.
Switched on only after both suites were verified green on a runner, on
this branch, rather than on the assumption that they would be:
GUIX Studio Demo Test 227 of 227 generation tests 147 s
GUIX Studio Demo Compile Test 212 of 212 compile tests 146 s
The other three workflows are deliberately left on their current
triggers, and each for its own reason:
- regression_test.yml does reach a runner, and is red. Its last real
run, 2026-06-30, was 5 failures out of 732: accordion menu,
ml_text_view_32bpp, animation_complete, animation_complete_push_stack
and one more. Adding dev here would make every pull request red on a
suite that has been broken since June. It needs fixing first, and
that is not this change.
- studio_view_test.yml can now find the Studio again, but no run has
got far enough to say whether GUI automation through win32 window
handles survives a hosted runner. Enabling gating on an unknown is
what this commit is written to avoid.
- studio_msix_package.yml is workflow_dispatch only by design. It
packages a release rather than testing a change, so there is nothing
to gate.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ng (#175)
Commit 8a35f69 (#158) made _gx_animation_start() assign the return value of
_gx_canvas_drawing_initiate() to its local "status" variable. That variable is
both the value returned to the caller and the flag that gates linking the
animation into the active list, so the drawing status leaked into the
completion status.
_gx_widget_show() releases the view list of the animation root window on the
line just above that call, so _gx_canvas_drawing_initiate() reports
GX_NO_VIEWS every time an animation canvas is used. The animation was
therefore never linked into _gx_system_animation_list, the frame timer was
never started and _gx_animation_complete() never ran: no animation frames
were produced and GX_ANIMATION_PUSH_STACK never pushed its target onto the
screen stack.
The drawing status is now held in a separate draw_status variable. The #148
guard that keeps _gx_canvas_drawing_complete() from being called after
GX_DRAW_NESTING_EXCEEDED is preserved, while the completion status returned
to the caller is left untouched.
_gx_animation_drag_tracking_start() contains the identical block but returns
GX_SUCCESS unconditionally, so it was not affected. Its variable is renamed to
draw_status as well, with no change in behaviour, so the two functions cannot
drift apart again.
Verified on Linux with the default_build_coverage regression suite. Before the
fix, matching CI run 28477318897 exactly:
447 guix_all_widgets_16bpp_canvas_animation SEGFAULT
457 guix_animation_complete golden_file_frame_id = 1, test_frame_id = 3
458 guix_animation_complete_push_stack Failed, no output
After the fix all three pass and 730 of 732 tests pass. The two remaining
failures, guix_ml_text_view_32bpp and guix_all_widgets_accordion_menu, are
unrelated to animation and are diagnosed separately.
No new regression test is added: guix_animation_complete,
guix_animation_complete_push_stack and guix_all_widgets_16bpp_canvas_animation
already cover this path and are the tests that caught the regression. No
golden file is regenerated, since the change restores the recorded behaviour
rather than altering it. guix_canvas_draw_nesting_overflow_no_output, the test
added by #158, still passes.
No documentation change is required: no public API or behaviour contract
changes.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The fix for issue #148 (#158) made every caller of
_gx_canvas_drawing_initiate() skip its draw when the call returns
GX_DRAW_NESTING_EXCEEDED. Four of those callers push a nested context on the
widget they are already drawing, for one reason only: to narrow the clipping
rectangle to the widget's client area. For them, skipping the draw turns a
correct rendering into no rendering at all, which is what made
guix_all_widgets_accordion_menu report "Frame 12 is different".
The accordion menu screen of the all_widgets demo nests widgets nine levels
deep - multi_level_accordion, menu_list, mla_menu_1_accordion, menu_list,
text_view_3 - and _gx_system_canvas_refresh() consumes two contexts before the
widget tree is walked, so the eight slots of GX_MAX_CONTEXT_NESTING are
exhausted before _gx_multi_line_text_view_text_draw() can push its own. Raising
the limit in a scratch build makes frame 12 match the existing golden file
exactly, which shows that the golden records the correct rendering and that the
text is now being lost rather than merely clipped differently.
When the stack is full there is nothing to push, but the caller's context is
still the right context to draw through: it was created for the same widget and
differs only in its clipping rectangle. These four callers now narrow the
caller's clipping rectangle, draw, and restore it, instead of dropping the draw.
_gx_canvas_drawing_complete() is still not called on overflow, so the stack
corruption that #158 fixed stays fixed.
Applied to _gx_multi_line_text_view_text_draw, _gx_multi_line_text_input_draw,
_gx_rich_text_view_text_draw and _gx_single_line_text_input_draw.
_gx_widget_block_move and _gx_radial_progress_bar_background_draw are left
alone: neither pushes a clip-only context on the widget being drawn.
guix_canvas_draw_nesting_overflow_render_no_output closes the coverage gap that
#158 left. guix_canvas_draw_nesting_overflow_no_output covers detection and
state preservation; the new test covers what a caller must render: that the text
is still drawn at maximum nesting depth, that the borrowed context is handed
back with its nesting count, context pointer and clipping rectangle unchanged,
and that the pixels match those produced when a nested context is available.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
b8bb23b (#159) fixed issue #130 by letting the overflow branch of
_gx_multi_line_text_view_display_info_get() run when an ASCII space is the
character that overflows the available width. That branch consumes the space
and any consecutive spaces without adding them to the row width, which is
correct, and then breaks. When the whitespace ran to the end of the source
line, the line terminator was left behind: the next call started on it, hit
the GX_KEY_LINE_FEED case immediately and returned a row of one byte and zero
width, which draws as a blank line that is not in the text.
The overflow branch now takes the line terminator with the whitespace when
nothing else separates them, handling a bare line feed and a carriage return /
line feed pair, and guarding the second byte on the remaining length -- unlike
the pre-existing GX_KEY_CARRIAGE_RETURN case earlier in the same loop, which
reads ch.gx_string_ptr[1] unchecked. gx_text_display_width is untouched, so the
issue #130 fix stands.
Two such rows appear in the guix_ml_text_view_32bpp fixture, at string offsets
2992 and 23988 of readme_guix_generic.txt: "...Improved internal logic." with
twelve trailing spaces, and "...cursor_pos_calculate.c" with one. Both were
confirmed with a conditional breakpoint on display_number == 1 and
display_width == 0 preceded by a space, which fires exactly twice on the
pre-fix code and never after.
The two extra rows are why 268 of that test's 300 frames and 144 of
guix_bidi_text_draw_32bpp's 429 frames have differed from their golden data
since June. Nothing inside GUIX reads gx_text_display_width -- every caller
uses only gx_text_display_number, to advance its index -- so the row count
alone governs where every row starts, and two extra rows change the
scrollbar's value-to-pixel mapping. Every scroll step then lands at a slightly
different pixel offset and the whole text block is drawn a few pixels up or
down. Of the 300 frames, 297 were a pure vertical shift of otherwise identical
text; only 3 were laid out differently, and those 3 are the two places above.
No golden data is regenerated. gx_multi_line_text_view_text_total_rows for the
long text view goes back from 2226 to 2224, and both tests pass against their
existing golden files.
guix_ml_text_view_word_wrap_no_output did not protect #159's fix: it passed on
the pre-#159 code as well. Its available_width was one pixel too generous --
a_width + space_width, so the over-wide row the old code produced measured
exactly available_width and still satisfied "width <= available_width". It is
now a_width + space_width - 1, which makes appending the space genuinely
overflow, and three cases are added: a line feed terminator, a carriage return
/ line feed terminator, and the total row count that
_gx_multi_line_text_view_string_total_rows_compute() derives from them. That
last one is the quantity the golden frames actually depend on, and the only
one of the three a unit test can pin without golden data.
The test was verified to discriminate in both directions by rebuilding against
each. Against the pre-#159 source it fails four width assertions, which is
issue #130. Against #159 as shipped it fails the line feed row (2 bytes, not
3), the carriage return / line feed row (2, not 4) and the row count (3, not
2). It passes only with both fixes in place.
Verified on Linux across all eighteen build configurations, 1847 tests, no
failures. default_build_coverage 733/733, dynamic_bidi_text_build 3/3
including guix_bidi_text_draw_32bpp, no_utf8_build_coverage 135/135.
One coverage boundary worth naming: the unit test builds against the
all_widgets demo, which is not in NO_UTF8_DEMOS, so it runs in
default_build_coverage and disable_error_check_build but not in the
GX_UTF8_SUPPORT-off configurations. The changed code is common to both paths
-- only the surrounding character advance differs -- and no_utf8_build_coverage
covers it through its 135 golden tests.
No documentation change is required. _gx_multi_line_text_view_display_info_get
is internal, and the GUIX documentation does not describe multi-line text view
wrapping behaviour for trailing whitespace.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
)
_gx_multi_line_text_view_display_info_get() decided whether a line terminator
was "\r" or "\r\n" by examining ch.gx_string_ptr[1], the byte after the current
character, without checking that a byte remained. When the carriage return is
the last byte of the range, that access is outside the caller's buffer.
A GX_STRING carries its own length and need not be NUL terminated -- that is
why the _ext string API exists, and it is what the deprecation warning added in
#167 says about the older char * API. _gx_multi_line_text_view_text_set_ext()
stores the caller's GX_STRING verbatim unless GX_STYLE_TEXT_COPY is set, and
callers of this function pass end_index = gx_string_length, so in the default
configuration the read lands one byte past what the application supplied.
The consequence is not only the read. If that byte happens to be 0x0A, the
function reports a two-byte terminator for a one-byte remainder, so every
caller advances its index by one more than the string holds. In
_gx_multi_line_text_view_string_total_rows_compute() the loop then exits with
index == gx_string_length + 1 and evaluates string.gx_string_ptr[index - 1],
one byte past the end as well, and can add a row that is not in the text. The
same over-advance reaches the line index cache in
gx_multi_line_text_view_line_cache_update.c and the cursor arithmetic in
gx_multi_line_text_input_cursor_pos_update.c.
Both reads now go through string rather than ch. string has already been
advanced past the current character and its length is exactly the number of
bytes still readable there, so guarding on it bounds the access to what the
caller supplied.
The same idiom in _gx_multi_line_text_input_new_line_character_get() is
corrected as well. That one is not currently reachable as a read past the end:
its only caller, _gx_multi_line_text_input_text_set_ext(), writes a NUL
immediately after the copied text. It is fixed anyway because it depends on an
invariant established in a different function and documented nowhere, and
because the byte it reads decides whether the widget inserts a one- or two-byte
terminator on every subsequent Enter.
The rest of the codebase was audited for the same shape. Five other sites
handle a carriage return followed by a possible line feed; all five are already
correct. gx_rich_text_view_line_info_get.c, gx_multi_line_text_input_char_insert.c
and gx_utility_bidi_paragraph_reorder.c guard on the remaining length, the
preprocessed-line-break branch at the top of the changed function guards on it
too, and gx_multi_line_text_button_line_pointers_set.c walks a NUL-terminated
char * buffer where the byte after a carriage return is at worst the
terminator.
guix_ml_text_line_terminator_bounds_no_output covers this. It places a line
feed immediately after the string under test but outside the length the widget
is given, so the over-read is observable without a sanitizer: the unfixed code
counts the out-of-bounds byte and reports a 4-byte row for a 3-byte string,
which the test catches as "Expected: 3, Got: 4". Two controls pin the cases
that must not change -- a "\r\n" pair genuinely inside the string still counts
as two bytes, and a carriage return followed by ordinary text still counts as
one.
The test also asserts the terminator a multi-line text input adopts from its
text, for a lone carriage return and for a "\r\n" pair. Those two cases pass
both before and after the change, for the reason given above; they are there to
pin the behaviour, not to reproduce a fault.
Verified on Linux across all eighteen build configurations, no failures.
No documentation change is required: no public API or behaviour contract
changes, and the corrected behaviour is what the documented terminator handling
already describes.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX Studio Demo

227 tests 227 ✅ 23m 30s ⏱️
1 suites 0 💤
1 files 0 ❌

Results for commit e2d28f5.

♻️ This comment has been updated with latest results.

@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX Studio Demo Compile

213 tests 213 ✅ 18m 59s ⏱️
1 suites 0 💤
1 files 0 ❌

Results for commit e2d28f5.

♻️ This comment has been updated with latest results.

@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX

1 849 tests +1 117 1 849 ✔️ +1 122 31m 11s ⏱️ + 18m 15s
18 suites + 17 0 💤 ± 0 
18 files + 17 0 - 5 

Results for commit e2d28f5. ± Comparison against base commit 826487c.

♻️ This comment has been updated with latest results.

@github-actions

Copy link
Copy Markdown

Code Coverage

PackageLine RateBranch RateHealth
default_build_coverage Package 1100%99%
Summary100% (44377 / 44475)99% (24284 / 24434)

@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX Studio View

40 tests 40 ✅ 1h 21m 55s ⏱️
1 suites 0 💤
1 files 0 ❌

Results for commit e2d28f5.

♻️ This comment has been updated with latest results.

compare_file() locates the line where comparison should start, so that the
banner GUIX Studio writes into every generated file -- which carries a revision
string and a generation timestamp -- is skipped rather than compared. It looked
that line up correctly in the first file and then looked it up in the first file
again for the second:
for line in list_2:
if compare_start_string in line:
start_row_2 = list_1.index(line)
So the golden was sliced at the *generated* file's offset. While both files
happened to reach "#include" on the same line the mistake was invisible, which
is why it survived since the tests were added in #84.
The offsets diverged when 6ba13db added a ten-line MIT licence header to all
111 golden .c and .h files. GUIX Studio does not emit that header, so every
golden now reaches its first #include ten lines later than the file it is
compared against. The golden was therefore sliced ten lines early and compared
banner text against source.
That made every one of the 107 .c and .h comparisons in the suite fail -- all 29
reported test failures -- while the .csv, .xliff and .xml comparisons passed.
Those use skip_line instead of compare_start_string and never reach this code,
which is exactly the split the failure log shows.
The reported mismatch is the same in all 29: a generated "#include" line against
a golden banner comment. Confirmed arithmetically -- in every case the golden
line reported sits exactly ten lines before that golden's own first #include:
generic_16bpp_resources.c reported idx 13, #include at 23, delta 10
generic_16bpp_resources.h reported idx 16, #include at 26, delta 10
generic_16bpp_specifications.c reported idx 14, #include at 24, delta 10
generic_16bpp_specifications.h reported idx 16, #include at 26, delta 10
compare_output_file() is the only caller that passes compare_start_string, so
the change cannot affect the xliff, xml or plain-file comparisons.
Verified by running the suite locally against a Studio built from this tree.
Seven suites that fail in CI now pass with no mismatches at all, each gaining
exactly the tests it had been failing:
CI (broken) local (fixed)
Font 9 / 1 10 / 0
Multi-Themes 18 / 1 19 / 0
Project Import 10 / 2 12 / 0
Trigger Edit 6 / 1 7 / 0
Trigger Target Rename 1 / 1 2 / 0
Bidi Text 3 / 1 4 / 0
Widget Name 3 / 1 4 / 0
Project Import compares a generated specifications file, so this also shows that
the screen flow prototype block added by #173 does not disturb these goldens:
that block is only emitted for projects that use Screen Flow, and these do not.
No golden file is regenerated. The goldens still carry "GUIX Studio Revision
6.1.12.0" and a 2022 timestamp in their banners, and still name Azure RTOS
rather than Eclipse ThreadX, but the banner is what the comparison is supposed
to skip -- so none of that needs to be touched to make the suite correct.
This workflow had never actually executed before 2026-08-27. Every earlier run
was cancelled after queueing 24 hours for the retired windows-2019 image, or
sat awaiting approval on a fork pull request, including the run for the v6.5.1
release itself. #174 gave it a runner again; this is the first result it has
ever produced, and the defect it found is in the test harness rather than in
GUIX or in Studio.
Note that studio_view_test.yml triggers only on pull requests targeting master,
so this change is not exercised by its own pull request into dev. It is
validated by the release pull request that carries dev to master.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@fdesbiens
fdesbiens merged commit 52c1bb4 into masterAug 27, 2026
13 checks passed
Sign up for freeto 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.

1 participant

@fdesbiens
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Merging changes for the v6.5.1.202602a hotfix release - #183

Merged
fdesbiens merged 8 commits into
masterfrom
dev
Aug 27, 2026
Merged

Merging changes for the v6.5.1.202602a hotfix release#183
fdesbiens merged 8 commits into
masterfrom
dev

Conversation

@fdesbiens

Copy link
Copy Markdown
Contributor

No description provided.

fdesbiensand others added 7 commits August 7, 2026 11:58
When GX_DISABLE_DEPRECATED_STRING_API is not defined (i.e., the
pre-5.6 GX_CHAR* API is still enabled), gx_api.h now emits a
#pragma message directing developers to define
GX_DISABLE_DEPRECATED_STRING_API and migrate to the GX_STRING-based
replacement functions.
The deprecated functions omit a string length and cannot safely handle
non-NUL-terminated strings or prevent buffer overruns. All new
applications should use the _ext() replacement variants.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fixed Screen Flow code generation with GX_ANIMATION_POOL_SIZE = 0
GUIX Studio emitted five static helper functions in the generated
specification file as soon as a project used Screen Flow. Two of them,
gx_studio_action_parent_find() and gx_studio_animation_execute(), are
reachable only from the GX_ACTION_TYPE_ANIMATION handler and reference
the GUIX animation API, which is compiled out when the animation pool
is empty:
- gx_system_animation_get() is only defined under
"#if (GX_ANIMATION_POOL_SIZE > 0)" in gx_api.h.
- The body of _gx_animation_start() is guarded the same way in
gx_animation_start.c, so the reference does not resolve at link
time either.
A project that defined GX_ANIMATION_POOL_SIZE as 0 in gx_user.h to save
resources therefore failed to build even when it used no animation
action at all. GCC 14 rejects the implicit declaration outright, and
MSVC fails on the unresolved external.
The screen generator now wraps both helpers and the two
GX_ACTION_TYPE_ANIMATION dispatch sites in
"#if (GX_ANIMATION_POOL_SIZE > 0)". Every other action type keeps
working with an empty pool, and an animation action simply becomes a
no-op at runtime.
The generator also emits forward prototypes for the four static screen
flow helpers, which addresses the secondary request in the issue. Note
that MISRA C:2012 Rule 8.1 covers explicitly specified types and Rule
8.4 applies to objects and functions with external linkage, so static
helpers without a visible prototype were not a deviation. The
prototypes are still worth having and match the declaration the
generator already emits for gx_studio_nested_widget_create().
All the checked-in generated specification files were regenerated so
that they stay in sync with the generator, including the golden files
of the Studio view test.
Added test/guix_studio_test/test_demo/test_animation_pool_size.py as a
regression test, registered in the demo compile CTest project. It
verifies that every checked-in Screen Flow specification file guards
the animation helpers, then compiles them all with
GX_ANIMATION_POOL_SIZE set to 0, promoting the MSVC implicit
declaration warning to an error so the failure mode above is caught.
Fixes#172
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Added hotfix letter support to the release version scripts
The release tooling could not express a hotfix version. Two gaps:
- Get-StudioReleaseVersion rejected anything that was not
major.minor.patch.build, and update_studio_release_version.ps1
hard-coded GUIX_HOTFIX_VERSION as ' '. The documented path to build
a hotfix installer was therefore unusable, and running the script
after a manual edit would have reset the hotfix constant.
- prepare_release.sh looked for gx_port.h under ports only, so it
never updated the version string of the GUIX Studio Win32 port in
guix_studio/ports/gx_port.h.
Get-StudioReleaseVersion now accepts an optional trailing hotfix letter
and exposes it as Hotfix and HotfixDefine. The Win32 and MSIX version
formats are strictly numeric and cannot carry the letter, so the
revision is offset by the position of the letter in the alphabet
instead: 6.5.1.202602 keeps revision 2, and 6.5.1.202602a becomes
revision 3. That keeps the hotfix installer recognizable as the newer
build for Windows upgrade detection and for the Store. Versions without
a hotfix letter resolve exactly as before.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Updated version number constants to 6.5.1.202602a
Prepares the emergency hotfix release that carries the Screen Flow fix
for GX_ANIMATION_POOL_SIZE = 0.
Produced by scripts/update_studio_release_version.ps1 -Version
6.5.1.202602a:
- gx_api.h: GUIX_HOTFIX_VERSION is now 'a'. The major, minor, patch
and build constants are unchanged, as is the 6.5.1 banner.
- studiox.rc: FileVersion and ProductVersion strings are
6.5.1.202602a, FILEVERSION and PRODUCTVERSION are 6,5,1,3.
- guix_installer_release.iss: StudioFullVersion is 6.5.1.202602a and
StudioVersionInfoVersion is 6.5.1.3, so the installer is named
guix_studio_setup_version_6.5.1.202602a.exe.
- Package.appxmanifest: the MSIX identity version is 6.5.1.03.
STUDIOX_VERSION_NUMBER stays at 202602. It stamps the Studio version
into .gxp project files, and a hotfix does not change the project file
format.
Verified against a rebuilt guix_studio.exe, which reports FileVersion
6.5.1.202602a with a numeric file version of 6.5.1.3, matching what
verify_studio_installer.ps1 expects.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Updated port version strings to 6.5.1.202602a
Updated the _gx_version_id string in all 47 gx_port.h files for the
emergency hotfix release, using the same substitution that
prepare_release.sh performs.
This includes guix_studio/ports/gx_port.h, the GUIX Studio Win32 port,
which the release script previously missed because it only searched the
ports directory. That gap is fixed in the preceding commit, so a future
release picks the file up automatically.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…#174)
* Revived the Windows workflows, which had reached no runner since 2025
The four GUIX Studio workflows requested `runs-on: windows-2019`, an
image GitHub retired on 2025-06-30. Nothing can match a retired label,
so every run queued for the full 24 hours and was then cancelled without
ever starting a job:
run 28477318708 created 2026-06-30T21:33:40Z
cancelled 2026-07-01T21:33:41Z
Every run of these workflows since 2025-09-29 has that same
created-plus-24h-cancelled shape. The Studio demo, demo compile, view and
MSIX package tests have gated nothing for about a year, and a manual
workflow_dispatch on 2026-08-25 queued the same way.
Three more defects were in the path behind that one, so fixing the image
alone would not have produced a green run.
- actions/upload-artifact was pinned at v3.1.3 in all four workflows.
GitHub began auto-failing every request that used v3 on 2025-01-30,
and both upload steps run under `if: success() || failure()`, so
every run would have ended red even with the tests passing.
- The scripts hard-coded a Visual Studio edition path. build_guix.cmd,
test_studio_demo.cmd, test_studio_demo_compile.cmd and
test_studio_view.cmd all call VS 2022 *Enterprise*, which no
developer machine has, and build_guix_studio.cmd calls VS 2022
*Community*, which no GitHub runner has. So the demo test's build
step would have failed on the first line. All five now locate the
installation with vswhere, matching what
build_guix_studio_msix_package.cmd already did, and check the result
instead of assuming the call succeeded. Verified on a Community
install, where the Enterprise path does not exist.
- studio_msix_package.yml uploaded
guix_studio/build/vs_2019/msix_package_project/AppPackages. That
directory has not existed since the solution moved to vs_2022, which
is where build_guix_studio_msix_package.cmd builds, so the artifact
was always empty.
Every action reference is now a 40-character commit SHA with the version
in a trailing comment, following the pattern eclipse-threadx/threadx
adopted in August 2026. A tag can be repointed at any commit; a SHA
cannot, which is what makes "which code ran in our CI" answerable from
the repository. Versions moved with the pinning:
actions/checkout v4 -> v7.0.1
actions/upload-artifact v3.1.3 -> v7.0.1
EnricoMi/publish-unit-test-result-action v2 -> v2.24.0
Compatibility was checked against each new action.yml rather than
assumed, for every input these workflows actually pass: checkout keeps
`submodules`; upload-artifact keeps `name` and `path`, and the one
upload per run means the v4 rule against uploading a name twice does not
bite; the publish action keeps `check_name` and `files`, and the
`composite` variant still exists at v2.24.0, which is the one a Windows
runner needs. All three tags were confirmed to resolve to the SHAs
pinned here, and all three are the current latest.
The runner image is pinned rather than tracking windows-latest, on the
same reasoning threadx applies to ubuntu-24.04: which image the build
ran on should be a reviewable commit, not something that changes
underneath it. Floating would have hidden this failure differently
rather than preventing it.
Also removed the dead `C:\Program Files\Python36` PATH lines. No
supported runner image has shipped that Python for years, python is
already on PATH there, and the lines embedded literal quote characters
into PATH.
The trigger branches are deliberately left alone in this commit. These
workflows fire on master only, so they still gate no pull request to dev
-- the same defect threadx fixed in its own suites. That wants a
dispatch run to go green first, on evidence rather than hope, and is a
separate change.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Added the Dependabot configuration the pinned actions need
Follow-up to the pinning in the preceding commit.
A SHA pin with nothing moving it is worse than a floating tag: it holds
CI on whatever was current the day it was written. This repository is the
case study for that. Nothing had ever reported that an action moved, so
actions/upload-artifact sat at v3.1.3 in all four Windows workflows --
four majors behind, and a version GitHub began auto-failing every request
for on 2025-01-30 -- while actions/checkout sat on an unpinned @v4, three
majors behind.
This adds .github/dependabot.yml: weekly, github-actions only, and closes
the reference to that path which the pinning comment in each workflow now
makes. Dependabot understands the SHA form and rewrites the trailing
version comment together with the pin, so the comment cannot drift away
from the SHA it describes.
It follows the configuration eclipse-threadx/threadx added in August
2026, with three differences that are specific to this repository and
documented in the file:
- Only three distinct actions are in use, so the default limit of five
would be enough. Ten is set anyway, to match the sibling repositories
and to leave room for a wave of majors after a long gap.
- There is no .github/CODEOWNERS here, so no reviewer routing exists
for Dependabot to honour. Worth adding, but not by this file.
- The "dependencies" label does not exist in this repository yet.
Dependabot creates it on the first pull request.
Two choices worth stating rather than leaving to be rediscovered.
target-branch is dev. Dependabot reads this file from the default
branch, which is master -- but master is deliberately kept behind dev,
and pull requests belong on dev. So this arms on merge without firing:
nothing happens until a release merge carries the file to master.
Setting target-branch also opts out of Dependabot security updates,
which only ever run against the default branch. For this ecosystem the
cost is small, since an action advisory arrives as an ordinary bump on
the weekly run, but it is a real trade.
Patch and minor are grouped into a single pull request, because a queue
reviewed one item at a time is a queue that gets ignored -- which is the
failure mode this file exists to prevent. Majors stay ungrouped, one
pull request each, because every breaking change met in an action here
has been a major: upload-artifact v4 stopped allowing an artifact name to
be uploaded twice in a run, and v6 requires a runner of 2.327.1 or newer.
Dependabot reports drift, not silence. The Windows workflows rotted
because they never reached a runner, not because nobody was told, and
the runner image fix in the preceding commit is the cure for that half.
The cross-repository reusable workflow in regression_test.yml is
correctly left alone: it references a branch, so it carries no version to
move.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Renamed the Studio IMAGE_INFO struct, which the Windows SDK now defines
With the Windows workflows reaching a runner again, the Studio build
failed immediately on both of them:
guix_studio\system_pngs.h(15,8): error C2011:
'IMAGE_INFO': 'struct' type redefinition
Windows Kits\10\Include\10.0.26100.0\um\Filter.h(340,16): error C2011:
'IMAGE_INFO': 'struct' type redefinition
plus eight C2027 "use of undefined type" errors following from it.
guix_studio declared its own two-field `IMAGE_INFO` in the global
namespace. A newer Windows SDK added a struct of the same name to
um/Filter.h, which arrives through the Windows headers the MFC sources
already include. SDK 10.0.22621.0 does not define it; 10.0.26100.0 does.
This was latent rather than new. studiox.vcxproj sets
`<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>`, the
MSBuild wildcard for "newest installed SDK", so which SDK compiles the
Studio depends entirely on the machine. A developer box with 22621 as its
newest builds; the runner image, which carries 26100, does not. The
collision was simply waiting for whichever came first.
Renamed the Studio type to STUDIO_IMAGE_INFO -- 13 references across five
files. It is private to the Studio, so nothing outside guix_studio sees
the name.
Also dropped the `-winsdk=10.0.22621.0` argument that
build_guix_studio.cmd passed to VsDevCmd. It never had the effect it
looks like it has: the wildcard in the project file governs the SDK the
compile actually uses, so the argument only set the shell environment and
left MSBuild to pick the newest SDK anyway. Keeping it would suggest the
build is pinned to an SDK when it is not. With the collision fixed there
is nothing to pin against.
Verified by rebuilding the Studio against SDK 10.0.22621.0 locally; the
runner will exercise 10.0.26100.0.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Pointed the demo test back at the guix project that exists
The demo compile test could not build the GUIX library at all:
Exception: guix.sln not found.
path=../../../ports/win32/build/vs_2022/guix.vcxproj
522aafc ("Updated scripts for VS Community 2022", February 2025) moved
three paths in test_main.py from vs_2019 to vs_2022. Two of them were
right: guix_studio/build/vs_2019 really had been renamed to vs_2022. The
third was collateral damage from the same search and replace --
ports/win32/build/vs_2019 was never renamed, and still holds the only
guix.vcxproj and guix.sln in the repository.
So --build_guix has raised this exception for eighteen months, before
compiling a single library configuration. Nothing said so, because the
workflow that calls it never reached a runner.
Restored the vs_2019 path. Verified by building the library locally:
guix.vcxproj is Win32-only and still on PlatformToolset v142, and it
produces Debug/gx.lib.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Pointed the view test at the Studio build directory that exists
The Studio view test could not start the Studio at all:
MSBUILD : error MSB1009: Project file does not exist.
Switch: ../../../guix_studio/build/vs_2019/studiox.sln
Exception: Unable to locate Studio executable.
522aafc ("Updated scripts for VS Community 2022", February 2025) renamed
guix_studio/build/vs_2019 to vs_2022 and updated test_demo/test_main.py
for it, but never touched the test_view directory. Three references there
still named the old directory:
- test_view/test_main.py project_sln_path
- test_view/test_utils.py studio_exe_path
- test_view/test_string_import_export.py studio_release
So this suite has been unable to locate the Studio for eighteen months.
Like the guix.vcxproj path in the preceding commit, nothing reported it,
because the workflow that runs it never reached a runner.
The remaining build/vs_2019 references in the tree belong to
ports/win32/build/vs_2019, which is a real directory and is left alone.
This commit makes the suite able to find and launch the Studio. Whether
it then passes on a hosted runner is a separate question -- it drives the
Studio through win32 window handles, and no run has got far enough to
say. Its trigger branches are deliberately not changed here for that
reason.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Ran the Studio demo suites on dev, where the pull requests actually are
The demo and demo compile workflows trigger on master only, for both push
and pull_request. dev is the integration branch, so these suites have
gated no pull request that anybody opened -- the same defect
eclipse-threadx/threadx fixed in its own regression suites in August 2026.
Switched on only after both suites were verified green on a runner, on
this branch, rather than on the assumption that they would be:
GUIX Studio Demo Test 227 of 227 generation tests 147 s
GUIX Studio Demo Compile Test 212 of 212 compile tests 146 s
The other three workflows are deliberately left on their current
triggers, and each for its own reason:
- regression_test.yml does reach a runner, and is red. Its last real
run, 2026-06-30, was 5 failures out of 732: accordion menu,
ml_text_view_32bpp, animation_complete, animation_complete_push_stack
and one more. Adding dev here would make every pull request red on a
suite that has been broken since June. It needs fixing first, and
that is not this change.
- studio_view_test.yml can now find the Studio again, but no run has
got far enough to say whether GUI automation through win32 window
handles survives a hosted runner. Enabling gating on an unknown is
what this commit is written to avoid.
- studio_msix_package.yml is workflow_dispatch only by design. It
packages a release rather than testing a change, so there is nothing
to gate.
Assisted-by: Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ng (#175)
Commit 8a35f69 (#158) made _gx_animation_start() assign the return value of
_gx_canvas_drawing_initiate() to its local "status" variable. That variable is
both the value returned to the caller and the flag that gates linking the
animation into the active list, so the drawing status leaked into the
completion status.
_gx_widget_show() releases the view list of the animation root window on the
line just above that call, so _gx_canvas_drawing_initiate() reports
GX_NO_VIEWS every time an animation canvas is used. The animation was
therefore never linked into _gx_system_animation_list, the frame timer was
never started and _gx_animation_complete() never ran: no animation frames
were produced and GX_ANIMATION_PUSH_STACK never pushed its target onto the
screen stack.
The drawing status is now held in a separate draw_status variable. The #148
guard that keeps _gx_canvas_drawing_complete() from being called after
GX_DRAW_NESTING_EXCEEDED is preserved, while the completion status returned
to the caller is left untouched.
_gx_animation_drag_tracking_start() contains the identical block but returns
GX_SUCCESS unconditionally, so it was not affected. Its variable is renamed to
draw_status as well, with no change in behaviour, so the two functions cannot
drift apart again.
Verified on Linux with the default_build_coverage regression suite. Before the
fix, matching CI run 28477318897 exactly:
447 guix_all_widgets_16bpp_canvas_animation SEGFAULT
457 guix_animation_complete golden_file_frame_id = 1, test_frame_id = 3
458 guix_animation_complete_push_stack Failed, no output
After the fix all three pass and 730 of 732 tests pass. The two remaining
failures, guix_ml_text_view_32bpp and guix_all_widgets_accordion_menu, are
unrelated to animation and are diagnosed separately.
No new regression test is added: guix_animation_complete,
guix_animation_complete_push_stack and guix_all_widgets_16bpp_canvas_animation
already cover this path and are the tests that caught the regression. No
golden file is regenerated, since the change restores the recorded behaviour
rather than altering it. guix_canvas_draw_nesting_overflow_no_output, the test
added by #158, still passes.
No documentation change is required: no public API or behaviour contract
changes.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The fix for issue #148 (#158) made every caller of
_gx_canvas_drawing_initiate() skip its draw when the call returns
GX_DRAW_NESTING_EXCEEDED. Four of those callers push a nested context on the
widget they are already drawing, for one reason only: to narrow the clipping
rectangle to the widget's client area. For them, skipping the draw turns a
correct rendering into no rendering at all, which is what made
guix_all_widgets_accordion_menu report "Frame 12 is different".
The accordion menu screen of the all_widgets demo nests widgets nine levels
deep - multi_level_accordion, menu_list, mla_menu_1_accordion, menu_list,
text_view_3 - and _gx_system_canvas_refresh() consumes two contexts before the
widget tree is walked, so the eight slots of GX_MAX_CONTEXT_NESTING are
exhausted before _gx_multi_line_text_view_text_draw() can push its own. Raising
the limit in a scratch build makes frame 12 match the existing golden file
exactly, which shows that the golden records the correct rendering and that the
text is now being lost rather than merely clipped differently.
When the stack is full there is nothing to push, but the caller's context is
still the right context to draw through: it was created for the same widget and
differs only in its clipping rectangle. These four callers now narrow the
caller's clipping rectangle, draw, and restore it, instead of dropping the draw.
_gx_canvas_drawing_complete() is still not called on overflow, so the stack
corruption that #158 fixed stays fixed.
Applied to _gx_multi_line_text_view_text_draw, _gx_multi_line_text_input_draw,
_gx_rich_text_view_text_draw and _gx_single_line_text_input_draw.
_gx_widget_block_move and _gx_radial_progress_bar_background_draw are left
alone: neither pushes a clip-only context on the widget being drawn.
guix_canvas_draw_nesting_overflow_render_no_output closes the coverage gap that
#158 left. guix_canvas_draw_nesting_overflow_no_output covers detection and
state preservation; the new test covers what a caller must render: that the text
is still drawn at maximum nesting depth, that the borrowed context is handed
back with its nesting count, context pointer and clipping rectangle unchanged,
and that the pixels match those produced when a nested context is available.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
b8bb23b (#159) fixed issue #130 by letting the overflow branch of
_gx_multi_line_text_view_display_info_get() run when an ASCII space is the
character that overflows the available width. That branch consumes the space
and any consecutive spaces without adding them to the row width, which is
correct, and then breaks. When the whitespace ran to the end of the source
line, the line terminator was left behind: the next call started on it, hit
the GX_KEY_LINE_FEED case immediately and returned a row of one byte and zero
width, which draws as a blank line that is not in the text.
The overflow branch now takes the line terminator with the whitespace when
nothing else separates them, handling a bare line feed and a carriage return /
line feed pair, and guarding the second byte on the remaining length -- unlike
the pre-existing GX_KEY_CARRIAGE_RETURN case earlier in the same loop, which
reads ch.gx_string_ptr[1] unchecked. gx_text_display_width is untouched, so the
issue #130 fix stands.
Two such rows appear in the guix_ml_text_view_32bpp fixture, at string offsets
2992 and 23988 of readme_guix_generic.txt: "...Improved internal logic." with
twelve trailing spaces, and "...cursor_pos_calculate.c" with one. Both were
confirmed with a conditional breakpoint on display_number == 1 and
display_width == 0 preceded by a space, which fires exactly twice on the
pre-fix code and never after.
The two extra rows are why 268 of that test's 300 frames and 144 of
guix_bidi_text_draw_32bpp's 429 frames have differed from their golden data
since June. Nothing inside GUIX reads gx_text_display_width -- every caller
uses only gx_text_display_number, to advance its index -- so the row count
alone governs where every row starts, and two extra rows change the
scrollbar's value-to-pixel mapping. Every scroll step then lands at a slightly
different pixel offset and the whole text block is drawn a few pixels up or
down. Of the 300 frames, 297 were a pure vertical shift of otherwise identical
text; only 3 were laid out differently, and those 3 are the two places above.
No golden data is regenerated. gx_multi_line_text_view_text_total_rows for the
long text view goes back from 2226 to 2224, and both tests pass against their
existing golden files.
guix_ml_text_view_word_wrap_no_output did not protect #159's fix: it passed on
the pre-#159 code as well. Its available_width was one pixel too generous --
a_width + space_width, so the over-wide row the old code produced measured
exactly available_width and still satisfied "width <= available_width". It is
now a_width + space_width - 1, which makes appending the space genuinely
overflow, and three cases are added: a line feed terminator, a carriage return
/ line feed terminator, and the total row count that
_gx_multi_line_text_view_string_total_rows_compute() derives from them. That
last one is the quantity the golden frames actually depend on, and the only
one of the three a unit test can pin without golden data.
The test was verified to discriminate in both directions by rebuilding against
each. Against the pre-#159 source it fails four width assertions, which is
issue #130. Against #159 as shipped it fails the line feed row (2 bytes, not
3), the carriage return / line feed row (2, not 4) and the row count (3, not
2). It passes only with both fixes in place.
Verified on Linux across all eighteen build configurations, 1847 tests, no
failures. default_build_coverage 733/733, dynamic_bidi_text_build 3/3
including guix_bidi_text_draw_32bpp, no_utf8_build_coverage 135/135.
One coverage boundary worth naming: the unit test builds against the
all_widgets demo, which is not in NO_UTF8_DEMOS, so it runs in
default_build_coverage and disable_error_check_build but not in the
GX_UTF8_SUPPORT-off configurations. The changed code is common to both paths
-- only the surrounding character advance differs -- and no_utf8_build_coverage
covers it through its 135 golden tests.
No documentation change is required. _gx_multi_line_text_view_display_info_get
is internal, and the GUIX documentation does not describe multi-line text view
wrapping behaviour for trailing whitespace.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
)
_gx_multi_line_text_view_display_info_get() decided whether a line terminator
was "\r" or "\r\n" by examining ch.gx_string_ptr[1], the byte after the current
character, without checking that a byte remained. When the carriage return is
the last byte of the range, that access is outside the caller's buffer.
A GX_STRING carries its own length and need not be NUL terminated -- that is
why the _ext string API exists, and it is what the deprecation warning added in
#167 says about the older char * API. _gx_multi_line_text_view_text_set_ext()
stores the caller's GX_STRING verbatim unless GX_STYLE_TEXT_COPY is set, and
callers of this function pass end_index = gx_string_length, so in the default
configuration the read lands one byte past what the application supplied.
The consequence is not only the read. If that byte happens to be 0x0A, the
function reports a two-byte terminator for a one-byte remainder, so every
caller advances its index by one more than the string holds. In
_gx_multi_line_text_view_string_total_rows_compute() the loop then exits with
index == gx_string_length + 1 and evaluates string.gx_string_ptr[index - 1],
one byte past the end as well, and can add a row that is not in the text. The
same over-advance reaches the line index cache in
gx_multi_line_text_view_line_cache_update.c and the cursor arithmetic in
gx_multi_line_text_input_cursor_pos_update.c.
Both reads now go through string rather than ch. string has already been
advanced past the current character and its length is exactly the number of
bytes still readable there, so guarding on it bounds the access to what the
caller supplied.
The same idiom in _gx_multi_line_text_input_new_line_character_get() is
corrected as well. That one is not currently reachable as a read past the end:
its only caller, _gx_multi_line_text_input_text_set_ext(), writes a NUL
immediately after the copied text. It is fixed anyway because it depends on an
invariant established in a different function and documented nowhere, and
because the byte it reads decides whether the widget inserts a one- or two-byte
terminator on every subsequent Enter.
The rest of the codebase was audited for the same shape. Five other sites
handle a carriage return followed by a possible line feed; all five are already
correct. gx_rich_text_view_line_info_get.c, gx_multi_line_text_input_char_insert.c
and gx_utility_bidi_paragraph_reorder.c guard on the remaining length, the
preprocessed-line-break branch at the top of the changed function guards on it
too, and gx_multi_line_text_button_line_pointers_set.c walks a NUL-terminated
char * buffer where the byte after a carriage return is at worst the
terminator.
guix_ml_text_line_terminator_bounds_no_output covers this. It places a line
feed immediately after the string under test but outside the length the widget
is given, so the over-read is observable without a sanitizer: the unfixed code
counts the out-of-bounds byte and reports a 4-byte row for a 3-byte string,
which the test catches as "Expected: 3, Got: 4". Two controls pin the cases
that must not change -- a "\r\n" pair genuinely inside the string still counts
as two bytes, and a carriage return followed by ordinary text still counts as
one.
The test also asserts the terminator a multi-line text input adopts from its
text, for a lone carriage return and for a "\r\n" pair. Those two cases pass
both before and after the change, for the reason given above; they are there to
pin the behaviour, not to reproduce a fault.
Verified on Linux across all eighteen build configurations, no failures.
No documentation change is required: no public API or behaviour contract
changes, and the corrected behaviour is what the documented terminator handling
already describes.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX Studio Demo

227 tests 227 ✅ 23m 30s ⏱️
1 suites 0 💤
1 files 0 ❌

Results for commit e2d28f5.

♻️ This comment has been updated with latest results.

@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX Studio Demo Compile

213 tests 213 ✅ 18m 59s ⏱️
1 suites 0 💤
1 files 0 ❌

Results for commit e2d28f5.

♻️ This comment has been updated with latest results.

@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX

1 849 tests +1 117 1 849 ✔️ +1 122 31m 11s ⏱️ + 18m 15s
18 suites + 17 0 💤 ± 0 
18 files + 17 0 - 5 

Results for commit e2d28f5. ± Comparison against base commit 826487c.

♻️ This comment has been updated with latest results.

@github-actions

Copy link
Copy Markdown

Code Coverage

PackageLine RateBranch RateHealth
default_build_coverage Package 1100%99%
Summary100% (44377 / 44475)99% (24284 / 24434)

@github-actions

github-actionsBot commented Aug 27, 2026

Copy link
Copy Markdown

Test Results GUIX Studio View

40 tests 40 ✅ 1h 21m 55s ⏱️
1 suites 0 💤
1 files 0 ❌

Results for commit e2d28f5.

♻️ This comment has been updated with latest results.

compare_file() locates the line where comparison should start, so that the
banner GUIX Studio writes into every generated file -- which carries a revision
string and a generation timestamp -- is skipped rather than compared. It looked
that line up correctly in the first file and then looked it up in the first file
again for the second:
for line in list_2:
if compare_start_string in line:
start_row_2 = list_1.index(line)
So the golden was sliced at the *generated* file's offset. While both files
happened to reach "#include" on the same line the mistake was invisible, which
is why it survived since the tests were added in #84.
The offsets diverged when 6ba13db added a ten-line MIT licence header to all
111 golden .c and .h files. GUIX Studio does not emit that header, so every
golden now reaches its first #include ten lines later than the file it is
compared against. The golden was therefore sliced ten lines early and compared
banner text against source.
That made every one of the 107 .c and .h comparisons in the suite fail -- all 29
reported test failures -- while the .csv, .xliff and .xml comparisons passed.
Those use skip_line instead of compare_start_string and never reach this code,
which is exactly the split the failure log shows.
The reported mismatch is the same in all 29: a generated "#include" line against
a golden banner comment. Confirmed arithmetically -- in every case the golden
line reported sits exactly ten lines before that golden's own first #include:
generic_16bpp_resources.c reported idx 13, #include at 23, delta 10
generic_16bpp_resources.h reported idx 16, #include at 26, delta 10
generic_16bpp_specifications.c reported idx 14, #include at 24, delta 10
generic_16bpp_specifications.h reported idx 16, #include at 26, delta 10
compare_output_file() is the only caller that passes compare_start_string, so
the change cannot affect the xliff, xml or plain-file comparisons.
Verified by running the suite locally against a Studio built from this tree.
Seven suites that fail in CI now pass with no mismatches at all, each gaining
exactly the tests it had been failing:
CI (broken) local (fixed)
Font 9 / 1 10 / 0
Multi-Themes 18 / 1 19 / 0
Project Import 10 / 2 12 / 0
Trigger Edit 6 / 1 7 / 0
Trigger Target Rename 1 / 1 2 / 0
Bidi Text 3 / 1 4 / 0
Widget Name 3 / 1 4 / 0
Project Import compares a generated specifications file, so this also shows that
the screen flow prototype block added by #173 does not disturb these goldens:
that block is only emitted for projects that use Screen Flow, and these do not.
No golden file is regenerated. The goldens still carry "GUIX Studio Revision
6.1.12.0" and a 2022 timestamp in their banners, and still name Azure RTOS
rather than Eclipse ThreadX, but the banner is what the comparison is supposed
to skip -- so none of that needs to be touched to make the suite correct.
This workflow had never actually executed before 2026-08-27. Every earlier run
was cancelled after queueing 24 hours for the retired windows-2019 image, or
sat awaiting approval on a fork pull request, including the run for the v6.5.1
release itself. #174 gave it a runner again; this is the first result it has
ever produced, and the defect it found is in the test harness rather than in
GUIX or in Studio.
Note that studio_view_test.yml triggers only on pull requests targeting master,
so this change is not exercised by its own pull request into dev. It is
validated by the release pull request that carries dev to master.
Assisted-by: Claude Code (Opus 5)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@fdesbiens
fdesbiens merged commit 52c1bb4 into masterAug 27, 2026
13 checks passed
Sign up for freeto 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.

1 participant

@fdesbiens