Skip to content

[pull] master from GaijinEntertainment:master#985

Merged
pull[bot] merged 7 commits into
forksnd:masterfrom
GaijinEntertainment:master
May 12, 2026
Merged

[pull] master from GaijinEntertainment:master#985
pull[bot] merged 7 commits into
forksnd:masterfrom
GaijinEntertainment:master

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented May 12, 2026

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

borisbat and others added 7 commits May 11, 2026 21:26
…ommands

C++ encoder in modules/dasStbImage/src/apng_write_impl.h reuses stb's
internal CRC32 + zlib_compress, runs encode + file I/O on a worker
thread with a bounded queue (drop-oldest on overflow). Backpatches
acTL.num_frames on close as a single 12-byte write (body + recomputed
CRC). Exposes stbi_apng_begin/frame/end/dropped to daslang.

Live commands live next to screenshot in opengl_live.das: record_start
(file/fps/max_seconds), record_stop, record_status. Capture runs from
[before_update] at the fps throttle with auto-stop on max_seconds.

dastest covers chunk parse + acTL backpatch, frame 0 decode via
stbi_load (default-image fallback), RGB+RGBA, error paths, overflow
drops, parallel writers — 12 cases green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dasGLFW.main.cpp gains a multi-listener callback chain for cursor pos,
mouse button, and scroll. The dispatcher captures whatever prior C
callback was installed (ImGui_ImplGlfw, app-level callbacks) and replays
it as the chain's "prev", so synth events posted via DasGlfw_Post* /
Dispatch* reach every real listener too. New publics:
glfw_chain_add_cursor_pos / mouse_button / scroll, glfw_chain_clear,
glfw_post_cursor_pos / mouse_button / scroll, glfw_dispatch_cursor_pos.

glfw_live grows a synthetic mouse driver with live commands:
mouse_pos, mouse_click, mouse_scroll, mouse_move_to, mouse_play,
mouse_stop, mouse_status. Per-frame [before_update] tick lerps between
move waypoints and posts one cursor event per frame, so ImGui and
overlays see smooth motion. get_synth_cursor() exposes (active,x,y) for
overlays to draw against the synth position instead of GetMousePos() --
ImGui_ImplGlfw's per-frame poll would otherwise overwrite io.MousePos
with the real OS cursor on focused windows.

The OS cursor is never warped: it lives outside the GL back buffer
(the window manager paints it at display time), so warping has no
effect on glReadPixels-driven APNG recordings.

daslib/json_boost.das: PERF018 fix -- from_JV array form now iterates
the array directly instead of via range(length).

docs: register stbi_apng_* in das2rst stbimage groups + fill the four
handmade stubs the apng commit left behind; register the synthesized
finalize() in strudel_scheduler's lifecycle group; document the new
glfw_live mouse commands in daslang_live.rst + skills/daslang_live.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
#1 DasGlfw_ChainClear restores prev callbacks before erasing the map
entry. Without this, the dispatcher stays installed but early-returns
on every real event, silently disconnecting ImGui_ImplGlfw and any
other prior listener.

#2 ApngWriter::enqueue validates stride_bytes >= row_bytes and
rejects null pixels. Negative or too-small stride was being cast to
size_t and underflowing into OOB reads.

#3 stbi_apng_frame docstring now matches the implementation: positive
stride only, always returns 1 on success, drops oldest on overflow
(visible via stbi_apng_dropped). The prior copy claimed negative
stride and a 0-return-on-full contract, neither of which exists.

#4 mouse_click validates `action` and returns a structured error on
anything other than "press" / "release". Typos like "pressed" no
longer silently flip to release.

#5 mouse_play sorts the wire timeline by t_ms before building the
internal queue, so out-of-order input no longer drops earlier events.
Extracted MouseEventWire + sort_play_events into a small standalone
module (live/mouse_events) so the regression test under
tests/dasglfw/test_mouse_play_sort.das can require only the
algorithm, not glfw_live's full GLFW/OpenGL chain. The module is
marked options no_aot to keep the test interpreter-friendly under
test_aot. tests/aot/CMakeLists.txt registers tests/dasglfw/ alongside
the other test directories.

#6 record_start clamps `fps` to >= 1.0 and stores/reports the
effective value, instead of accepting a sub-1 fps in status output
while running the scheduler at the clamped rate.

