调整 xterm 大小时,getch() 不返回 KEY_RESIZE [英] KEY_RESIZE not returned from getch() when xterm is resized

查看:77
本文介绍了调整 xterm 大小时,getch() 不返回 KEY_RESIZE的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在带有标准 readline 和 curses 模块的 Debian Linux 9 机器上使用 Python 3.7.以下代码在 xterm 中运行并调整 xterm 大小时应输出True":

I am using Python 3.7 on a Debian Linux 9 box with the standard readline and curses modules. The following code should output 'True' when run inside an xterm and the xterm is resized:

import readline
import os
import curses

terminal_resized = False

def main(stdscr):
    global terminal_resized
    ch = stdscr.getch()
    if ch == curses.KEY_RESIZE:
        terminal_resized = True

os.unsetenv('LINES')
os.unsetenv('COLUMNS')

curses.wrapper(main)
print(terminal_resized)

但是,输出为False",表明对 getch() 的调用未返回 KEY_RESIZE.实际上,它返回的是 -1.

However, the output is 'False', indicating that the call to getch() is not returning KEY_RESIZE. Indeed, it is returning -1 instead.

请注意,如果我不导入 readline 模块,代码将按预期工作.

Note that the code works as expected if I don't import the readline module.

在谷歌搜索此问题的解决方案时,我遇到了 2016 年的一篇帖子,指出在导入 readline 和 curses 模块时存在冲突.基本上,readline 模块设置了 'LINES' 和 'COLUMNS' 环境变量,这会干扰 ncurses 的内置 SIGWINCH 信号处理程序,它最终负责 getch() 在调整终端大小时返回 KEY_RESIZE.这就是为什么我在那里调用 unsetenv() 的原因.

In Googling for solutions to this problem, I encountered a post from 2016 indicating that there's a conflict when importing both the readline and curses modules. Basically, the readline module sets the 'LINES' and 'COLUMNS' environment variables and this interferes with the built-in SIGWINCH signal handler of ncurses which is what is ultimately responsible for getch() returning KEY_RESIZE when the terminal is resized. This is the reason why I have those calls to unsetenv() in there.

然而,那些 unsetenv() 调用显然在 2019 年没有任何影响.确实,当我在导入 readline 后尝试打印所有环境变量时,我在输出中看不到对LINES"或COLUMNS"的引用.尽管如此,我还是将那些对 unsetenv() 的调用放在了那里,看看它是否会做任何有用的事情.

However, those unsetenv() calls apparently have no effect in 2019. Indeed, when I try to print out all of the environment variables after importing readline I see no reference to 'LINES' or 'COLUMNS' in the output. I put those calls to unsetenv() in there anyway, though, to see if it would do anything useful.

有谁知道如何让curses getch() 方法返回KEY_RESIZE,就像在Python 3.7 中也导入readline 模块时应该返回的那样?

Does anybody know how to get the curses getch() method to return KEY_RESIZE like it is supposed to when the readline module is also imported in Python 3.7?

推荐答案

在使用 strace 的快速检查中,我可以看到某些东西将 SIGWINCH 处理程序重置回 SIG_DFL(无动作)在 ncurses 为 SIGWINCH 设置其处理程序之后.readline 的符号表有这些相关的入口点:

In a quick check with strace, I can see that something resets the SIGWINCH handler back to SIG_DFL (no action) after ncurses sets its handler for SIGWINCH. The symbol table of readline has these relevant entrypoints:

_rl_block_sigwinch
_rl_redisplay_after_sigwinch
_rl_release_sigwinch
_rl_sigwinch_resize_terminal
rl_catch_sigwinch

readline 笔记的文档

The documentation for readline notes

    o A new variable, rl_catch_sigwinch, is available to application
      writers to indicate to readline whether or not it should install its
      own signal handler for SIGWINCH, which will chain to the calling
      applications's SIGWINCH handler, if one is installed;

然而,阅读 libpython3.5 的源代码,似乎开发者没有考虑到这一点:

However, reading the source for libpython3.5, it appears that the developer did not take that into account:

/* Helper to initialize GNU readline properly. */

static void
setup_readline(readlinestate *mod_state)
{
...
    rl_readline_name = "python";
    /* Force rebind of TAB to insert-tab */
    rl_bind_key('\t', rl_insert);
    /* Bind both ESC-TAB and ESC-ESC to the completion function */
    rl_bind_key_in_map ('\t', rl_complete, emacs_meta_keymap);
    rl_bind_key_in_map ('\033', rl_complete, emacs_meta_keymap);
#ifdef HAVE_RL_RESIZE_TERMINAL
    /* Set up signal handler for window resize */
    sigwinch_ohandler = PyOS_setsig(SIGWINCH, readline_sigwinch_handler);
#endif

This change in 2016 appears to be related to the problem you're seeing (offhand, it looks like a new problem was introduced without solving the old one). If the signal-handler added for readline doesn't chain over to the one in ncurses, then the latter's no longer used and ncurses cannot return KEY_RESIZE. Also—if readline sets up its handler first, ncurses won't set up its handler.

后者似乎是这种情况:import readline 调用设置信号处理程序的模块初始化.当 Python curses 包装器调用 initscr 时,会初始化 ncurses 信号处理程序.这在 PyInit__curses(在 import curses 上调用的函数)中没有完成,因为这会清除屏幕.或者,如果 newterm 被调用(这不会清除屏幕),ncurses 将初始化其信号处理程序,但 Python 不会这样做.

The latter seems to be the case: the import readline calls the module initialization which sets up a signal handler. The ncurses signal handler is initialized when the Python curses wrapper calls initscr. That's not done in PyInit__curses (the function called on import curses) because that would clear the screen. Alternatively, ncurses would initialize its signal handler if newterm was called (which would not clear the screen), but Python doesn't do that.

可以通过加载 ncurses(或 ncursesw!)库并随后调用 newterm 来解决此问题通过 endwin,在 import 语句之前执行此操作.这似乎是很多工作.我建议打开一个错误报告.

You could work around this by loading the ncurses (or ncursesw!) library and calling newterm followed by endwin, doing that before the import statements. That seems like a lot of work. I'd recommend opening a bug report.

供参考:

  • #23735 - Readline not adjusting width after resize with 6.3
  • Signal Handlers, in ncurses documentation.

这篇关于调整 xterm 大小时,getch() 不返回 KEY_RESIZE的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