Emacs, unicode, escape-последовательности мыши xterm и широкие терминалы

Краткая версия: при использовании режима xterm-mouse-mode emacs, Кто-то (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, send:

\40+x (x < 96)  
\300+X/64 \200+X%64 (otherwise)  

Мы должны добавить единицу к заданной позиции x из emacs, потому что позиции в xterm начинаются с единицы, а не с нуля. Следовательно, появляется волшебное число x = 95, потому что оно закодировано как «\ 300 \ 200» - первое экранированное число. Кто-то (emacs? Bash? Xterm?) Рассматривает их как управляющие последовательности "C0" из ISO 2022 . Начиная с x = 159, мы переходим к последовательностям "C1" (\ 301 \ 200), которые также являются частью ISO 2022.

Проблемы возникают с \ 302 последовательностями, что соответствует текущему пределу x = 223. Несколько лет назад я смог расширить этот хак, чтобы вручную перехватывать последовательности \ 302 и \ 303, что позволило решить эту проблему. Перенесемся на несколько лет вперед, и сегодня я обнаружил, что застрял на x = 223, потому что кто-то заменяет эти последовательности на \ 0.

Итак, там, где я ожидал, что щелкнув строку 1, столбец 250 даст

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

Вместо отчетов emacs (для любого col> 223)

ESC [ M SPC C-@ ! ESC [ M # C-@ !

Я подозреваю, что виновата поддержка Unicode / UTF-8. Некоторые поиски показывают, что стандарт Unicode разрешал последовательности C0 и C1 как часть UTF-8 до ноября 2000 года , и я думаю, что кто-то не получил памятку (к счастью). Однако \ 302 \ 200 - \ 302 \ 237 - это управляющие последовательности Unicode , так что кто-то проглатывает их (делая с ними неизвестно что!) и вместо этого возвращает \ 0.

Еще несколько вопросов:
- Кто это тот, кто перехватывает коды до того, как они достигнут буфера потерь emacs?
- Если на самом деле речь идет только об управляющих последовательностях, почему символы после \ 302 \ 237, которые являются кодировками UTF-8 печатного Unicode, также возвращаются как \ 0?
- Что заставляет emacs решать, отображать ли потери в виде символов Юникода или восьмеричных escape-последовательностей, и почему они не совпадают? Например, мой самодельный cygwin emacs 23.2.1 (xterm 229) сообщает \ 301 \ 202 для столбца 161, но мой emacs 22.3.1 (xterm 215), предоставленный rhel5.5, сообщает "Â" (латинское A с циркумфлексом) , что на самом деле \ 303 \ 202 в UTF-8!

Обновление:

Вот патч для 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 ... patch заставляет 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, запутаются этим патчем, поскольку escape-последовательности мыши становятся длиннее. Однако эти приложения ужасно не соответствуют текущему xterm, потому что позиции мыши с x> 95 выглядят как коды utf-8, но не являются. Я бы создал новый режим мыши для xterm, но некоторые приложения (экран GNU!) Отфильтровывают неизвестные escape-последовательности. Emacs - единственное приложение для мыши с терминалом, которое я использую, поэтому я считаю патч чистым выигрышем, но YMMV.

13
задан Ryan 26 August 2010 в 15:08
поделиться

2 ответа

Хорошо, разобрался. На самом деле есть две проблемы.

Во-первых, некоторые погружения в исходники показывают, что xterm обрезает активную с помощью мыши область окна до 223x223 символов и отправляет 0x0 для всех остальных позиций.

Во-вторых, emacs-23 поддерживает UTF-8 и сбивается с толку, когда события мыши имеют x> 160 и y> 94; в этих случаях кодировка xterm для x и y выглядит как двухбайтовый символ UTF-8 (например, 0xC2 0x80), и в результате последовательность мыши кажется короткой на один символ.

Я работаю над патчем для xterm, чтобы события мыши выдавали UTF-8 (который одновременно разблокировал бы emacs-23 и разрешил бы терминалы до 2047x2047), но я еще не уверен, как это будет происходить.

4
ответ дан 2 December 2019 в 00:44
поделиться

Я думаю, что проблема, из-за которой ваш обходной путь (и исправление исходного кода, которое было включено в один из выпусков v22) перестала работать в 23.2, кроется в самом Emacs. 23.1 может обрабатывать щелчки мыши после столбца 95 с использованием urxvt, gnu screen, putty или iTerm, но 23.2 не может. Установка всего на latin-1 не имеет значения. 23.1 имеет тот же код в xt-mouse.el. Однако src / lread.c и src / character.h изменились, и с первого взгляда я могу предположить, что ошибка где-то там. Что касается того, что происходит после столбца 223, я понятия не имею.

Для всех, кого раздражает регрессия xt-mouse в 23.2, вот модифицированная версия xterm-mouse-event-read, которая работает с щелчками мыши до столбца 222 (благодарность Райану за обработку переполнения> 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). Он снова работает в 23.2 до столбца 222, но ему не хватает обработки переполнения> 222, которую предложил Райан:

(defun xterm-mouse-event-read ()
  (let ((c (read-char)))
    (if (> c #x3FFF80)
        (+ 128 (- c #x3FFF80))
      c)))
2
ответ дан 2 December 2019 в 00:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: