考虑到 Red Hat Linux 有软盘安装这一安装方式,安装代码运行于有限的资源环境中,特别是在有限的文件空间中。NEWT 的大小一开始就成为一个重要的问题。为了最小化它所占的空间,NEWT 的设计思想有下面几点:
- NEWT 由 C 语言写成,而不是C++
- NEWT 所有窗口的生成都是基于堆栈的数据结构,窗口的外观都是统一的模板,不由程序员修改
- 输入设备只支持终端键盘
NEWT 提供C语言的应用程序接口 (API),它不直接进行低级屏幕操作,是在 S-Lang 库上实现的。
在 NEWT 中,每一个可显示的项目称为组件。组件主要有两类:Form 组件与非 Form 组件。非 Form 组件是除 Form 以外的所有组件。Form 逻辑地组织各个组件成为一个功能集。Form 是一个容器组件,让其他组件都装在其上,有点类似 gtk 中的垂直盒 (Vbox) 和水平盒 (Hbox)。当一个应用准备从用户中获得输入,就要 run 一个 Form,这也类似于 gtk 中的 gtk_widget_show()。Form 组件可以包含其他任何组件,也包含其他的 Form 作为子 Form。
每一个组件都是由一个属于 newtComponent 数据类型的变量唯一标志,这一变量必须由生成该组件的函数建立,而不能由程序员自己建立。属于 newtComponent 数据类型的变量其实只是一个指针。
有三个函数是每个 NEWT 程序都必须用到的,头两个用于初始化系统。
int newtInit(void); void newtCls(void); |
newtInit() 必须是每个 NEWT 程序第一个调用的函数,用于初始化数据结构和重置终端于 raw 模式。大多数应用在 newtInit() 后立即调用 newtCls(),newtCls() 用于清除屏幕,这个函数不是必须的,但有助于优化屏幕。
当一个 NEWT 程序准备结束时,就要调用以下函数。
int newtFinished(void); |
newtFinished() 恢复终端在调用 newtInit() 前的状态,重置原来的输入状态。若没调用这函数,终端需重启才能回到命令行状态。
在 Linux 上用 gcc 编译时带 -lnewt 参数。以下所说的函数都可在 /usr/include/newt.h(Red Hat Linux 7.1)中找到。
NEWT 定义了若干个标志 (FLAG),这里仅介绍常用的几个。
- NEWT_FLAG_RETURNEXIT�D�D 当在组件上按回车时程序退出
- NEWT_FLAG_HIDDEN�D�D 输入不回显,大多数应用在输入密码的情况
- NEWT_ENTRY_SCROLL�D�D 允许滚动输入
- NEWT_FLAG_WRAP�D�D 换行时整个单词换行
- NEWT_FLAG_BORDER�D�D 加边框
- NEWT_FLAG_MULTIPLE�D�D 允许多选
若要使用多个标志,可对多个标志进行与操作 ('|')。
终端所显示的背景,只有不被任何窗口遮盖的部分称为根窗口。一般地,应用不需使用到根窗口,而把文字写到属于自己的窗口上。所有组件都不应放在根窗口上。根窗口只用于显示一些辅助信息,例如程序的作者姓名、版本等。NEWT 提供两种在根窗口显示文字的方式,它们是唯一能越出组件自己当前窗口写文字的 NEWT 函数。
void newtDrawRootText(int left, int top, const char * text); |
该函数用于在根窗口指定位置写出 text 字苻串,left 和 top 为字苻串 text 的开始处,left 是屏幕 x 坐标,top 是屏幕 y 坐标。
Left 和 top 允许为负数。屏幕坐标原点在显示器的左上角,x 坐标从原点出发至左向右增大,y 坐标从原点出发至上向下增大。点 (10,5) 表示以左上角为原点 x=10,y=5, 而 left,top 为负,表与为正数方向相对。点 (-10,5) 表以右上角为原点 x=10,因此点 (10,5) 与点 (-10,5) 在屏幕左右两边相对,同理点 (10,5) 与点 (10,-5) 在屏幕上下两边相对。
在文本方式下,通常屏幕的最后一行用于显示帮助信息,如每个快捷键所对应的功能等。这一提示行称为帮助行(help line)。正如前面所述,NEWT 是基于堆栈,压栈 Push 操作显示帮助行,出栈 Pop 操作删除帮助行。基于栈的操作后面还回遇到。
void newtPushHelpLine(const char * text); void newtPopHelpLine(void); |
newtPushHelpLine() 用于显示帮助行,text 为所要显示的字苻串指针,若为 NULL 则显示缺省的帮助行。NewtPopHelpLine() 则删除帮助行。
在缺省情况下,NEWT 程序不能非正常退出,尽管大多数 Unix 程序可以通过按 Ctrl-z 强迫退出,但在 NEWT 不支持此功能。因 NEWT 初始化时屏蔽所有终端信号。
typedef void (*newtSuspendCallback)(void); void newtSetSuspendCallback(newtSuspendCallback cb); |
但可通过调用 newtSetSuspendCallback() 实现 Ctrl-z 强迫退出的功能,cb 是相应的回调函数,它不带参数,只做清理工作,如 newtFinished()等。早期 Red Hat Linux 的安装程序当运行到硬盘分区时,若你使用 Fdisk 则屏幕回到命令行状态运行 Fdisk 进行分区,退出 Fdisk 时又回到当前窗口。要实现这种功能,在回调函数中加入下面两个函数:
void newtSuspend(void); void newtResume(void); |
newtSuspend() 告诉 NEWT 程序回到终端初始化前的状态,做需要做的工作。如需要回到 NEWT 界面,调用 newtResume() 恢复。
生成窗口有两种主要方式:
int newtCenteredWindow(int width, int height, const char * title); int newtOpenWindow(int left, int top, int width, int height, const char * title); |
由名字可知,newtCenteredWindow() 在屏幕中心生成窗口,width 为窗口宽度,height 为窗口高度,title 为窗口标题字串指针,标题文字为红色。由 newtOpenWindow() 生成的窗口位置由 left,top 确定,其余参数同上。
所有窗口的删除都是用同一种方式,由于 NEWT 是基于堆栈来操作,只有在栈顶的窗口才能被删除,位于栈顶的窗口即当前你能看到的不被任何其他窗口遮盖的窗口。
void newtPopWindow(void); |
这函数删除屏幕最顶层的窗口,并重画被该窗口遮盖的部分。
正如前述那样,Form 是一个容器组件,同一时间只能生成一个 Form,所有组件都必须放在 Form 上,然后运行它。生成一个 Form 用以下函数:
newtComponent newtForm(newtComponent vertBar, const char * help, int flags); |
vertBar 是垂直滚动条,help 是提示信息,通常这两个参数都不会用到,用 NULL 即可,flags 就是前述的标志。该函数返回标志所生成的 Form 的变量。
把组件放在 Form 上用以下函数:
void newtFormAddComponent(newtComponent form, newtComponent co); void newtFormAddComponents(newtComponent form, ...); |
newtFormAddComponent() 只放一个组件在 Form 上,而 newtFormAddComponents() 则可放多个组件,最后用 NULL 结束。
然后就运行它:
newtComponent newtRunForm(newtComponent form); |
其中 Form 参数是 newtForm() 返回的变量。
删除一个 Form:
void newtFormDestroy(newtComponent form); |
当一个 Form 被删除时,其上的组件一同被删除,并释放内存资源。
几乎所有的 Form 都包含最小一个按钮。按钮分两类:完全按钮 (full button) 和紧缩按钮 (compact button)。完全按钮富有立体感,紧缩按钮则单调些。
newtComponent newtButton(int left, int top, const char * text); newtComponent newtCompactButton(int left, int top, const char * text); |
newtButton() 生成完全按钮,letf,top 指定该按钮位置,text 是指向按钮文字的指针,该函数返回按钮的 newtComponent 变量。NewtCompactButton() 生成紧缩按钮,参数及返回值同上。
标签是 NEWT 程序最简单的组件,用于显示给定的文本但不允许用户输入。
newtComponent newtLabel(int left, int top, const char * text); void newtLabelSetText(newtComponent co, const char * text); |
newtLabel() 生成标签组件,并返回它的 newtComponent 变量,参数 left,top 指定标签组件位置,text 为给定的文本。NewtLabelSetText() 用于动态改变标签组件的文字,co 是要改变的标签组件,test 是要改变的字串指针。
输入盒可让用户输入字符串到 Form 中然后由应用接收。
newtComponent newtEntry(int left, int top, const char * initialValue,
int width,char ** resultPtr, int flags);
void newtEntrySet(newtComponent co, const char * value, int cursorAtEnd);
char * newtEntryGetValue(newtComponent co); |
newtEntry() 生成输入盒组件,其中 initialValue 为初始化字符串指针,若不需则用 NULL,width 为宽度,resultPtr 为指向当前输入的字符,flags 为标志。Flags 设为 NEWT_ENTRY_SCROLL,当输入的字串长度等于输入盒的宽度时输入盒将往后滚动,否则不能再输入;Flags 设为 NEWT_FLAG_HIDDEN 输入不回显,主要应用于输入密码方面。NewtEntrySet() 用于动态地改变输入盒的内容,value 为字符串指针,cursorAtEnd 实质是一个逻辑变量,由于C语言没有逻辑类型变量,仅用 int 代替,若为0则表 TRUE,指定光标跟随,非0光标不跟随输入。NewtEntryGetValue() 返回输入的字串。
检查盒可对其代表的内容通过按空格键切换预定的各种状态。
newtComponent newtCheckbox(int left, int top, const char * text,
char defValue,const char * seq, char * result);
char newtCheckboxGetValue(newtComponent co); |
newtCheckbox() 生成一个检查盒,text 标明它所代表的内容,defValue 为缺省值也即初始值,seq 为切换的顺序,result 指向当前状态。如 defValue='@',seq="@*X", 则初始时为 [@], 当按空格键==》 [*] 再按空格键==》 [X],如此循环。若 result 为 NULL 则需 NewtCheckboxGetValue() 获取当前状态。
单选按钮的外观与检查盒非常相似。不同的是单选按钮是由若干个组成一个集合,当一个单选按钮被选中时,其他单选按钮则被清除。若集合中只有一个单选按钮,它总会被选中,因而就失去了选择的意义。
newtComponent newtRadiobutton(int left, int top, const char * text,
int isDefault, newtComponent prevButton);
newtComponent newtRadioGetCurrent(newtComponent setMember); |
newtRadiobutton() 建立单选按钮集合,text 为代表单选按钮的字符串指针,isDefault 是逻辑开关,为 1 表逻辑 TRUE, 初始状态为选中;为0表 FALSE,初始状态为不选中。若当前单选按钮是集合中的第一个,prevButton 为 NULL 让 newtRadiobutton() 自动建立一个集合;若不是第一个,prevButton 为前一个由 newtRadiobutton() 生成的单选按钮。
范围组件通常用于制作水平进度条。
newtComponent newtScale(int left, int top, int width, long long fullValue); void newtScaleSet(newtComponent co, unsigned long long amount); |
newtScale() 生成水平进度条,width 为宽度,fullValue 为进度条的最大值。NewtScaleSet() 用于设置进度条的值。
文本盒能让终端显示一个文本块。
newtComponent newtTextbox(int left, int top, int width, int height, int flags); void newtTextboxSetText(newtComponent co, const char * text); |
newtTextbox() 建立一个文本盒,其中 flags 可设为 NEWT_FLAG_WRAP、NEWT_FLAG_SCROLL 和它们的与操作。文本盒建立后,由 newtTextbox() 填充文本。
newtComponent newtVerticalScrollbar(int left, int top, int height,
int normalColorset, int thumbColorset); |
newtVerticalScrollbar() 生成滚动条,normalColorset 为滚动条颜色,thumbColorset 为滚动块颜色。
列表和是 NEWT 中最重要的组件,允许多选或单选
newtComponent newtListbox(int left, int top, int height, int flags); int newtListboxAppendEntry(newtComponent co, const char * text, const void * data); void * newtListboxGetCurrent(newtComponent co); void newtListboxSetWidth(newtComponent co, int width); void newtListboxSetCurrent(newtComponent co, int num); void newtListboxSetCurrentByKey(newtComponent co, void * key); |
newtListbox() 生产列表盒,flags 可设为 NEWT_FLAG_SCROLL、NEWT_FLAG_RETURNEXIT、NEWT_FLAG_BORDER、 NEWT_FLAG_MULTIPLE 和它们的与操作。NewtListboxAppendEntry() 用于在当前列表盒最后追加一个列表项,每一个个列表项由 key 唯一标志,key 可为任意类型,只要能和其他列表项区别开来就可以了。data 为 key 数据。操作列表盒的函数还有:
void newtListboxSetEntry(newtComponent co, void * key, const char * text);
int newtListboxInsertEntry(newtComponent co, const char * text,
const void * data, void * key);
int newtListboxDeleteEntry(newtComponent co, void * key);
void newtListboxClear(newtComponent co); |
#include <newt.h>
#include <stdio.h>
void rootwin_show()
{
newtCls();
/* 请观察 left,top 为正数 , 为负数地情形 */
newtDrawRootText(0, 0, "左上角");
newtDrawRootText(-6, 0, "右上角");
newtDrawRootText(0, -3, "左下角");
newtDrawRootText(-6, -3, "右下角");
/* 注意 helpline 缺省时的内容 */
newtPushHelpLine(NULL);
newtRefresh();
sleep(10);
newtPushHelpLine("我的帮助行");
newtRefresh();
sleep(3);
newtPopHelpLine();
newtRefresh();
sleep(1);
}
void label_button()
{
newtComponent form, label, entry, button,cb;
char * entryValue;
newtCls();
newtCenteredWindow(50,10,"输入与标签演示");
/*left,top 是相对于中心窗口而言 */
label = newtLabel(10, 1, "请输入 :");
entry = newtEntry(19, 1, NULL, 20, &entryValue,
NEWT_FLAG_SCROLL);
newtEntrySet(entry,"\0",0);
button = newtButton(10, 5, "完全按钮");
cb=newtCompactButton(25,5,"紧缩按钮");
form = newtForm(NULL,NULL, 0);
newtFormAddComponents(form, label, entry, button,cb, NULL);
newtRunForm(form);
if(*entryValue!='\0')
{
newtDrawRootText(0,0,"你输入了 :");
newtDrawRootText(12,0,entryValue);
}
else
newtDrawRootText(0,0,"无输入 !");
newtRefresh();
newtFormDestroy(form);
sleep(5);
}
void check_radio()
{
newtComponent form, checkbox, rb[3], button,lable1,lable2;
char cbValue,cv[2];
int i;
newtCls();
newtOpenWindow(10, 8, 40, 11, "检查盒与单选盒演示");
lable1 = newtLabel(2, 1, "检查盒 :");
checkbox = newtCheckbox(10, 1, "A checkbox", ' ', " *X", &cbValue);
lable2 = newtLabel(2, 4, "单选盒 :");
rb[0] = newtRadiobutton(10, 3, "Choice 1", 1, NULL);
rb[1] = newtRadiobutton(10, 4, "Choice 2", 0, rb[0]);
rb[2] = newtRadiobutton(10, 5, "Choice 3", 0, rb[1]);
button = newtButton(15, 7, "退出");
form = newtForm(NULL, NULL, 0);
newtFormAddComponent(form, checkbox);
newtFormAddComponent(form, lable1);
newtFormAddComponent(form, lable2);
for (i = 0; i < 3; i++)
newtFormAddComponent(form, rb[i]);
newtFormAddComponent(form, button);
newtPushHelpLine("< 空格健 > 选择");
newtRefresh();
newtRunForm(form);
for (i = 0; i < 3; i++)
if (newtRadioGetCurrent(rb[0]) == rb[i])
{ newtDrawRootText(0, 0, "单选盒 :");
newtDrawRootText(9, 0, "第");
if(i==0)newtDrawRootText(11, 0,"1");
if(i==1)newtDrawRootText(11, 0,"2");
if(i==2)newtDrawRootText(11, 0,"3");
newtDrawRootText(12, 0, "个");
}
newtDrawRootText(0, 3, "检查盒状态 :");
cv[0]=cbValue;cv[1]='\0';
newtDrawRootText(13, 3, cv);
newtRefresh();
newtFormDestroy(form);
sleep(5);
}
void test()
{
char message[] = "This is a pretty long message. It will be displayed "
"in a newt textbox, and illustrates how to construct "
"a textbox from arbitrary text which may not have "
"very good line breaks.\n\n"
"Notice how literal \\n characters are respected, and "
"may be used to force line breaks and blank lines.";
newtComponent form, text, button;
newtCls();
text = newtTextboxReflowed(1, 1, message, 30, 5, 5, 0);
button = newtButton(12, newtTextboxGetNumLines(text) + 2, "退出");
newtOpenWindow(10, 5, 37,
newtTextboxGetNumLines(text) + 7, "文本盒");
form = newtForm(NULL, NULL, 0);
newtFormAddComponents(form, text, button, NULL);
newtRunForm(form);
newtFormDestroy(form);
}
main()
{
newtComponent ls,fm;
int p = 1, q = 2, r = 3, s = 4, t = 5, *u;
newtInit();
do {
newtCls();
newtRefresh();
newtDrawRootText(0,0,"这是我的一个 NEWT 演示程序");
newtCenteredWindow(50,10,"请选择");
ls = newtListbox(18,3,5,NEWT_FLAG_RETURNEXIT);
newtListboxAppendEntry(ls,"根窗口演示",&p);
newtListboxAppendEntry(ls,"输入盒与按钮",&q);
newtListboxAppendEntry(ls,"检查盒与单选盒",&r);
newtListboxAppendEntry(ls,"文本盒",&s);
newtListboxAppendEntry(ls,"退出 ",&t);
newtPushHelpLine(" Move using the arrow keys and press ENTER to select");
fm = newtForm(NULL,NULL,0);
newtFormAddComponents(fm,ls,NULL);
newtRunForm(fm);
u = newtListboxGetCurrent(ls);
newtPopWindow();
newtFormDestroy(fm);
switch(*u) {
case 1:
rootwin_show();
break;
case 2:
label_button();
break;
case 3:
check_radio();
break;
case 4:
test();
break;
case 5:
newtFinished();
exit(0);
}
} while(1);
}
|
运行结果如下图 :
运行结果
梁俊辉,对Linux的网络应用和程序设计有浓厚兴趣。您可以通过电子邮件 zeusliang@21cn.com跟他联系。