自计算早期以来,闪烁是一种常见的做法,尤其是对于光标。
但是,当我运行strace
检查它们的系统调用时,终端仿真器konsole
和 shellbash
都不要注册任何类型的计时器(通过timer_settime()
)或间隔计时器(通过setitimer()
)。同时,这些程序不能使用自旋锁来等待一定的时间。
真正的终端能够做到这一点,因为它们的控制器可以理解眨眼退出控制序列。但是图形监视器显然不能做这些事情。
那么这些程序如何让它们的文本闪烁,尤其是在图形环境中呢?文本也可以在非 X 图形终端中闪烁(就像你按Ctrl+Alt+F2
)。
闪烁的终端光标是如何发明的?这个问题展示了它们被发明的原因,以及真实终端如何实现它们的技术细节。
您的问题混淆了两个概念:闪烁的光标和闪烁的文本。
GNOME 终端 (VTE) 支持闪烁光标很长时间了,我在 5 年前添加了闪烁文本支持。让我分享三个关于闪烁文本支持的有趣细节,希望你会喜欢它们:)
这不是您问题的答案(我已经单独发布了一个实际答案),这只是一个额外的横向评论。
一个有趣的方面是:究竟什么时候闪烁文本?
文本闪烁速度应该与光标闪烁速度相关联,还是不同的属性?我们以同样的速度前进,劫持了那个选项。IIRC
konsole
对两者使用不同的频率。多个带有闪烁文本的终端是否应该同时闪烁?我们的结论是,是的,他们应该这样做,否则会很烦人(好像闪烁的文字一开始并不烦人)。何时“打开”或“关闭”文本与系统时钟相关联。
每次按键后,闪烁的光标总是以完全“开启”状态重新启动。我们应该保持这种行为吗?结论是肯定的,保持这种行为。
光标和文本应该同步闪烁吗?希望很容易看出我们不能同时拥有所有东西,其中一个可能的标准必须放宽。所以不,这两个不同步。
这不是回答这些问题的唯一可能方式,而是我们发现最有意义的方式。
\e[5m
为了实现闪烁文本支持,我们何时看到相应的或转义序列并不重要\e[25m
。滚动条可能被设置到它的非默认位置,显示回滚内容。显然,对于每个单元格,我们需要跟踪所有颜色和装饰属性,包括是否使该单元格闪烁。重要的是此时当前视口内是否有这样的单元格。VTE 有时需要重绘整个屏幕,但有时只重绘屏幕的一部分(内容发生变化的地方)。
其中一项标准是:如果视口中没有闪烁的文本(在绝大多数情况下),我们不应该每隔 0.6 秒左右唤醒一次终端什么都不做(或者更糟:重新绘制相同的内容)。这对于节省能源、笔记本电脑电池寿命等很重要。
很长一段时间,我都在考虑周期性计时器。每当绘制闪烁文本时,启动一个周期性计时器,每次闪烁状态发生变化时都会启动该计时器。然后我们重新绘制屏幕(最好只绘制相关部分)。
但是然后:我们怎么知道什么时候停止这个周期性计时器?很长一段时间我都没有看到一个干净、简单的答案。
然后一分钱掉了下来。让我们不要在周期性计时器中思考。让我们只安装一个一次性定时器,目前屏幕上闪烁的文本需要改变它的状态。如果视口中仍然有闪烁的文本,那么重绘将安装下一个一次性计时器,依此类推,直到那里的内容不再闪烁。这种坚如磐石且超级简单的解决方案的代价是在最后进行了一次额外的不必要的重绘,这是没人关心的(除非您调试或 strace,并在存在
poll()
闪烁文本的情况下研究这些调用)。许多人希望看到闪烁的文本,如果那是应用程序发出的内容。许多人讨厌这个功能并且永远不想看到闪烁的文字。据推测,类似于光标仅在聚焦终端中闪烁的方式,许多人希望仅在聚焦终端中看到闪烁的文本。
然而,GNOME 终端还提供了第四个不寻常的选项:只在未聚焦的窗口中闪烁文本。这是我的想法,思考过程是:眨眼大概是用来把你的注意力吸引到真正出乎意料和令人震惊的事情上。在您专注的愿景中,这不是必需的,并且可能会分散注意力(例如,如果您正在尝试解决您收到警报的问题)。然而,你的周边视觉对变化更敏感,比如眨眼,而不是仅仅打印一次静态警报。
我们不在我们的软件中进行分析,不发送统计数据(这会引起愤怒),所以我没有关于这四个选项受欢迎程度的信息,特别是如果有人发现这个“仅在不专心时”选项有用。
我已经知道
strace
了gnome-terminal-server
,这是 GNOME 终端的实际过程。当空闲时,只是闪烁光标,它驻留在一个
poll(..., 598)
或类似的内核调用中,即poll()
超时时间略短于 0.6 秒。(GNOME 的默认完整闪烁周期为 1.2 秒,因此每个“开”或“关”状态为 0.6 秒。这会缩短上次实际工作量,例如重新绘制光标区域。)这
poll()
会一直等到给定文件描述符之一有活动,或者计时器超时,或者信号到达。这
poll()
不是“手动”实现的,而是 VTE(GNOME Terminal 和许多其他终端后面的终端仿真小部件)在 GLib 的主循环中注册了一个事件处理程序,而 GLib 负责实际的底层实现。由 GLib 决定使用什么方法,例如它可以使用select
, 或epoll
withtimerfd
,或者可能还有其他选择。我在'ing时看到几乎相同的
poll()
调用,而且我怀疑大多数其他终端仿真器也会做类似的事情。strace
konsole
在终端(Bash 或其他)上运行的程序仅输出请求闪烁的适当转义码,但并不参与实际使其在此之后发生。打印转义符的程序可能早已消失,而闪烁的文本在终端上仍然可见。