#7 record_tick resyncs next_capture_t to `now + frame_interval_s`
after a stall instead of catch-up bursting. delay_ms now reflects
the actual elapsed time between captures so playback timing matches
the real spacing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI: revert the mouse_events module extraction from round 1. daspkg's
exe build treats live/glfw_live as a shared module
(dasModuleGlfw.shared_module) and rejected the require of a
native-path-only mouse_events ("error[20115]: Shared module glfw_live
has incorrect dependency type. Can't require mouse_events because its
not shared"). Put MouseEventWire + sort_play_events back inline in
glfw_live.das; drop the mouse_events.das file, the .das_module
register_native_path, and the tests/dasglfw entries in
tests/aot/CMakeLists.txt. The regression test under tests/dasglfw/
is now self-contained -- it mirrors the wire-event shape with a local
struct and verifies the same sort + less-than comparator lands
t_ms-ascending, so no glfw_live require chain is needed.

#8 mouse_play button branch validates action in {"press", "release"}
the same way mouse_click does. Invalid strings return a structured
error instead of silently flipping to release.

#9 dropped the "Stable-sort" claim from sort_play_events' docstring
and from the test -- daslang's sort is qsort, not stable. The test
now uses distinct t_ms values (no ties), asserting only the
non-decreasing-t_ms invariant mouse_play depends on.

#10 record_tick now deletes the per-frame pixels buffer at end of
function. Local var array doesn't finalize on scope exit, so the
heap was growing by width*height*4 bytes per captured frame.

#11 stbi_apng_frame docstring now states the writer expects bottom-up
row order (glReadPixels output) and flips internally, so callers
don't double-flip.

#12 write_chunk + the acTL backpatch fold the chunk type + body into
an incremental byte-table CRC32 instead of memcpy'ing the whole body
into a temporary vector. Removes the per-chunk peak-memory doubling.
12/12 APNG tests pass (CRC sanity verified through chunk-parse round
trip).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
#13 ApngWriter::end now rejects zero-frame closes. Previously it would
emit IHDR/acTL/IEND with no IDAT/fdAT -- not a valid PNG/APNG. Stores
filepath in begin(), and on end() with frames_written == 0 closes the
file, calls remove() on the partial artifact, and returns failure so
the caller doesn't ship a corrupt file.

#14/#16/#17 test_apng.das: every stbi_load result is now followed by
a success(loaded != null, ...) assertion, and the dimension checks
(equal x/y) only fire when the load succeeded. Locals x/y/comp are
initialized to 0 so a load failure doesn't read uninitialized memory
in subsequent (skipped) checks. Applied to test_apng_basic and
test_apng_double_writers' l1/l2 paths.

#15 test_apng_drop_accounting (renamed from
test_apng_drop_on_overflow) replaces the tautological
dropped_before_end >= 0 assertion with the real submitted = emitted +
dropped invariant: dropped_before_end + chunks.n_fctl == 40. drop_count
is only mutated by enqueue (finalized once all 40 submits complete),
and end() drains the worker queue, so the invariant is race-free.
Works whether the worker was fast (few drops, lots of fcTL) or slow
(lots of drops, few fcTL).

#18 stbi_apng_frame docstring now notes that 0 can also indicate the
writer entered an internal error state from a prior async I/O or
encode failure, and that callers should stop and call stbi_apng_end
in that case.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
#19 ApngWriter::enqueue guards size_t overflow on row_bytes * h.
Realistic 32-bit hit (windows-32 CI lane); on 64-bit only kicks in for
gigapixel frames. One-line `if (h && row_bytes > SIZE_MAX/h) return
false;` before the resize.

#20 ApngWriter::emit_frame guards the deflate-input multiply
((row_bytes + 1) * h) and the implicit narrowing to int when calling
stbi_zlib_compress (which takes int len). Same 32-bit motivation;
the INT_MAX cap also matters when zlib API only sees a 31-bit length.
Pulled in <climits> for INT_MAX.

#21 daslang_live.rst regression -- the apng commit had updated the
live/opengl_live row to "OpenGL screenshot + APNG video recording
commands.", but the round-1 synth-mouse commit silently clobbered it
back to "OpenGL screenshot command." during the stash juggling for
the Sphinx-baseline test. Restored. Copilot caught it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…events

dasGLFW chain dispatcher + synth input; glfw_live mouse driver; APNG recorder
@pull pull Bot locked and limited conversation to collaborators May 12, 2026
@pull pull Bot added the ⤵️ pull label May 12, 2026
@pull pull Bot merged commit a5b12f6 into forksnd:master May 12, 2026
@pull pull Bot added the ⤵️ pull label May 12, 2026
@pull pull Bot had a problem deploying to github-pages May 12, 2026 14:58 Error
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant