Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1680840
  • 博文数量: 584
  • 博客积分: 13857
  • 博客等级: 上将
  • 技术积分: 11883
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-16 09:34

分类: LINUX

2010-11-11 11:34:03

第四章  Linux程序开发技术及 GTK图形程序开发

4.1        Linux下的程序开发环境

4.1.1基本知识

(1) 标准 (ANSI C, POSIX, SVID, XPG, ...)

ANSI C:这一标准是 ANSI(美国国家标准局)于 1989 年制定的 C 语言标准。 后来被 ISO(国际标准化组织)接受为标准,因此也称为 ISO C。

POSIX:该标准最初由 IEEE 开发的标准族,部分已经被 ISO 接受为国际标准。

SVID:System V 的接口描述。System V 接口描述(SVID)是描述 AT&T Unix System V 操作 系统的文档,是对 POSIX 标准的扩展超集。

XPG:X/Open 可移植性指南。X/Open 可移植性指南(由 X/Open Company, Ltd.出版), 是比 POSIX 更为一般的标准。

(2) 函数库和系统调用

 glibc:众所周知,C 语言并没有为常见的操作,例如输入/输出、内存管理,字符串操作等提供内置的支持。相反,这些功能一般由标准的“函数库”来提供。GNU 的 C 函数库,即 glibc,是 Linux 上最重要的函数库,它定义了 ISO C 标准指定的所有的库函数,以及由 POSIX 或其他 UNIX 操作系统变种指定的附加特色,还包括有与 GNU 系统相关的扩展。目前,流行的 Linux 系统使用 glibc 2.0 以上的版本。glibc 基于如下标准:ISO C: C 编程语言的国际标准,即 ANSI C,POSIX,Berkeley Unix,SVID,XPG。

 

其他重要函数库:除 glibc 之外,流行的 Linux 发行版中还包含有一些其他的函数库,这些函数库具有重要地位,例如:GNU Libtool,CrackLib,LibGTop。图形文件操作函数库包括: libungif、libtiff、libpng、Imlib, libjpeg 等,可分别用来操作 GIF、TIFF、PNG、JPEG 以及其他一些格式图形文件。

(3) 系统调用

系统调用是操作系统提供给外部程序的接口。在 C 语言中,操作系统的系统调用通常通过函数调用的形式完成,在 Linux 系统中,系统调用函数定义在 glibc 中。

谈到系统调用时,需要注意如下几点:系统调用函数通常在成功时返回 0 值,不成功时返回非零值。errno 中包含有错误代码。系统调用是一个非常耗时 的过程。

(4) C 语言编程风格

    良好的编程风格可以在许多方面帮助开发人员,可以增加代码的可读性,并帮助你理清头绪。编程风格最能体现一个程序员的综合素质。

在 Linux 中,我们经常看到的是定义非常简单的函数接口和变量名称。C 语言最初来自 UNIX 操作系统,与 UNIX 的设计原则一样,C 语言被广泛认可和使用的一个重要原因是它的灵活性以及简洁性。在利用 C 语言编写程序时,应当符合其简洁的设计原则,而不应当使用非常复杂的变量命名方法。Linus 为 Linux 内核定义的 C 语言编码风格要点如下:

缩进时,使用长度为 8 个字符宽的 Tab 键。如果程序的缩进超过 3 级,则应考虑重新设计程序。

大括号的位置。除函数的定义体外,应当将左大括号放在行尾,而将右大括号放在行首。函数的定义体应将左右大括号放在行首。如下所示:

int function(int x, int y)

{

        if (x == y) {

               ...

        }

 else {

                ...

     }

     return 0;

}

应采用简洁的命名方法。对变量名,不赞成使用大小写混写的形式,但鼓励使用描述性的名称;尽可能不使用全局变量;不采用匈牙利命名法表示变量的类 型;采用短小精悍的名称表示局部变量;保持函数短小,从而避免使用过多的局部变量。保持函数短小精悍。不应过分强调注释的作用,应尽量采用好的编码风格而 不是添加过多的注释。

(5) 库和头文件的保存位置

函数库:

/lib:系统必备共享库

/usr/lib:标准共享库和静态库

/usr/i486-linux-libc5/lib:libc5 兼容性函数库

/usr/X11R6/lib:X11R6 的函数库

/usr/local/lib:本地函数库

头文件:

