嵌入式浏览器开发

jackxiang 2009-8-11 11:13 | |

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 简介
GTK (GIMP Toolkit) 起源於开发用来做为GIMP (General Image Manipulation Program)的一套工具. GTK建立在GDK (GIMP Drawing Kit)的上层, 基本上是将Xlib功能包装起来. 它被称为GIMP toolkit是因为它是为了开发GIMP而写的, 但现在被许多免费软体计划所使用。
4.2.2 一个简单的GTK程序

#include <gtk/gtk.h>

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浏览器,并便于移植到嵌入式系统中。该项目是开放源码项目,由sogo456@263.net负责维护,网址是:http://netbit_browser.myetang.com

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

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<BufSize) 

  { j=0;

    while(line_size<SCREEN_WIDTH-20 && Buf[i]!='\n')

    {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>和相关标记对字体的设置阐述其算法。

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

<font size=4 color=#0000FF>

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

modify it under the terms of the <font size=5 color=FF0000>GNU General

Public License </font>as published by the Free Software Foundation;

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

</font>

显示的效果应为GNU General Public License的字号为5,颜色为FF0000,即红色;free software应为粗体,受首尾两个呼应的font标记约束,其它字字号均为4,颜色为0000FF,由于free software只被<b></b>这一对加粗符号约束,所以其颜色应受首尾的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运行时的界面,所打开的页面源代码如下:

<html>

<body>

<h1>

<font color=#FF00FF><b>Netbit Browser Version 0.0.1 Demo</b></font></h1>

<hr>

<h4>License</h4>

<p>

<font size=4 color=#0000FF>

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

modify it under the terms of the <font size=5 color=FF0000>GNU General

Public License </font>as published by the Free Software Foundation;

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

</font>

<hr>

<h3>Design based on GTK, by sogo and ce!</h3>

</body>

</html>


以下为主菜单


以下为工具条


输入网页的URL,即可进行访问。


以下为打开文件对话框


以下为查看HTML源码对话框


下面对比Netbit Browser,看看其它浏览器查看此网页的效果。

以下为KDE浏览该网页的效果


以下为GZILLA浏览该网页的效果,GZILLA对字体颜色的处理比较差,只有黑色的字体。对字号的支持也不好。


以下为Netscape显示该网页的效果,Netscape默认背景色是灰色。


IE查看该网页的效果,字体不同是由于IE设置的默认字体不同。


可以看到,在对简单英文网页的支持效果上看,Netbit Browser,已接近于成熟浏览器的水平,甚至优于一些小型的嵌入式浏览器如GZILLA,Netfront,但在复杂页面的显示上还有较大的差距。

可以得出的结论是,Netbit Browser 0.0.1 Demo 版已经具有了一定的实用价值,但要对其进行完善,工作量还很巨大。

对比Netscape,IE的漫长的开发历史和巨大的资金投入,Netbit Browser的未来依然生死未卜。

 内文分页: [1] [2] [3] [4]

作者:jackxiang@向东博客 专注WEB应用 构架之美 --- 构架之美,在于尽态极妍 | 应用之美,在于药到病除
地址:https://jackxiang.com/post/1919/
版权所有。转载时必须以链接形式注明作者和原始出处及本声明!

评论列表
发表评论

昵称

网址

电邮

打开HTML 打开UBB 打开表情 隐藏 记住我 [登入] [注册]