Windows Terminal 下的非窗口 Emacs 可能存在的幽灵字符现象

今天测试 Emacs 在 Windows 终端的真彩 patch 时在 Windows Terminal 中观察到了在 *Help* Buffer 中滚动出现字符“拖影”或者叫“幽灵字符”的现象:

这一情况在非 Windows Terminal 的旧 cmd 中不会出现(下图使用了 conhost.exe 来启动 cmd)

不知道这是我个人机器的问题,还是 Windows Terminal 普遍存在的问题?

复现方式很简单,通过 C-h v cursor-type 打开一个变量的 *Help* buffer(或者是任意其他帮助文本足够长的变量或函数),并确保窗口采用上下分屏来使得 *Help* buffer 无法完全显示所有的帮助文本。接着将光标置于 *Help* buffer 第一行通过 C-n 向下移动到达 buffer 底部,最后观察 *Help* buffer 的显示效果截图即可。

注:可通过 (setopt scroll-step 1) 使滚动连续。

普遍的. 我的华硕和联想笔记本都有这种情况, Windows 11.

4 个赞

看来就是 WT 的问题了。

更有可能是 conpty (windows terminal 团队同时在维护的上游 project)的问题,猜测所有的 windows 平台的 terminal 都会有这个问题。

1 个赞

patch在哪里?我也来测试看看真彩色

bug#79298: patch: full color in windows terminal

邮件里的两个附件打在 master 上就行。编译后在 Win10/11 终端里试试 emacs -nw

用了可以发个 feedback 邮件

还有一种可能是widows terminal的gpu渲染有问题,gpu渲染这块每过一段时间就会有regression :joy:,如果有软件渲染的选项可以打开试试

1 个赞

根据文档的说法,我们可以设定 experimental.rendering.forceFullRepainttrue 来强制重绘整个屏幕的所有帧:Windows 终端呈现设置 | Microsoft Learn

不过这一文档的最后更新时间为 2023 年的十月,已经过去两年了 :rofl: ,我通过在 settings.json 添加这一项或 rendering.forceFullRepaint 似乎没有起到什么作用。WT 的图形化配置界面提供了选择 WARP 软渲染的选项,但是似乎也没有什么用。

相关资料还是太少了,这个可能有点类似 The windows terminal works abnormally. - Microsoft Q&A

虽然很狗血,不过我发现这是系统显示语言的问题,如果把设置里面的系统显示语言设置为英文则不会出现这个问题。

具体设置位置是:设置 → 时间和语言 → 语言和区域 → Windows 显示语言 → 选择 English (United States) 或其他英语。

考虑到引号(双引号,单引号)的全角半角字符使用了相同的 Unicode 码点,我怀疑即使设定 WT 的字体是等宽字体,WT 在系统为中文(不知道日文是什么情况)时获取的引号字符为全角宽度,但显示效果仍然是半角,这就导致了这个问题…

目前还在 bug-gnu-emacs 上和 Eli 继续讨论。

2 个赞

:seedling:,比我想象的要复杂一点,和 Emacs 在终端环境中确定字符宽度的相关代码有关,涉及到 cjk-ambiguous-chars-are-wide 这个选项。

简单来说就是如果 Emacs 在语言环境下设定的字符宽度与终端的字符宽度不匹配就会出现问题,完整的讨论我写了个博客: 在 Windows Terminal 的 Emacs 中规避中文环境下可能出现的全角符号导致的字符伪影问题

下面是我搓出来的配置,应该能保证默认情况下能够让终端中的 Emacs 与其终端保持统一:

(defun yy/check-w32-cjk-terminal ()
  (when (and (eq system-type 'windows-nt)
             (not (display-graphic-p)))
    (let* ((eat (process-attributes (emacs-pid)))
           (ppid (cdr (assq 'ppid eat)))
           res)
      (while-let ((_ (null res))
                  (_ (numberp ppid))
                  (attr (process-attributes ppid))
                  (proc (cdr (assq 'comm attr))))
        (when-let* ((a (assoc proc '(("WindowsTerminal.exe" . 0)
                                     ("conhost.exe" . 1)))))
          (setq res (cdr a)))
        (setq ppid (cdr (assq 'ppid attr))))
      (if res (and (= res 0) (setopt cjk-ambiguous-chars-are-wide nil))
        (pcase-let ((`(,a ,b ,c) (w32-version)))
          (unless (or (< a 10) (and (= a 10) (= b 0) (< c 19045))
                      (not (and (= a 10) (= b 0))))
            (let* ((fn (lambda (k) (w32-read-registry 'HKCU "Console\\%%Startup" k)))
                   (dc (funcall fn "DelegationConsole"))
                   (dt (funcall fn "DelegationTerminal"))
                   (flag (if (not (string-equal dc dt)) 2
                           (pcase dc
                             ("{00000000-0000-0000-0000-000000000000}" 0)
                             ("{B23D10C0-E52E-411E-9D5B-C09FDF709C7D}" 1)
                             (_ (error "Unrecognized Terminal Option"))))))
              (cond
               ((and (= c 19045) (= flag 2))
                (setopt cjk-ambiguous-chars-are-wide nil))
               ((and (< 19045 c 22621) (= flag 2))
                (setopt cjk-ambiguous-chars-are-wide nil))
               ((and (>= c 22621) (memq flag '(0 2)))
                (setopt cjk-ambiguous-chars-are-wide nil))))))))))
1 个赞