/usr/include:系统头文件

/usr/local/include:本地头文件

 

4.1.2   Linux 上的编译器和调试器

(1)    Linux 上的 C/C++ 编译器和调试器

运行 gcc/egcs:Linux 中最重要的软件开发工具是 GCC。GCC 是 GNU 的 C 和 C++ 编译器。实际上,GCC 能够编译三种语言:C、C++ 和 Object C(C 语言的一种面向对象扩展)。利用 gcc 命令可同时编译并连接 C 和 C++ 源程序。

例如利用如下的命令可编译生成可执行文件,并执行程序:

$ gcc -o factorial main.c factorial.c

$ ./factorial 5

GCC 可同时用来编译 C 程序和 C++ 程序。一般来说,C 编译器通过源文件的后缀名来判断是 C 程序还是 C++ 程序。在 Linux 中,C 源文件的后缀名为 .c,而 C++ 源文件的后缀名为 .C 或 .cpp。

但是,gcc 命令只能编译 C++ 源文件,而不能自动和 C++ 程序使用的库连接。因此,通常使用 g++ 命令来完成 C++ 程序的编译和连接,该程序会自动调用 gcc 实现编译。

例如可以如下调用 g++ 命令编译、连接并生成可执行文件:

$ g++ -o hello hello.C

$ ./hello

(2) gcc/egcs 的主要选项

-ansi               只支持 ANSI 标准的 C 语法。这一选项将禁止 GNU C 的某些特色,

-c                  只编译并生成目标文件。

-DMACRO             以字符串“1”定义 MACRO 宏。

-DMACRO=DEFN        以字符串“DEFN”定义 MACRO 宏。

-E                  只运行 C 预编译器。

-g                  生成调试信息。GNU 调试器可利用该信息。

-IDIRECTORY         指定额外的头文件搜索路径DIRECTORY。

-LDIRECTORY         指定额外的函数库搜索路径DIRECTORY。

-lLIBRARY           连接时搜索指定的函数库LIBRARY。

-m486               针对 486 进行代码优化。

-o FILE             生成指定的输出文件。用在生成可执行文件时。

-O0                 不进行优化处理。

-O 或 -O1           优化生成代码。

-O2                 进一步优化。

-O3                 比 -O2 更进一步优化,包括 inline 函数。

-shared             生成共享目标文件。通常用在建立共享库时。

-static             禁止使用共享连接。

-UMACRO             取消对 MACRO 宏的定义。

-w                  不生成任何警告信息。

-Wall               生成所有警告信息。

(3)gdb

    GNU 的调试器称为 gdb,该程序是一个交互式工具,工作在字符模式。在 X Window 系统中,有一个 gdb 的前端图形工具,称为 xxgdb。gdb 是功能强大的调试程序,可完成如下的调试:设置断点;监视程序变量的值;程序的单步执行;修改变量的值。

在可以使用 gdb 调试程序之前,必须使用 -g 选项编译源文件。可在 makefile 中如下定义 CFLAGS 变量:CFLAGS = -g,运行 gdb 调试程序时通常使用如下的命令:gdb progname

(4) 创建和使用静态库

创建一个静态库是相当简单的。通常使用 ar 程序把一些目标文件(.o)组合在一起,成为一个单独的库,然后运行 ranlib,以给库加入一些索引信息。

(5) 创建和使用共享库

特殊的编译和连接选项-D_REENTRANT使得预处理器符号 _REENTRANT 被定义,这个符号激活一些宏特性。-fPIC选项产生位置独立的代码。由于库是在运行的时候被调入,因此这个选项是必需的,-shared选项告诉编译器产生共享库代码。

 

4.1.3   GNU make 和 makefile

(1) GNU make简介

在大型的开发项目中,通常有几十到上百个的源文件,如果每次均手工键入 gcc 命令进行编译的话,则会非常不方便。因此,人们通常利用 make 工具来自动完成编译工作。这些工作包括:如果仅修改了某几个源文件,则只重新编译这几个源文件;如果某个头文件被修改了,则重新编译所有包含该头文件的源 文件。利用这种自动编译可大大简化开发工作,避免不必要的重新编译。

实际上,make 工具通过一个称为 makefile 的文件来完成并自动维护编译工作。makefile 需要按照某种语法进行编写,其中说明了如何编译各个源文件并连接生成可执行文件,并定义了源文件之间的依赖关系。

当修改了其中某个源文件时,如果其他源文件依赖于该文件,则也要重新编译所有依赖该文件的源文件。

默认情况下,GNU make 工具在当前工作目录中按如下顺序搜索 makefile: GNUmakefile,makefile,Makefile。在 UNIX 系统中,习惯使用 Makefile 作为 makfile 文件。

(2) makefile 基本结构

makefile 中一般包含如下内容:

* 需要由 make 工具创建的项目,通常是目标文件和可执行文件。

* 要创建的项目依赖于哪些文件。

* 创建每个项目时需要运行的命令。

 

例如,假设你现在有一个 C++ 源文件 test.C,该源文件包含有自定义的头文件 test.h,则目标文件 test.o 明确依赖于两个源文件:test.C 和 test.h。另外,你可能只希望利用 g++ 命令来生成 test.o 目标文件。

这时,就可以利用如下的 makefile 来定义 test.o 的创建规则:

test.o: test.C test.h

       g++ -c -g test.C

一个 makefile 文件中可定义多个目标,利用 make target 命令可指定要编译的目标,如果不指定目标,则使用第一个目标。通常,makefile 中定义有 clean 目标,可用来清除编译过程中的中间文件,例如:

clean:

    rm -f *.o

 

运行 make clean 时,将执行 rm -f *.o 命令,最终删除所有编译过程中产生的所有中间文件。

(3) makefile 变量

GNU 的 make 工具除提供有建立目标的基本功能之外,还有许多便于表达依赖性关系以及建立目标的命令的特色。其中之一就是变量或宏的定义能力。如果你要以相同的编译选项 同时编译十几个 C 源文件,而为每个目标的编译指定冗长的编译选项的话,将是非常乏味的。但利用简单的变量定义,可避免这种乏味的工作,例如:

CC = gcc

CCFLAGS = -D_DEBUG -g -m486

test.o: test.c test.h

    $(CC) -c $(CCFLAGS) test.c

    在上面的例子中,CC 和 CCFLAGS 就是 make 的变量。GNU make 通常称之为变量,而其他 UNIX 的 make工具称之为宏,实际是同一个东西。在 makefile 中引用变量的值时,只需变量名之前添加 $ 符号。

(4) 运行 make

GNU make 命令还有一些其他选项:

-C DIR              在读取 makefile 之前改变到指定的目录 DIR。

-f FILE             以指定的 FILE 文件作为 makefile。

-h                  显示所有的 make 选项。

-i                  忽略所有的命令执行错误。

-I DIR              当包含其他 makefile 文件时,可利用该选项指定搜索目录。

-n                  只打印要执行的命令,但不执行这些命令。

-p                  显示 make 变量数据库和隐含规则。

-s                  在执行命令时不显示命令。

-w                  在处理 makefile 之前和之后,显示工作目录。

-W FILE             假定文件 FILE 已经被修改。

4.2        GTK图形程序开发

4.2.1 简介

起源於开发用来做为GIMP (General Image Manipulation Program)的一套工具. GTK建立在GDK (GIMP Drawing Kit)的上层, 基本上是将Xlib功能包装起来. 它被称为GIMP toolkit是因为它是为了开发GIMP而写的, 但现在被许多免费软体计划所使用。

4.2.2 一个简单的GTK程序

#include

int main( int argc,char *argv[] )

{ GtkWidget *window;

  gtk_init (&argc, &argv);

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

  gtk_widget_show (window);

  gtk_main ();

  return(0);

}

编译方法:gcc -Wall -g base.c -o base `gtk-config --cflags --libs`

该程序是一个最简单的窗口程序。

 

4.2.3 GTK的层次结构

GIMP

GTK+

GDK

GLIB

XLIB

 

4.2.4 GTK的事件与信号处理

GTK是一个事件驱动的工具集,一个GTK应用通常在gtk_main上休眠直到一个事件发生,这时控制被传递给合适的函数。

   信号处理函数:

gint gtk_signal_connect( GtkObject, gchar,GtkSignalFunc, gpointer);

void gtk_signal_disconnect( GtkObject, gint);

void gtk_signal_handler_block( GtkObject, guint);

void gtk_signal_handler_unblock( GtkObject, guint);

void gtk_signal_emit( GtkObject, guint, ... );

