Skip to content

Font Fallback Support and 64bit bug fix - #555

Open
blreay wants to merge 38 commits into
cyd01:masterfrom
blreay:master
Open

blreay wants to merge 38 commits into
cyd01:masterfrom
blreay:master

Conversation

@blreay

@blreay blreay commented Jun 23, 2026

Copy link
Copy Markdown

新功能

字体回退(Font Fallback)系统

  • 终端文本包含主字体无法渲染的字符(制表符、CJK、符号……)时,自动切换到配置的备选字体。
  • 配置方式:在 kitty.ini 中增加 [FontFallback] 节:
    • Fallback=字体名1,字体名2,... — 逗号分隔的备选字体列表(用户配置的字体优先于内置默认字体)。
    • Override=U+E000-U+F8FF:指定字体;... — 为特定 Unicode 区间强制指定字体。
    • Log=info|debug|trace — 诊断日志(默认关闭,日志文件位于 <exe目录>/fontfallback.log,单文件 10 MB 滚动)。
  • 技术实现:通过 GetGlyphIndicesW 逐码点探测缺失字形,将文本按字体拆分为渲染段,每个渲染段剪裁到对应单元格内绘制。
  • 已知限制:补充平面(SMP)码点(如 emoji ≥ U+1F600)的字形探测不可靠,请使用 Override= 显式指定。

Bug 修复

64 位 URL 下划线/SSH 崩溃(正则表达式 ABI 不匹配)

  • 现象:64 位版本输入 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_nsubrm_eo 被读取到错误的字节偏移,导致 URL 检测循环无限执行,link_regions[] 溢出至 580 条记录,最终栈破坏。
  • 修复:在 regex/regex.h 中添加 #if defined(_WIN64) 的条件 typedef 修正 ABI,并在 url/urlhack.c 中将 regmatch_t 数组扩容至 re_nsub + 1

64 位 URL 下划线蔓延

  • 已检测到的 URL 的下划线属性现在正确终止于链接末尾,不再蔓延到后续文本。

GCC 13 编译修复

  • 多重定义错误:GCC 13 默认启用 -fno-common,移除了跨编译单元的重复全局变量声明。
  • i686 构建失败:绕过了 MinGW immintrin.h 中损坏的 AVX-512 intrinsic 头文件包含路径。

优化与基础设施

构建系统

  • 并行编译build.sh 默认使用 -j$(nproc)
  • 静态库管理:自带库(base64bcryptblocnotejpegmd5miniregex)重命名为 xxx_32.a / xxx_64.a,Makefile 通过 LIBSUFFIX 变量按架构选取,杜绝 64
    构建覆盖 32 位库的隐患。
  • 自动修复build.sh 在 32 位构建前自动检测并修复上一次 64 位构建可能造成的 .a 污染。

调试与测试基础设施

  • Wine 调试环境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 位均已通过。
  • CLAUDE.md:为后续 AI 辅助开发准备的全面上手文档(构建、调试、架构全景、ABI 陷阱)。
  • 工程文档docs/superpowers/ 目录下包含设计规范、实施计划、两份详细复盘和一份 wine 调试操作手册。

New Features

Font Fallback System

  • Terminal text that uses characters the primary font cannot render (box-drawing, CJK, symbols...) now automatically falls back to configured secondary fonts.
  • Configuration: add [FontFallback] section to kitty.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).
  • The fallback probe uses GetGlyphIndicesW to detect missing glyphs per code-point, then splits the text into per-font runs and paints each run clipped to its cell.
  • Limitation: supplementary-plane codepoints (emoji ≥ U+1F600) cannot be probed — use Override= to assign them.

Bug Fixes

64-bit URL/SSH Crash (regex ABI mismatch)

  • Symptom: typing http://w on 64-bit KiTTY caused the entire screen to fill with underlines; some SSH connections to Ubuntu hosts segfaulted.
  • Root cause: the vendored 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 shifted re_nsub / rm_eo to the wrong byte offsets, making the URL-finder loop infinitely, overflowing link_regions[] and clobbering the stack.
  • Fix: added #if defined(_WIN64) ABI-correcting typedefs in regex/regex.h, and resized the regmatch_t buffer to re_nsub + 1 in url/urlhack.c.

64-bit URL Underline Bleed

  • Underline attributes on detected URLs now stop cleanly at the link boundary instead of bleeding into following text.

GCC 13 Build Fixes

  • Multiple-definition errors: -fno-common is now the default in GCC 13. Removed duplicate global-variable declarations across translation units.
  • i686 build break: a broken AVX-512 intrinsic include path in MinGW's immintrin.h is now worked around.

Optimizations & Infrastructure

Build System

  • Parallel compilation: build.sh now uses -j$(nproc) by default.
  • Vendored libraries (base64, bcrypt, blocnote, jpeg, md5, mini, regex) renamed to xxx_32.a / xxx_64.a. The Makefile selects the correct suffix via the LIBSUFFIX variable, eliminating the trap where a 64-bit build would silently overwrite a 32-bit archive.
  • Auto-restore: build.sh detects if a previous cross64 polluted the 32-bit .a files and repairs them before a fresh cross build.

Debugging & Testing Infrastructure

  • Wine debugging environment (scripts/setup-wine-debug-env.sh): one-shot script to install wine32/wine64, Xvfb, xdotool, xpra, gdb, winedbg, puttygen, tesseract-OCR.
  • Automated smoke test (scripts/build-and-test.sh): clean → build → Xvfb + wine launch → SSH to localhost → OCR verify. Both 32-bit and 64-bit pass.
  • CLAUDE.md: comprehensive onboarding guide for future development sessions (build, debug, architecture overview, ABI traps).
  • Engineering docs: design spec, implementation plan, two detailed postmortems, and a wine-debug runbook in docs/superpowers/.

chuyi-zhaoyong-zzy and others added 30 commits May 26, 2026 00:23
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.
…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.
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>
blreay and others added 8 commits May 29, 2026 11:51
…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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants