作者:Michael 文章来源:SUN中国技术社区
下载 项目源代码
在本系列的第一篇文章中,我们构建了一个简单的可在任何支持 TCP/IP 插槽的 MIDP 设备上运行的终端模拟器。它包含一个实现 Telnet 协议的 Connection 和一个经过定制来显示终端内容的 Canvas。在第二篇文章中,我假定您已经阅读了第一篇文章并熟悉了这些组件。
现在我们将进一步加强这个应用程序。首先,我们将通过添加一个更高级的终端类型来增加一些复杂性。然后我们将添加对用户输入的支持——注意大多数移动设备的各种限制。完成后,我们就能够使用这个应用程序来通过 Telnet 连接到远程服务器并运行许多种类的程序,而这一切都是从您的 MIDP 设备来进行的。
关于 ANSI 终端
在上一篇文章中我们实现了一个“哑巴”终端。它显示的仅仅是一个字符流,当进入的字符到达屏幕的边缘时就换行到下一行,在遇到一个换行符时就跳到下一行。虽然这种程度的交互性对于整代的命令行应用程序是足够的,但较复杂的软件则将屏幕作为一个整体来处理,这就需要编写和清除特定位置的字符以提供更好的用户体验。
这里有一段有趣的历史。在 20 世纪 70 年代,众多生产视频终端的厂商提供的屏幕操纵功能专有且各不相同,并以不兼容的方式来实现。要编写可利用多种这样的设备的面向屏幕的软件就很困难。
American National Standards Institute (ANSI) 参与进来。坚持其通过可互操作性来支持商业的要求,ANSI 介入并发布了标准 X3.64:Additional Controls for Use with American National Standard Code for Information Interchange。这个文件定义了现在所知的 ANSI 终端类型。它对将光标移动到屏幕上的特定位置、在光标位置插入和删除字符都规定了标准的命令序列。
最为重要的是命令序列定义自身,因为即使是未实现所有命令的终端也至少能够将不支持的命令识别为命令,并安全地忽视它们。这一进步使得软件开发商可以按照通用标准来编写,并确信他们的应用程序能够至少占用较小的容量来在很多厂商的设备上运行。ANSI 的故事是软件行业一个重复出现的主题的一个很好的例子:采用一种标准会极大地扩展可互操作性。
ANSI 终端标准是一种很像 Telnet 协议的协议。它定义了多个特殊的字符序列,使一个应用程序可区分要解释的命令和要显示到屏幕的数据。
ANSI 终端是我们将要模拟的终端类型。TelnetConnection 已经在其到达屏幕之前过滤出 Telnet 握手和协商;现在我们将增加另一个过滤器来过滤出并解释 ANSI 命令。因为这些命令是我们的终端的指令,实现该逻辑的最好的地方就是在 TelnetCanvas 类自身中。
终端转义序列如何工作?
当 Telnet 使用字节值 255 来以信号方式表示一个命令序列时,ANSI 使用 ASCII 转义序列,其值是 27。ANSI 将如下工作:
我们从输入读取一个字节。如果其值不是 27 (
ESC),则它不是一个命令∶我们将它直接送到应用程序并继续读取。我们读取下一个字节。如果其值不是 133 (
[),则它不是一个命令;我们将该字节后所跟的 27 直接送到应用程序并继续读取。我们将继续读取,直到读取到大于 63 的一个字节。这些字节形成一个字符串,其中含有命令的参数。最后一个字节(是 64 或更大)是命令代码。我们处理(或忽略)该命令并继续读取。
许多命令从官方来讲是标准的一部分。虽然完全模拟是一个值得的目标,我们仍将精力放在获得足够的功能来使众多软件可以接受地运行。我们将实现以下命令:
Cursor Control Sequences Erase Sequences AMove cursor up n lines@Insert n blank spacesBMove cursor down n linesJErase display: after cursor (n=0), before cursor (n=1), or entirely (n=2).CMove cursor forward n spacesDMove cursor backward n spacesKErase line: after cursor (n=0), before cursor (n=1), or entirely (n=2).GMove cursor to column xHMove cursor to column x, row yLInsert n new blank linesdMove cursor to row yMDelete n lines from cursorsSave current cursor positionPDelete n characters from cursoruReturn to saved cursor position