事件与信号不是一回事

 

4.2.5 GTK的控件

使用一个控件的步骤

l      gtk_*_new 初始化一个控件

l      绑定控件与信号及事件

l      把它包装到一个包容器中

l      告诉GTK显示这个控件(gtk_widget_show)

控件体系类似于MFC的类库,子类具有父类的特征,在使用时要使用类型强制转换。

 

第五章 Netbit浏览器开发与分析

5.1 Netbit 浏览器简介

Netbit Browser 是基于Linux平台的浏览器,使用了gtk作为gui开发工具。项目的目的是要建立一个小型的、快捷的web浏览器,并便于移植到嵌入式系统中。该项目是开放源码项目,

 

项目目前已完成 0.0.1 demo版,实现了基本界面,html4.0词法分析、支持本地文本文件查看,英文网页浏览(使用菜单open file),支持简单http连接,支持URL访问,如输入等,支持字体颜色、 字号等基本的元素。

 

Netbit Browser在界面上主要承袭了Gzilla的风格,在技术上主要涉及了词法、语法分析,文档布局,PIXMAP画图,文件IO操作,简单HTTP访问等技术。

界面模块

控制模块


PIXMAP画图模块


5.2 Netbit Browser浏览器各部份的功能

IO模块

词法分析模块

文本文件显示模块

HTML文件显示模块


(1)主程序:用于初始化。

(2)界面模块:窗口、菜单、工具条、滚动条等的初始化。

(3)控制模块:负责命令的响应,消息的处理,是软件控制的中枢。

(4)IO模块:包括http和文件操作。

(5)词法分析模块:HTML的词法分析。

(6)文本文件的显示模块:对Plain text显示的处理,也包含相应的布局算法。

(7)HTML文件的显示模块:在Netbit browser中使用了语法分析与布局同时进行的方式,由此模块来驱动HTML文件的显示。

(8)使用PIXMAP的画图模块:是实际输出采用的方法,被6、7模块调用。

 

以下将就主要的较复杂的模块进行说明。

 

5.3 界面模块

(1) interface.c,interface.h:用于界面的初始化和定制。

主要的函数说明:

void a_Interface_init(void);界面初始化的主函数

void a_Interface_status(BrowserWindow *bw, const char *format, ... );

设置状态栏内容。

void a_Interface_openfile_dialog(BrowserWindow *bw);文件打开对话框

void a_Interface_set_Page_title(BrowserWindow *bw, char *title);

设置窗口标题。

void a_Interface_entry_open_url(GtkWidget *widget, BrowserWindow *bw);

打开网址输入栏输入的网址。

BrowserWindow *a_Interface_new_browser_window(gint width, gint height);

新建浏览窗口,是很重要的函数,在其中创建了所有的界面控件,并对按钮等进行了消息绑定。

 

(2) menu.c,menu.h

主要函数:GtkWidget *a_Menu_mainbar_new (BrowserWindow *bw);

定义了主菜单项,并进行了消息绑定

 

(3) browser.h

定义了重要的窗口结构如下:

struct _BrowserWindow

{

   /* 主窗口的widgets */

   GtkWidget *main_window;

   GtkWidget *back_button;

   GtkWidget *forw_button;

   GtkWidget *stop_button;

   GtkWidget *location;

   GtkWidget *location_button;

   GtkWidget *status;

 

   /* 键盘控制表*/

   GtkAccelGroup *accel_group;

 

   /* 工具条按钮 */

   GtkWidget *back_menuitem;

   GtkWidget *forw_menuitem;

   GtkWidget *stop_menuitem;

 

   /* 主文档 widget. (用于绘制HTML或其它) */

   GtkWidget *layout;

 

   /* 当前光标类型 */

   GdkCursorType CursorType;

 

   /* 对话框widgets*/

   GtkWidget *open_dialog_window;

   GtkWidget *open_dialog_entry;

   GtkWidget *openfile_dialog_window;

   GtkWidget *quit_dialog_window;

 

   /* 指向保存词法分析结果的数据结构 */

   BitTokenContext *global_cx;

 

   /* 文件类型:html or plain text*/

   gint file_type;

};

 

5.4 控制模块

(1)command.c,command.h

主要的函数说明:

void a_Commands_openfile_callback (GtkWidget *widget, gpointer client_data);打开文件的对话框

