Conversation
Document two issues with Nerd Font usage in KiTTY: 1. PUA characters display only half-width due to mismatch between static Unicode width tables (mk_wcwidth) and actual font glyph width, causing ATTR_NARROW compression or empty UCSWIDE cells 2. Nerd Font fonts missing from font selection dialog because CF_FIXEDPITCHONLY flag filters out fonts with varying advance widths (Nerd Font icons are 2x width), which Windows classifies as variable-pitch Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
GCC 10+ defaults to -fno-common, which breaks tentative definitions in headers that previously merged as common symbols. Fixed by: - url/urlhack.h: add extern to variable declarations - kitty_commun.c: move urlhack_default_regex/urlhack_liberal_regex definitions here (linked by all executables) - kitty.h: add extern to TrayIcone - kitty.c: remove duplicate TrayIcone definition - pageant.h: add extern to trayIcone - window.c: add TrayIcone and trayIcone definitions - pageant.c: guard conf/hinst/trayIcone with MOD_INTEGRATED_AGENT - puttygen.c: guard conf with MOD_INTEGRATED_KEYGEN
GCC 13's immintrin.h on i686 pulls in avx512bf16intrin.h and avxneconvertintrin.h which reference types (__m512bh, __bf16, _Float16) unsupported on 32-bit targets. Fix: only include immintrin.h on x86_64; on i686, define _IMMINTRIN_H_INCLUDED to satisfy shaintrin.h's guard check. The specific headers (wmmintrin.h, smmintrin.h) already provide what's needed for SHA-NI on i686.
GDI + GetGlyphIndicesW route: per-codepoint slot probing, run splitting, multi-pass ExtTextOutW with cell clipping. EAW-driven width preserved. Independent file logger (default off, configurable via [FontFallback] ini), log file colocated with the KiTTY binary. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Tasks cover: header/stubs, logging, Makefile wiring, ini reading, window.c integration, glyph probe/cache, split/draw, ini Fallback/Override, reset handling, clean build verification, test strings, known limitations. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add winfont_fallback.o to both the putty.exe dependency list and the link command, and add a compile rule mirroring window.o's pattern. Mark winfb_state_touch_unused as __attribute__((unused)) to silence -Wunused-function until Tasks 6-10 wire up the state. Verified with both cross (32-bit) and cross64 (64-bit) builds.
…ortable exe variants
…dow.c lifecycle Task 5: hook the font fallback module's lifecycle entry points into KiTTY's existing font management: - include winfont_fallback.h after the kitty_* include cluster - call winfb_init at the end of init_fonts (before ReleaseDC) so the module sees the freshly created primary LOGFONT and cell metrics - call winfb_reset after each of the four init_fonts re-creates in reset_window (stub for now; T11 will turn it into a full re-init) - call winfb_cleanup as the last action in deinit_fonts - call winfb_log_close in cleanup_exit immediately after deinit_fonts All five sites use the stub implementations from T2 — once the probe cache / HFONT factory lands in T6, these calls will start doing real work without further changes to window.c. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…d HFONT creation
Replace stub winfb_init/reset/cleanup/hfont with real implementations and
add internal winfb_lookup_slot. Module now:
* keeps a probe DC + retained primary HFONT for GetGlyphIndicesW
* resolves codepoints via override ranges, BMP cache, primary probe,
then each installed fallback slot (lazy HFONT creation per attr combo)
* supports a built-in default fallback list, user CSV (! to replace),
and "XXXX-YYYY:Face" override ranges (Task 10 will pass them in)
* verifies installed-ness via EnumFontFamiliesExA and compacts
uninstalled slots, remapping override slot indices accordingly
winfb_split and winfb_draw_runs remain stubs (owned by Task 7);
winfb_lookup_slot is __attribute__((unused)) for now and will be
exercised by Task 7's split implementation.
…imary HFONT failure
… do_text_internal
Covers Log/LogFile/Fallback/Override keys with default-off semantics and examples (Nerd Font Mono override, replace-builtins '!' syntax). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Strings cover ASCII / Box / Block / Geometric / Misc Symbols (U+23F5) / Dingbats / Arrows / CJK / Surrogate-pair emoji / Nerd Font PUA / Combining marks / Width-edge cases. Limitations doc notes monochrome emoji, baseline tolerance, doubled-line fallback, override syntax, RTL+fallback interaction, cache caps, and run cap. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…nt reinit GetGlyphIndicesW evaluates surrogate halves individually and never maps them to a real glyph, so non-BMP fallback never matches. Updated tests/font_fallback_test.txt and the design spec's limitations section to make this explicit and point users to the Override= workaround. Also dropped the four redundant reinit_font_fallback(NULL) calls in reset_window — init_fonts now reinit-s the fallback at its tail, so the surrounding reset_window callers were doing the same work twice. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…lt-in defaults Per user request: when the ini Fallback= list is set without the '!' prefix, the user's fonts are now tried FIRST and the built-in defaults are appended after them (instead of the previous prepend-builtins behavior). Case- insensitive deduplication is applied so listing a built-in just promotes it to the front. The '!' replace-entirely syntax is unchanged. Updates the header docstring and kitty_ini.txt documentation. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Both .h files are generated at build time by MAKEFILE.MINGW from kitty_ini.txt and docs/pages/CommandLine.md respectively (via sed), so they're build products and shouldn't live in the repo. Also ignore *.swp and the upx scratch file under 0.76b_My_PuTTY/windows/. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When a hyperlink's last character lies in the rightmost terminal column (region.x1 == cols, since x1 is half-open), the toggle-on-end position could never match the screen-walk loop bound (j < cols), so urlhack_is_link stayed at 1 forever and every subsequent cell (including blanks) on the screen was tagged with ATTR_UNDER. The pre-existing wrap-around special-case was checking x1 == cols-1, which is the wrong column (off-by-one against the half-open interval). Change to x1 >= cols so any link whose end falls at or past the right edge correctly advances the toggle to (0, y+1). Reproducer: `echo http://www.baidu.com` in an 80-col window with the URL ending exactly at column 79 — afterward every empty cell to the right and below shows an underline until the line is overwritten. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…lobbering the stack urlhack_go_find_me_some_hyperlinks() called regexec() with a single regmatch_t on the stack and nmatch=1. The bundled GNU regex library appears to write past nmatch on 64-bit MinGW builds when the compiled pattern has many subexpressions — the default URL regex has well over a dozen capture groups. The trampling corrupted whatever sat next to that local on the stack (in particular the outer regexec error code or text_pos pointer), which manifested as the URL-underline state machine going haywire and tagging every subsequent cell with ATTR_UNDER. Reproducer (64-bit only): typing 'http://w' in a normal terminal turns the entire screen tail into underline. 32-bit binaries don't trigger it because the same stack layout puts a less load-bearing local in the clobbered slot. Fix: give regexec a fixed-size 32-slot match array. We still only read groupArray[0] (the whole-match), but the library is now free to write all the subexpression ranges without escaping the buffer. 32 is well above what any built-in regex needs. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…errun) Captures the symptoms, the initial off-by-one false-positive, the architecture-sensitivity analysis, the actual root cause (GNU regex writing past nmatch when the compiled pattern has many capture groups), the fix (regmatch_t buf[32]), and lessons learned for future cross-arch debugging. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… regexec writes c1ff448 changed the regmatch_t buffer from a single stack variable to a fixed-size 32-slot array. That hid the URL-underline bleed but only because it moved the stack-write target to a different local. The underlying problem — GNU regex writing past whatever nmatch we pass when the compiled pattern's re_nsub is large — was never fixed. A subsequent symptom appeared: 64-bit kitty64.exe segfaults on Ubuntu SSH login (Ubuntu's MOTD/PS1 emits OSC title sequences that trigger a repaint, and the trampled bytes in the new stack layout happened to land on something load-bearing). 32-bit kitty.exe and CentOS-style prompts (no OSC title) don't trigger it. Real fix: read re_nsub from the compiled pattern and allocate exactly re_nsub + 1 regmatch_t slots (whole-match + each subexpression). Heap allocation so it scales with whatever regex the user configures, capped at 256 to refuse pathological patterns. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Covers: package install, building a debug-symboled kitty64_nocompress.exe, running it headless in Xvfb, taking screenshots, simulating mouse/keyboard with xdotool, attaching gdb with DWARF source-level debugging, alternative winedbg path, and a one-shot reproduction script for the URL underline bleed bug. Includes the cross64 .a file overwrite trap and common troubleshooting steps. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…he bundled GNULIB libregex_64.a Root cause: the prebuilt regex/libregex_64.a is GNULIB-based and was compiled expecting glibc-style 8-byte `unsigned long` and `ssize_t`-typed regoff_t. MinGW-w64 (LLP64) makes `unsigned long` and `int` only 4 bytes, so the historical KiTTY layout disagreed with the library on win64: field KiTTY offset / size library offset / size buffer 0 / 8 0 / 8 allocated 8 / 4 8 / 8 <-- mismatch used 12 / 4 16 / 8 <-- mismatch syntax 16 / 4 24 / 4 fastmap 24 / 8 32 / 8 translate 32 / 8 40 / 8 re_nsub 40 / 8 48 / 8 <-- mismatch total size 56 64 regmatch_t.rm_eo: KiTTY offset 4 / size 4 | library offset 8 / size 8 Result on 64-bit: - KiTTY read re_nsub = 0 (library wrote it at offset 48; KiTTY looked at offset 40), so the previous "nmatch = re_nsub + 1" fix collapsed back to 1 match slot. - KiTTY read regmatch_t.rm_eo as the high 4 bytes of rm_so (always 0), so the per-iteration "text_pos += rm_eo + 1" only advanced by 1 byte. The regex loop matched the same first URL repeatedly with ever-growing tail offsets, pushing hundreds of bogus link regions into link_regions[]. The toggle state machine in terminal.c then locked urlhack_is_link at 1 and painted the rest of the screen with ATTR_UNDER (the visible "URL underline bleed" symptom). - The 32-bit library was built with unsigned long = 4 bytes; KiTTY's layout matched it accidentally, which is why 32-bit was always fine. Fix: in regex.h, conditionally widen `unsigned long` -> uint64_t and `regoff_t` -> ssize_t on 64-bit targets, so the application-side struct layouts line up exactly with the prebuilt library. 32-bit layouts are untouched. Verified under wine64 + Xvfb with gdb attach: urlhack_rx.re_nsub now reads 13 (was 0) link_regions_current_pos after Ubuntu MOTD repaint: 3 (was 580) No more underline bleed. Also relaxes the "nmatch = re_nsub + 1" guard with a tiny floor of 4 so a not-yet-compiled regex with re_nsub = 0 still gets a usable match buffer. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ault The earlier postmortem (2026-05-28) chased the wrong assumption (GNU regex writing past nmatch). Actual root cause, found via gdb attach to kitty64.exe running under wine+Xvfb: regex/libregex_64.a is built from GNULIB with glibc- style 8-byte `unsigned long` and ssize_t-typed regoff_t, but regex/regex.h declares them as the MinGW-LLP64 4-byte equivalents. The struct layouts disagree on 64-bit: re_pattern_buffer is 64 bytes vs KiTTY's 56, re_nsub at offset 48 vs KiTTY's 40, regmatch_t.rm_eo at offset 8 vs KiTTY's 4. KiTTY read rm_eo = 0 every iteration, so the URL-finding loop matched the same URL repeatedly and overflowed link_regions[] to 580 entries, jamming the toggle state machine into "all subsequent cells underlined" mode. 32-bit escaped because MinGW32's unsigned long happens to be 4 bytes too. This document captures: the symptoms, the two wrong-assumption detours, the gdb session that found the real bug (with byte-level struct dumps), the fix in regex.h (KITTY_REGEX_ULONG / KITTY_REGEX_OFFT macros), the verification numbers (re_nsub now 13, link_regions count now 3), and the takeaway lessons. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…er-cross64 link failures
Root cause: MAKEFILE.MINGW's cross64 recipe does
cp mini/mini_64.a mini/mini.a
cp bcrypt/bcrypt_64.a bcrypt/bcrypt.a
cp base64/base64_64.a base64/base64.a
to satisfy its own 64-bit link line, but never restores them. Running
`./build.sh cross` immediately after `./build.sh cross64` then asks
i686-w64-mingw32-ld to consume x86_64 archives, which it silently
treats as "no matching symbols", leaving dozens of undefined-reference
errors like readINI / writeINI / bcrypt_string_base64 — symptoms that
look unrelated to the .a swap.
Fix: have build.sh restore the right architecture's archives before
each cross / cross64 run. The 64-bit versions are committed as
mini_64.a / bcrypt_64.a / base64_64.a (always available); the genuine
32-bit content was overwritten and committed by a previous cross64
build (commit fead573), so the 32-bit restore pulls from the older
035070c commit which still has authentic i386 objects.
Also makes the script work no matter which directory it's invoked
from (cd to its own dir first).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… via LIBSUFFIX Previously every vendored library was both committed as xxx.a (32-bit by convention but with no enforcement) and xxx_64.a (64-bit). The cross64 Makefile recipe would then `cp xxx_64.a xxx.a` to satisfy its sub-`make plink.exe pscp.exe ...` calls which had xxx.a hardcoded in their link lines. That left the repo's "32-bit" archives holding amd64 content after every cross64 run — so the next `make cross` would silently link against the wrong architecture and fail with hundreds of "undefined reference to readINI / writeINI / bcrypt_string_base64 ..." errors that looked unrelated to the .a files. Worse, the polluted state survived through commit fead573 because the user committed those overwritten .a files. So even a fresh `git checkout` produced a broken 32-bit baseline. This change: - Renames every vendored .a to xxx_32.a + xxx_64.a (both committed). The bare xxx.a names are gone from the repo and now ignored. - The genuine 32-bit content for base64 / bcrypt / mini was recovered from commit 035070c (last commit before the cross64 pollution). - The Makefile uses $(LIBSUFFIX) to pick between _32 and _64; cross / cross64 set it to _32 / _64 respectively and pass it down via `make -e` to the sub-recipes. - The cross64 recipe no longer does `cp xxx_64.a xxx.a`. It now normalises the link product (putty.exe or kitty64.exe) to a single name before the rest of the recipe runs. - build.sh stops needing the restore-from-old-commit dance: a single plain `make cross` or `make cross64` is enough, in any order, any number of times. Verified cross → cross64 → cross → cross64 in a fresh checkout: all four exit 0 with zero undefined references; produced kitty.exe (32-bit, 877 KB) and kitty64.exe (64-bit, 864 KB) each time. Note: md5/MD5check_64.a was, and is, actually i386 content — a pre-existing oddity. md5 only exports a handful of symbols and ld accepts them anyway. Left untouched. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two new scripts under scripts/:
setup-wine-debug-env.sh
- Enables i386 multiarch
- apt-get installs: wine, wine64, wine32:i386, xvfb, xdotool, xpra,
gdb, putty-tools, imagemagick, tesseract-ocr, openssh-server, file
- Initialises two wine prefixes under ~/wine_prefixes:
wp32 (WINEARCH=win32) for 32-bit kitty.exe
wp64 (WINEARCH=win64) for 64-bit kitty64.exe
- Prints a readiness report (commands, prefixes, versions, multiarch)
build-and-test.sh [cross|cross64|both]
For each requested architecture:
1. make clean + ./build.sh <arch>
2. Launch the resulting .exe under wine + Xvfb on display :99
3. SSH to local sshd (generates its own ed25519 key + .ppk if needed)
4. Auto-click the host-key accept dialog
5. Type three shell commands (uname -srm, echo, ls /etc/os-release)
6. Validate via:
- screen-c PNG size (terminal not blank)
- mean pixel value in cropped window region
- tesseract OCR for "uname / Linux / hello-from-<arch> / os-release"
7. Per-arch PASS/FAIL written to /tmp/kkit-<arch>/result.txt
Verified on Ubuntu 24.04 + wine 9.0: both 32-bit and 64-bit binaries
launch, render the Ubuntu MOTD correctly (no URL-underline bleed since
the regex ABI fix), and execute three shell commands whose output OCRs
back to the expected strings.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…rview Aimed at future Claude Code sessions opening the repo cold. Covers: - the only two build commands that matter (./build.sh cross / cross64) and the LIBSUFFIX-based 32/64 lib selection - end-to-end wine + Xvfb verification flow (setup-wine-debug-env.sh and build-and-test.sh — what they do, when to run them) - how to attach gdb to a wine-hosted KiTTY with DWARF symbols, including the -O2 -g + nocompress.exe gotcha - the two source trees (0.76 baseline vs 0.76b active), the MOD_PERSO extension modules, the font fallback subsystem hook point - the regex ABI trap that bit us hard (LP64 vs LLP64) and how to recognise its fingerprint on future 64-bit-only bugs - where to write design specs / implementation plans / runbooks / postmortems and what shape a postmortem should take - a few "looks broken but isn't" gotchas (cross64 fallback ld errors, MD5check_64.a actually being i386, wine font-fallback warnings) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Summarises 35 commits vs master: font fallback, regex-ABI crash fix, GCC 13 build fixes, parallel compile, LIBSUFFIX refactor, wine debug infrastructure. Suitable for release memo / changelog. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
by testing, both 64bit and 32bit can work correctly
hknet
added a commit
to hknet/KiTTY
that referenced
this pull request
Jul 19, 2026
When the primary terminal font lacks a glyph (box drawing, Nerd Font icons, CJK, symbols), probe a configurable list of fallback fonts and draw the character from the first font that has it. GDI-based: GetGlyphIndicesW probing on a private DC with BMP/SMP result caches, per-font run splitting, clipped ExtTextOutW drawing. Configured via kitty.ini [FontFallback]: active (master switch, default yes), fallback (font list), override (Unicode-range pinning), log/logfile. Ported from upstream PR cyd01#555 (author blreay), adapted for the 0.84 tree: configuration flows through LoadParameters instead of the classic ReadParameter, and the window.c integration reduces to a single hook around general_textout - text fully covered by the primary font takes the unchanged fast path, keeping RTL handling and Windows' built-in font linking as before. Two robustness fixes over the PR: a raster (non-TrueType) primary font disables the subsystem instead of sending every glyph to the first fallback font, and run-table overflow draws the tail with the primary font instead of dropping it. Built-in fallback list curated to fonts that ship with Windows 10/11. The PR's PUA double-width (term_char_width) change is deliberately not ported: it affects cell layout, not just rendering.
hknet
added a commit
to hknet/KiTTY
that referenced
this pull request
Jul 19, 2026
CHANGELOG and KNOWN-ISSUES for the missing-glyph font fallback (from upstream PR cyd01#555) and the test_conf default-expectation round, plus version bumps.
hknet
added a commit
to hknet/KiTTY
that referenced
this pull request
Aug 3, 2026
When the primary terminal font lacks a glyph (box drawing, Nerd Font icons, CJK, symbols), probe a configurable list of fallback fonts and draw the character from the first font that has it. GDI-based: GetGlyphIndicesW probing on a private DC with BMP/SMP result caches, per-font run splitting, clipped ExtTextOutW drawing. Configured via kitty.ini [FontFallback]: active (master switch, default yes), fallback (font list), override (Unicode-range pinning), log/logfile. Ported from upstream PR cyd01#555 (author blreay), adapted for the 0.84 tree: configuration flows through LoadParameters instead of the classic ReadParameter, and the window.c integration reduces to a single hook around general_textout - text fully covered by the primary font takes the unchanged fast path, keeping RTL handling and Windows' built-in font linking as before. Two robustness fixes over the PR: a raster (non-TrueType) primary font disables the subsystem instead of sending every glyph to the first fallback font, and run-table overflow draws the tail with the primary font instead of dropping it. Built-in fallback list curated to fonts that ship with Windows 10/11. The PR's PUA double-width (term_char_width) change is deliberately not ported: it affects cell layout, not just rendering.
hknet
added a commit
to hknet/KiTTY
that referenced
this pull request
Aug 3, 2026
CHANGELOG and KNOWN-ISSUES for the missing-glyph font fallback (from upstream PR cyd01#555) and the test_conf default-expectation round, plus version bumps.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
新功能
字体回退(Font Fallback)系统
kitty.ini中增加[FontFallback]节:Fallback=字体名1,字体名2,...— 逗号分隔的备选字体列表(用户配置的字体优先于内置默认字体)。Override=U+E000-U+F8FF:指定字体;...— 为特定 Unicode 区间强制指定字体。Log=info|debug|trace— 诊断日志(默认关闭,日志文件位于<exe目录>/fontfallback.log,单文件 10 MB 滚动)。GetGlyphIndicesW逐码点探测缺失字形,将文本按字体拆分为渲染段,每个渲染段剪裁到对应单元格内绘制。Override=显式指定。Bug 修复
64 位 URL 下划线/SSH 崩溃(正则表达式 ABI 不匹配)
http://w后整个屏幕被下划线填满;部分 Ubuntu 主机的 SSH 连接段错误(Segfault)。libregex_64.a(GNULIB)采用 LP64 ABI 编译(unsigned long= 8 字节),而 MinGW-w64/Win64 采用 LLP64 ABI(unsigned long= 4 字节)。结构体re_pattern_buffer的字段re_nsub和rm_eo被读取到错误的字节偏移,导致 URL 检测循环无限执行,link_regions[]溢出至 580 条记录,最终栈破坏。regex/regex.h中添加#if defined(_WIN64)的条件 typedef 修正 ABI,并在url/urlhack.c中将regmatch_t数组扩容至re_nsub + 1。64 位 URL 下划线蔓延
GCC 13 编译修复
-fno-common,移除了跨编译单元的重复全局变量声明。immintrin.h中损坏的 AVX-512 intrinsic 头文件包含路径。优化与基础设施
构建系统
build.sh默认使用-j$(nproc)。base64、bcrypt、blocnote、jpeg、md5、mini、regex)重命名为xxx_32.a/xxx_64.a,Makefile 通过LIBSUFFIX变量按架构选取,杜绝 64构建覆盖 32 位库的隐患。
build.sh在 32 位构建前自动检测并修复上一次 64 位构建可能造成的.a污染。调试与测试基础设施
scripts/setup-wine-debug-env.sh):一键安装 wine32/wine64、Xvfb、xdotool、xpra、gdb、winedbg、puttygen、tesseract-OCR。scripts/build-and-test.sh):清理 → 编译 → Xvfb + wine 启动 → SSH 连接 localhost → OCR 验证输出。32 位、64 位均已通过。docs/superpowers/目录下包含设计规范、实施计划、两份详细复盘和一份 wine 调试操作手册。New Features
Font Fallback System
[FontFallback]section tokitty.ini:Fallback=Font1,Font2,...— comma-separated fallback font list (user-configured fonts are tried first, before built-in defaults).Override=E000-F8FF:FontName;...— force-assign a specific font for a Unicode range.Log=info|debug|trace— diagnostic logging (default OFF, log at<exe-dir>/fontfallback.log, 10 MB rollover).GetGlyphIndicesWto detect missing glyphs per code-point, then splits the text into per-font runs and paints each run clipped to its cell.Override=to assign them.Bug Fixes
64-bit URL/SSH Crash (regex ABI mismatch)
http://won 64-bit KiTTY caused the entire screen to fill with underlines; some SSH connections to Ubuntu hosts segfaulted.libregex_64.a(GNULIB) was built with LP64 ABI (unsigned long= 8 bytes), while MinGW-w64/Win64 uses LLP64 (unsigned long= 4 bytes). This disagreement shiftedre_nsub/rm_eoto the wrong byte offsets, making the URL-finder loop infinitely, overflowinglink_regions[]and clobbering the stack.#if defined(_WIN64)ABI-correcting typedefs inregex/regex.h, and resized theregmatch_tbuffer tore_nsub + 1inurl/urlhack.c.64-bit URL Underline Bleed
GCC 13 Build Fixes
-fno-commonis now the default in GCC 13. Removed duplicate global-variable declarations across translation units.immintrin.his now worked around.Optimizations & Infrastructure
Build System
build.shnow uses-j$(nproc)by default.base64,bcrypt,blocnote,jpeg,md5,mini,regex) renamed toxxx_32.a/xxx_64.a. The Makefile selects the correct suffix via theLIBSUFFIXvariable, eliminating the trap where a 64-bit build would silently overwrite a 32-bit archive.build.shdetects if a previouscross64polluted the 32-bit.afiles and repairs them before a freshcrossbuild.Debugging & Testing Infrastructure
scripts/setup-wine-debug-env.sh): one-shot script to install wine32/wine64, Xvfb, xdotool, xpra, gdb, winedbg, puttygen, tesseract-OCR.scripts/build-and-test.sh): clean → build → Xvfb + wine launch → SSH to localhost → OCR verify. Both 32-bit and 64-bit pass.docs/superpowers/.