Здрасти.
Имеем следующий сетап:
Консоль Linux. Запущен демон gpm. Запущен tmux. Внутри tmux мы запускаем mc.
Баг: при обработке ввода mc рандомно подвисает. Например, нажимаем три раза стрелку вниз. Первые два раза ничего не происходит на экране (приложение не обновляет интерфейс). На третье нажатие отрабатывают все три.
Общей закономерности не просматривается. Бывает, нажатия доходят сразу. Бывает, доходит только пачка из 2, 3 или даже 4 нажатий.
В других сетапах баг не проявляется.
Если запускать mc с ключами -d или -x, баг тоже не проявляется.
Я покопал исходники, и вот что накопал.
Переменная use_mouse_p может принимать следующие значения: MOUSE_NONE, MOUSE_DISABLED, MOUSE_GPM, MOUSE_XTERM_NORMAL_TRACKING, MOUSE_XTERM_BUTTON_EVENT_TRACKING. При запуске программы она инициализирована значением MOUSE_NONE.
Если мы указываем ключ -d, она переинициализируется значением MOUSE_DISABLED. Если мы указываем ключ -x, mc считает, что имеет дело с xterm, и переинициализирует её одним из значений MOUSE_XTERM_*.
Затем в функции init_mouse() производится проверка значения. Если значение равно MOUSE_NONE, оно заменяется на MOUSE_GPM.
Затем в enable_mouse(), если use_mouse_p == MOUSE_GPM, производится установка соединения с gpm. Таким образом, mc пытается использовать мышь gpm, будучи запущенным из-под псевдотерминала (что бессмысленно).
Подвисание при обработке ввода происходит где-то в tty_get_event() или в is_idle() — я не разбирался, в чём именно проблема. На поверхностный взгляд, всё выглядит нормально. Возможно, баги находятся в библиотеке gpm. (Учитывая, каким говнокодом она написана, это наиболее вероятно.)
Как бы то ни было, на мой взгляд, некорректно пытаться использовать gpm из псевдотерминала. Прежде чем устанавливать соединение с gpm, следует проверить, работаем ли мы в виртуальном терминале Linux или в псевдотерминале pts.
Также, я думаю, что проверка переменной TERM не является правильным вариантом решения. TERM указывает на возможности терминала и понимаемые им коды, а gpm не является фичей терминала, это отдельный демон с отдельным API. Проверка значения ttyname() представляется более правильным решением. Если ttyname() возвращает имя вида /dev/tty*, использование gpm имеет смысл. Иначе — не имеет.
Я внёс следующие исправления в функцию init_mouse:
diff --git a/lib/tty/mouse.c b/lib/tty/mouse.c
index 511f2dd..060480a 100644
--- a/lib/tty/mouse.c
+++ b/lib/tty/mouse.c
@@ -84,8 +84,15 @@ init_mouse (void)
{
#ifdef HAVE_LIBGPM
case MOUSE_NONE:
- use_mouse_p = MOUSE_GPM;
- break;
+ {
+ char tname[64];
+ if (ttyname_r(0, tname, sizeof(tname)/sizeof(tname[0])) == 0 &&
+ strncmp(tname, "/dev/tty", sizeof("/dev/tty") - 1) == 0) /* check if stdin is a linux virtual terminal */
+ {
+ use_mouse_p = MOUSE_GPM;
+ }
+ break;
+ }
#endif /* HAVE_LIBGPM */
case MOUSE_XTERM_NORMAL_TRACKING:
После этого изменения баг не воспроизводится.