void a_Commands_openurl_callback (GtkWidget *widget, gpointer client_data);打开URL

void a_Commands_close_callback(GtkWidget * widget, gpointer client_data);关闭窗口

void a_Commands_exit_callback (GtkWidget *widget, gpointer client_data);退出程序

void a_Commands_viewsource_callback (GtkWidget *widget, gpointer client_data);查看HTML源码

void a_Commands_reload_callback (GtkWidget *widget, gpointer client_data);刷新当前网页

void a_Commands_home_callback (GtkWidget *widget, gpointer client_data);显示主页

void a_Commands_helphome_callback (GtkWidget *widget, gpointer client_data); 显示帮助

 

(2)nav.h,nav.c:是命令对应的与网页操作有关的具体实施

 

主要的函数说明:

void a_Nav_push(BrowserWindow *bw, const char*);按URL打开一个网址或文件,具有对不完整URL的兼容性。

void a_Nav_reload(BrowserWindow *bw);刷新当前网页

void a_Nav_open_splash(BrowserWindow *bw,char *str);打开起始页(内置页面)

 

5.5词法分析模块

词法分析的原理和算法在前面已有详述。

(1)    BitToken.c,BitToken.h

主要的函数说明:

BitTokenContext * Bit_NewContext(); 创建新的全局结构

int Bit_Tokenize(BitTokenContext *global_cx); 局部词法分析

void Bit_BeginToken(BitTokenContext *global_cx); 全局词法分析

int Bit_DestroyToken(BitTokenContext *global_cx); 释放内存

char *Token_ReadUntil(BitTokenContext *global_cx,char *sUntil);重要的字符处理函数,读取到指定字符后结束

char *Token_GetAttribute(BitTokenContext *global_cx); 取元素属性

void Token_ConvertIfNeed(char * aString); 转义字串的处理

int Token_ConsumTag(BitTokenContext *global_cx);处理元素

int Token_Consum_PlainText(BitTokenContext *global_cx); 处理文本

void Bit_ShowTokenResult(BitTokenContext *global_cx);显示分析结果

void Bit_SaveTokenResult(BitTokenContext *global_cx,char * filename); 保存分析结果

 

(2)    BitHtmlDtd.h,BitHtmlDtd.c

用于存储HTML4.0元素的名称和属性。

 

(3)    BitTokenList.h,BitTokenList.c

元素链表相关

(4)    BitTokenAttrList.h,BitTokenAttrList.c

元素属性链表相关

(5)    BitStr.h,BitStr.c

字符串处理函数

 

5.6使用PIXMAP的画图模块

因本部份是HTML文件的显示模块、文本文件的显示模块的基础,所以先予说明。

 

paint.c,paint.h

主要的函数说明:

gint pixmap_new(GtkWidget *widget,int width,int height);

在此函数中使用

pixmap  = gdk_pixmap_new(widget->window,width+30,height+30,-1);来新建一个pixmap。

 

gint expose_event (GtkWidget *widget, GdkEventExpose *event); 在expose消息到来时,即若界面被破坏需重画时,使用

gdk_draw_pixmap(widget->window,widget->style->fg_gc[GTK_WIDGET_STATE(widget)],pixmap,event->area.x,event->area.y,event->area.x,ent->area.y, event->area.width, event->area.height);来重画。

 

gint pixmap_repaint(GtkWidget *widget);用于提供手动重画。

gint Browser_Paint(BrowserWindow *bw);layout的主函数,用来根据文件类型来调用HTML文件的显示模块或文本文件的显示,同时初始化滚动条。

 

5.7 文本文件的显示模块

plain.c,plain.h

char *Plain_handle_tabs(const char *str)将TAB转为空格。

void a_Plain_write(GtkLayout *display,char *Buf1, gint BufSize)主要函数

下面介绍一下文本显示的算法。

指定默认字体

font=gdk_font_load("-adobe-helvetica-medium-r-normal--14-*-*-*-*-*-iso8859-1");

通过预布局来计算页面的长度:

while(i

  { j=0;

    while(line_size

    {str[j]=Buf[i];

     line_size+=gdk_char_width(font,str[j]);

     j++;

     i++;

    }

    str[j]='\0';   

    if(Buf[i]=='\n')i++;

    x=X_START;

    line_size=x;

    y+=16;

  }

创建PIXMAP

  pixmap_new(drawing_area,SCREEN_WIDTH,y);

  gc = gdk_gc_new(drawing_area->window);

进行真实的画图。

pixmap_repaint(drawing_area);

输出到PIXMAP并显示

 

5.8 HTML文件的显示模块

这部份是整个浏览器最重要的部份之一,综合了语法分析与HTML的布局、输出,其算法的好坏直接关系到网页的显示效果。

 

主要流程:

  while(pTtokenList!=NULL)

  {…………

   switch(pTtokenList->token->type)

        {

          case HTML_TITLE:

           …………

             break;

              

          case HTML_TEXT:

           …………

                 break;

    …………

       …………

          default:

       ………

           break;

      } //switch

   pTtokenList=pTtokenList->next;

  } //while

 

可以看到,这部份与语词分析结合的十分紧密,利用词法分析的结果,遍历各元素节点,取出其元素属性,根据一定的布局算法来进行布局。

 

例如:当遇到title元素时,就使用gtk函数来设定窗口标题为指定标题

gtk_window_set_title(GTK_WINDOW(bw->main_window),pTtokenList->token->pData);

其中pTtokenList->token->pData即为词法分析分析出的标题内容。

 

由于程序结构十分简单清晰,大部份元素的处理都简单易懂,参考源程序即可,下面主要针对和相关标记对字体的设置阐述其算法。

 

由于标记允许嵌套,所以使用了栈来对font元素进行管理,例如以下的HTML代码:

This program is not free software; you can redistribute it and/or

modify it under the terms of the GNU General

Public License as published by the Free Software Foundation;

either version 2 of the License, or (at your option) any later version.

 

显示的效果应为GNU General Public License的字号为5,颜色为FF0000即红色;free software应为粗体,受首尾两个呼应的font标记约束,其它字字号均为4,颜色为0000FF,由于free software只被这一对加粗符号约束,所以其颜色应受首尾的font标记的约束,即应为0000FF。

 

这种嵌套的约束方式带来了HTML元素管理的混乱,也容易产生冗余的HTML代码,但既然标准是这么定的,也只能想办法加以解决,固然现在随着样式表的广泛采用,font已面临寿终正寝,但仍然大量存在,特别在对字体的颜色的设置,使用font标记很方便。

 

栈式管理的主要算法详解:

void html_open_font(GtkWidget *widget,char * style_str,char *color_str,char *size_str,int html_element,int insert_to_list);

 

该函数用于指定当前的字体属性,其参数包括style,color,size,以及改变字体属性的元素的名称,int insert_to_list用于标记此字体属性是否入栈,通常是入栈的。

 

这样,在出现font或相关元素的首标记时,我们将词法分析的结果提取出来,即将其元素属性提取出来,作为参数传递给html_open_font 函数,该函数将这些属性进行组合,设置成为当前字体属性,并入栈保存;在出现font或相关元素的尾标记时,出于保险(因为存在交错包含关系的元素),首 先检验栈顶元素与正在处理的元素尾标记是否匹配(名称相同),如相同则出栈,并将栈内下一字体属性设为系统的当前字体属性。

 

出栈函数为void html_close_font(GtkWidget *widget,int html_element)

需要注意的是由于并不是所有的font元素都指定所有的属性,可能只指定其中的一个或一部份属性,因此在入栈时必须做这样的处理,即首先获取当前的字体属性,根据哪些属性发生了变化来组合新的字体属性,然后入栈。

 

所使用的栈的结构很简单,如下。

typedef struct _font_list{

int html_element;

char color_str[15];

char size_str[15];

char style_str[15];

}font_list;

此为font_list的类型定义,描述了字体属性的结构

 

font_list font_opening[50]; 定义一个数组作为栈的存储形式

int current_font=0; 定义一个整型变量,作为栈顶指针

 

如此,一个简单的数组就发挥了巨大的作用,配以一点点算法,就带来了丰富多彩的界面效果。

 

5.9 Netbit实际应用效果及比较

下图为Netbit browser运行时的界面,所打开的页面源代码如下:

Netbit Browser Version 0.0.1 Demo


License

This program is not free software; you can redistribute it and/or

modify it under the terms of the GNU General

Public License as published by the Free Software Foundation;

either version 2 of the License, or (at your option) any later version.


Design based on GTK, by sogo and ce!

阅读(1105) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~