Emacs、Unicode、xterm鼠标转义序列和宽终端

13

简短版:使用emacs的xterm-mouse-mode时,某个东西(emacs?bash?xterm?)拦截了xterm的控制序列,并将它们替换为\0。在宽屏幕上这很麻烦,因为只有前223列具有鼠标功能。

罪魁祸首是什么,我该如何解决?

据我所知,这与Unicode / UTF-8支持有关,因为5-6年前我上次使用大型显示器时这不是问题。

详细信息如下...

谢谢!

Emacs xterm-mouse-mode处理从x=95开始的鼠标点击存在已知的弱点。一个解决方法,被最新版本的emacs采用,将问题推迟到x=223处。

几年前,我发现xterm在7位八进制中编码位置。给定要编码的位置“x”,其中X=x-96,发送:

\40+x (x < 96)  
\300+X/64 \200+X%64 (otherwise)  
我们需要在emacs中给定的x位置上加1,因为xterm中的位置从1开始,而不是从0开始。因此,魔术数字x=95就出现了,因为它被编码为“\300\200”——第一个转义数字。某个人(emacs?bash?xterm?)将它们视为ISO 2022中的“C0”控制序列。从x=159开始,我们切换到“C1”序列(\301\200),这也是ISO 2022的一部分。
\302序列出现问题,对应当前的x=223限制。几年前,我能够手动扩展这个hack以拦截\302和\303序列,从而解决了这个问题。快进几年,今天我发现我又卡在x=223处,因为某个人正在用\0替换那些序列。
因此,当我期望点击第1行,第250列时会产生什么:

ESC [ M SPC \303\207 ! ESC [ M # \303\207 !

但是对于任何列数大于223的情况,emacs报告如下:

ESC [ M SPC C-@ ! ESC [ M # C-@ !
我怀疑是Unicode/UTF-8支持造成的问题。一些调查显示,Unicode标准在2000年11月之前允许C0和C1序列作为UTF-8的一部分,我猜某人没有收到这个备忘录(幸运的是)。然而,\302\200-\302\237是Unicode控制序列,因此某些人将它们吞掉了(不知道要做什么!)并返回\0。
更详细的问题如下:
- 谁是拦截代码并防止其进入emacs失效缓冲区的那个人?
- 如果真的只涉及控制序列,为什么在\302\237之后的字符(它们是可打印Unicode的UTF-8编码)也会返回\0?
- 是什么决定了emacs是以Unicode字符还是八进制转义序列来显示失效信息,并且为什么两者不匹配?例如,我的自制cygwin emacs 23.2.1(xterm 229)报告161列的\301\202,但我的rhel5.5附带的emacs 22.3.1(xterm 215)报告“”(带抑扬符的拉丁字母A),实际上是UTF-8的\ 303\202! 更新: 这是针对xterm-261的补丁,使其以utf-8格式发出鼠标位置:
diff -r button.c button.utf-8-fix.c
--- a/button.c  Sat Aug 14 08:23:00 2010 +0200
+++ b/button.c  Thu Aug 26 16:16:48 2010 +0200
@@ -3994,1 +3994,27 @@
-#define MOUSE_LIMIT (255 - 32)
+#define MOUSE_LIMIT (2047 - 32)
+#define MOUSE_UTF_8_START (127 - 32)
+
+static unsigned
+EmitMousePosition(Char line[], unsigned count, int value)
+{
+    /* Add pointer position to key sequence
+     * 
+     * Encode large positions as two-byte UTF-8 
+     *
+     * NOTE: historically, it was possible to emit 256, which became
+     * zero by truncation to 8 bits. While this was arguably a bug,
+     * it's also somewhat useful as a past-end marker so we keep it.
+     */
+    if(value == MOUSE_LIMIT) {
+       line[count++] = CharOf(0);
+    }
+    else if(value < MOUSE_UTF_8_START) {
+       line[count++] = CharOf(' ' + value + 1);
+    }
+    else {
+       value += ' ' + 1;
+       line[count++] = CharOf(0xC0 + (value >> 6));
+       line[count++] = CharOf(0x80 + (value & 0x3F));
+    }
+    return count;
+}
@@ -4001,1 +4027,1 @@
-    Char line[6];
+    Char line[9]; /* \e [ > M Pb Pxh Pxl Pyh Pyl */
@@ -4021,2 +4047,0 @@
-    else if (row > MOUSE_LIMIT)
-       row = MOUSE_LIMIT;
@@ -4028,1 +4052,5 @@
-    else if (col > MOUSE_LIMIT)
+
+    /* Limit to representable mouse dimensions */
+    if (row > MOUSE_LIMIT)
+       row = MOUSE_LIMIT;
+    if (col > MOUSE_LIMIT)
@@ -4090,2 +4118,2 @@
-       line[count++] = CharOf(' ' + col + 1);
-       line[count++] = CharOf(' ' + row + 1);
+       count = EmitMousePosition(line, count, col);
+       count = EmitMousePosition(line, count, row);
希望这个补丁(或类似的补丁)将出现在未来版本的xterm中……该补丁使xterm可以直接与emacs-23配合使用(它假定为utf-8输入),并且还修复了xt-mouse.el的现有问题。要在emacs-22中使用它,需要重新定义用于解码鼠标位置的函数(新定义也可在emacs-23中正常使用):
(defadvice xterm-mouse-event-read (around utf-8 compile activate)
  (setq ad-return-value
        (let ((c (read-char)))
          (cond
           ;; mouse clicks outside the encodable range produce 0
           ((= c 0) #x800)
           ;; must convert UTF-8 to unicode ourselves
           ((and (>= c #xC2) (< emacs-major-version 23))
            (logior (lsh (logand c #x1F) 6) (logand (read-char) #x3F)))
           ;; normal case
           (c) ) )))

将defun作为.emacs的一部分在您登录的所有计算机上进行分发,并在任何您工作的计算机上打补丁xterm。大功告成!

警告:使用xterm鼠标模式但不把其输入视为utf-8的应用程序会因此补丁而混乱,因为鼠标转义序列变得更长了。但是,这些应用程序在当前的xterm中也会因x > 95的鼠标位置看起来像utf-8代码而导致糟糕的错误。我会为xterm创建一个新的鼠标模式,但某些应用程序(gnu screen!)会过滤未知的转义序列。Emacs是我使用的唯一终端鼠标应用程序,所以我认为该补丁是净赢家,但您的情况可能有所不同。


2
我没有这么宽的终端,也不是Emacs用户,但这个问题很棒。由一个新用户提出,详细而且简明扼要。我不认为我能帮你解决这个问题(需要相当长的时间来重现和分析你的设置),但你尝试使用“script”(工具)来存储终端仿真器发送到应用程序的确切字节了吗?(哦,也许这个问题更适合Power User...) - scy
1
嗯。脚本看到的也是一样的。我还写了一个快速的C程序来启用鼠标模式,然后将它的stdin转换成八位流。$ xterm -e echo-octets即使直接从xterm中运行它(如上所示),即使我能想到的全部设置为7位,鼠标点击仍然被限制在\377或更低的范围内——这听起来很像8位编码。是编译器问题吗?不管怎样,这不是emacs……如果以上推测正确,希望也不是bash。 - Ryan
4个回答

5

xterm-262 添加了上面内联的补丁,但是这个补丁在设计上相当有问题。 Rxvt-unicode 的开发人员意识到了这一点,并添加了另一个更好的扩展来报告鼠标坐标。

现在我正在努力推广此功能。 Rxvt-unicodeiTerm2 已经支持这两个扩展。我为 xterm 创建了补丁(以支持 urxvt 扩展),并为 gnome-terminalkonsoleputty 创建了补丁,以支持这两个新扩展。至于应用程序方面,我已经为 Midnight Commander 添加了对 urxvt 扩展的支持。

请加入我的行列,并努力说服更多的终端开发人员和应用程序实现这些扩展(至少是 urxvt 扩展,因为其他扩展无法被应用程序正确地自动识别)。

有关技术细节和进一步指针,请参见http://www.midnight-commander.org/ticket/2662


4

好的,我已经弄清楚了。实际上有两个问题。

首先,一些源代码探索显示,xterm将窗口的鼠标启用区域剪切为223x223个字符,并对所有其他位置发送0x0。

其次,emacs-23支持UTF-8并且会因x>160和y>94的鼠标事件而混淆;在这些情况下,xterm的x和y的编码看起来像是一个双字节的UTF-8字符(例如0xC2 0x80),结果鼠标序列似乎少了一个字符。

我正在为xterm制作补丁,使鼠标事件发出UTF-8(这将解决emacs-23的混淆问题并允许多达2047x2047个终端),但我还不确定它的效果如何。


2
我认为导致你的解决方法(以及包含在v22版本之一的上游修复)在23.2中停止工作的问题出现在Emacs本身。 23.1可以使用urxvt,gnu screen,putty或iTerm,在第95列之后处理鼠标点击,但23.2无法。 将所有内容设置为latin-1没有任何区别。 23.1在xt-mouse.el中具有相同的代码。然而,src/lread.c和src/character.h已更改,并且乍一看,我会猜测错误就在那里。至于在第223列之后发生了什么,我不知道。
为了让其他受xt-mouse回归问题困扰的人受益,这是xterm-mouse-event-read的修改版本,可处理鼠标点击直到222列(感谢Ryan提供了>222的溢出处理,我的原始修复缺少)。 这可能在23.1或之前的版本中无效。
(defun xterm-mouse-event-read ()
  (let ((c (read-char)))
    (cond ((= c 0) #x100)  
       ; for positions past col 222 emacs just delivers
       ; 0x0, best we can do is stay at eol 
      ((= 0 (logand c (- #x100))) c) 
      ((logand c #xff))))) 

编辑:这是来自 Emacs 24(bzr head)的版本。它能够再次在23.2中工作,但仅限于第222列,缺少 Ryan 建议的 >222 溢出 eol 处理:

(defun xterm-mouse-event-read ()
  (let ((c (read-char)))
    (if (> c #x3FFF80)
        (+ 128 (- c #x3FFF80))
      c)))

1

虽然xterm现在可以通过补丁以utf-8模式工作,但是这种utf-8 hack在任何其他语言环境下都会以最糟糕的方式崩溃,因为Unicode字符将被丢弃,除非它们可表示。

rxvt-unicode在9.09版本之后具有1015模式,发送形式为“ESC [ code; x; y M”的回复,使用十进制数字。这样做的好处是不需要应用程序进行任何探测,并且可以在非utf-8语言环境中工作。


感谢您的更新。实际上,原则上我更喜欢rxvt的解决方案(它应该是vt100最初应该做的),但它会破坏太多其他应用程序。面临有时会破坏一些应用程序或强制所有应用程序重写的选择,我选择了较少侵入性的那个。话虽如此,事实证明我仍然不得不重新编写gnu screen中的鼠标处理方式,所以...... - Ryan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接