EGui是一个基于Linux Framebuffer的嵌入式图形系统,采用完全开放源代码方式发布代码。并且EGui图形代码将会完全免费。 EGui目前基本完成图形系统的完整的框架,有window管理,事件管理和事件分发,timer的实现,widget实现了一部分。
1. EGui 简介 EGui是一个基于Linux Frame buffer的嵌入式图形系统,希望它能为Linux提供嵌入式解决方案。关于他的其他的简介我做了一个FAQ。 - EGui 的网络资源有哪些?
web: wwww.egui.org 他其实连接到cnix.sf.net bbs: www.linuxfans.org 的 "EGui开源项目" 版块. bbs上有文档和下载. cnix.sf.net也都有文档和下载的连接地址. - EGui适合什么操作系统?
Linux ,只支持2.6.xx版本. - EGui和当前那个图形最接近?
GTK+FB.需要Framebuffer支持.或者QT/E。 - EGui采用C/S结构吗?
不需要,运行应用前,./bin/load_driver.sh即可. EGui本身带有驱动模块。窗口管理,事件管理都在驱动层实现。 - 怎么编译?
如果在PC make make install 如果在嵌入式平台: 修改config.mk CROSSCOMPILER=arm(mipsel)-linux- 修改src-gui/driver/Makefile KERNELSRC=/yourkernel make make install - 怎么运行?
运行 make install命令后 就会将编译的程序复制到 ./bin目录下了. ./load_driver.sh //安装驱动模块 ./ewm *.bmp& //启动背景桌面程序,bmp要24位色 ./fifteen //游戏程序 ./testegui //jawbreaker 游戏程序. ./monitor //CPU使用状态监视器. 1.1. 编译环境 在Fedora core 4上一步步建立环境 1 update FC4 kernel 2.6.12-1.1447_FC4 1: download kernel-2.6.12-1.1447_FC4.src.rpm 2: rpm -ivh kernel-2.6.12-1.1447_FC4.src.rpm 3: cd /usr/src/redhat/SPECS/ 4: uname -i if your computer is i386,do so. vi kernel-2.6.spec modify %define all_x86 i586 i686 as %define all_x86 i586 i686 i386 5: rpmbuild -bb kernel-2.6.spec 6: cd /usr/src 7: ln -s /usr/src/redhat/BUILD/kernel-2.6.12/linux-2.6.12/ linux 8: cd linux 9: cp configs/kernel-2.6.12-i686.config .config //your computer CPU match config filename. 10: make menuconfig // save and exit 11: make all 12: make modules_install 13: make install 14: vi /boot/grub/grub.conf add as follow: title Fedora Core (2.6.12) root (hd0,5) kernel /boot/vmlinuz-2.6.12-1.1447_FC4.root ro root=LABEL=/ rhgb quiet initrd /boot/initrd-2.6.12-1.1447_FC4.img 2修改启动选项 config for Egui 1: compile kernel add framebuffer support and add your VGA card driver 2: modify /boot/grub/grub.conf kernel /boot/vmlinuz-2.6.12-1.1447_FC4.root ro root=LABEL=/ rhgb quiet vga=0x316 add vga=0x316 //1024x768-16bbp mode 3: reboot your computer
1.2. 下载编译EGui 下载地址: http://www.linuxfans.org/nuke/modules.php?name=Site_Downloads&op=mydown&did=4269 此版本是这次文档指定版本。 $ tar zxvf EGui-2006-04-16.tar.gz $ cd EGui $ make $ make install //仅仅复制文件到./bin目录。 1.2.1. 交叉编译 $ tar zxvf EGui-2006-04-16.tar.gz $ cd EGui 修改 config.mk CROSSCOMPILER=mipsel-linux- 修改 src/driver/Makefile KERNELSRC :=/usr/src/linux $ make $ make install //仅仅复制文件到./bin目录。 1.3. 运行程序 - $ cd bin
$ ./load_driver.sh #使用root用户。 - $./ewm new.bmp >>/dev/null & ###启动root窗口程序。显示背景桌面。
$./testegui >>/dev/null & #启动jawbreaker游戏程序 第2步可以修改 run-egui.sh实现 #!/bin/bash ./ewm EGui.bmp >>/dev/null & sleep 3 ./testegui >>/dev/null &
1.4. 截图参考 2. EGui框架 这部分主要介绍EGui的整个框架部分,让大家了解EGui的架构及其代码的分布情况,并不介绍其细节或者实现方法。 详细的细节和实现方法将在以后的各部分详细说明。 上图是EGui的框架示意图。 EGui本身应该是三个部分,加上应用的调用部分就是四个部分。 Kernel驱动部分; Libegui部分; Libwidget部分; 应用程序; 2.1. Kernel驱动部分 这部分主要作用: 获取framebuffer信息; 创建/dev/egui设备文件号,默认是 c 240 0; 分配窗口的ID号; 获取输入事件,并且分发给窗口; 储存窗口的信息,包括位置; 代码分布: Src-gui/driver/ 2.2. Libegui部分 这部分主要作用: 从/dev/egui 获取和kernel通讯的设备的fd号; 从/dev/fb? 获取framebuffer地址; 对显卡的读写实现,包括:点,线,矩形,图片; 窗口的实现,绘制; 光标的移动和绘制; 事件传给widget; 代码分布: Src-gui/egui 2.3. Libwidget部分 这部分主要作用: 创建不同类型的widget,例如:button,form,pixmap,edit等等; 采用统一接口实现widget的显示,事件,等等; 事件的分发; 父子widget机制的实现; 代码分布: Src-gui/widget 2.4. 应用程序 EGui目前带有部分测试程序,代码都在demo/目录; ewm,桌面管理; fifteen,十五个button排序游戏; test , jawbreaker游戏; bmp, 显示和截图程序;截图程序将图片存储为 24位色BMP格式。 ./capturebmp a.bmp Chmod +r a.bmp 2.5. 其它说明 Include/common.h 和src-gui/driver/common.h是相同文件的复制,目的在于让src-gui/driver能独立编译。 3. 窗口和事件的实现 3.1. 数据结构说明 先解释几个重要的数据结构大都来自common.h 3.1.1. struct _EGui_FBinfo { int dev ; // fb0,fb1,fb2 int screen_width; // 1024 int screen_height; // 768 int bpp; // 16 unsigned char * Egui_phyaddress; unsigned char * smem_len; int p_width; // 1024 * 2,use by program int p_height; // 768 ,use by program int p_bpp; // 2 ,use by unsigned char * Egui_address; //mmap address char fbdevfile[8]; int red_length ; int green_length ; int blue_length ; int red_offset ; int green_offset; int blue_offset ; EGui_Window *ewindow; } ; int dev ; // fb0,fb1,fb2 int screen_width; // 1024 int screen_height; // 768 int bpp; // 16 unsigned char * Egui_phyaddress; unsigned char * smem_len; 上面的部分来自framebuffer信息, int p_width; // 1024 * 2,use by program int p_height; // 768 ,use by program int p_bpp; // 2 ,use by unsigned char * Egui_address; //mmap address 这部分是程序转换后的结果,经常会拿来使用。 3.1.2. struct _EGui_Window { int pid; /* program id */ 建立窗口进程的ID. Ecolor *bgcolor; /* background color */窗口的背景颜色 short x, y; /* start postition */窗口在屏幕的开始坐标位置。 short width, height; /* windows w ,h */窗口的宽和高 short max_width; /* visible w */ short max_height; /* visible h */ short id; /* windows id */ char name [NAMELEN]; /* windows name */ char window_type; short title_h; /* title hieght */ short frame_w; /* frame width */ unsigned char *savebuf; /* save buffer window size */ unsigned char *saveself; /* save oneself */ EGui_Window *twindow; /* top window */ EGui_Window *bwindow; /* bottom window */ EGui_FBinfo *fbinfo; /* follow struct only can use in kernel * Don't use in use mode.NOTE:: */ window_list *window_list; } ; 窗口的ID:窗口的ID号从1-0xFE,为普通的窗口号,最大支持255个窗口, 0xFF为特殊的ROOT窗口。 EGui目前设计弹出式菜单,和弹出窗口为普通窗口,其他的widget不算窗口。没有窗口ID。 window_list *window_list;这个结构在kernel里使用,应用部分不可访问。 3.1.3. struct _EGui_Event { short x; /* cursor x */当前光标的位置 short y; /* cursor y */ int type; /* event type */ int code; /* key code */ int pid; } ; Type:有以下几类。新的代码已经有些改变了。 /* EGui Event type */ #define PRESS_KEY 1 #define CLICK_LEFT 2 #define CLICK_RIGHT 3 #define CLICK_MIDDLE 4 #define REL_LEFT 5 #define REL_RIGHT 6 #define REL_MIDDLE 7 #define RELEASE_KEY 8 /* These are events about window, * they aren't input device events. */ #define FOCUS_CHANGED 9 /* window's focus change */ #define W_MOVE 10 /* window's move event */ #define W_SIZE_CHANGED 11 /* window's size change */ #define CURSOR_MOVE 12 /* only cursor move */ #define HIDE_CURSOR 13 /* cursor hide */ #define REDRAW_RECTANGLE 14 3.1.4. struct _window_list { struct list_head *next, *prev; /* save data for window */ EGui_Window *window; /* window */ unsigned char *width_in; unsigned char *height_in; int pid; /* program id */ short id; /* window id */ wait_queue_head_t event_wait; /* Zzzzz ... */ int done; }; 这是个列表,用来将window连接在一起,排在链头的是焦点窗口,依次往后排列。 这样ROOT窗口应该排在链尾。 unsigned char *width_in; unsigned char *height_in; 用来记录窗口的位置,width_in,height_in字符长度为屏幕长度,窗口所占用部分为1,其他为用: 例如屏幕位1024x768,窗口开始点位 20,32.宽为100,高为50那么, Width_in 长度为1024,从width_in[20]-----width_in[120],为1,其他部分为0。 Height_in 长度为768,从height_in[32]-----height_in[82]为1,其他部分为0。 event_wait:每个窗口都有此数据结构,当没有事件产生时,窗口处于等待状态。 为了配合timer使用,此等待达到10毫秒就会返回。 3.2. 窗口的实现 目前窗口有create,draw,close,move功能。 3.2.1. 窗口的create 首要函数在src-gui/egui/window.c Egui_CreateWindow ( EGui_FBinfo *fbinfo,short x,short y, short width,short height, Ecolor * bgcolor,char window_type) 此函数主要做以下几件事情: 申请EGui_Window数据空间,设置pid,位置,背景色,类型等。 通过ioctl 通知kernel建立window_list. Kernel部分需要工作: 代码在src-gui/driver/kegui.c new_window ( void __user * argp) 通过map表申请id,添加新的window_list, 主要为了在kernel里管理窗口。 3.2.2. 窗口的draw 首要函数在src-gui/egui/window.c Egui_drawwindow ( EGui_Window *ewindow) 主要绘画窗口的标题栏,标题,和边框。 3.2.3. 窗口的close 在kernel里从map表里释放ID,释放window_list表。 3.2.4. 窗口的查找 窗口的查找主要是光标对应的窗口,从而分发光标事件。 所以src-gui/driver提供了窗口查找函数。 为了方便,可以通过pid,id查找窗口。 3.3. 事件的实现 图形窗口主要为了方便人机交互,所以事件的管理是图形系统的一个关键部分。 键盘输入的管理很简单,一般都是将按键交给焦点窗口,但是鼠标事件就非常复杂。 需要查找光标所对应的窗口。 3.3.1. EGui事件的获取 Kernel部分: EGui事件在kernel部分获取键盘,鼠标等input事件。 利用kernel的input机制,注册event_handler函数,这样就可以获取所有通过input注册的输入事件。这一点不需要像其他的图形系统一样繁琐的去猜测到底系统是什么鼠标,或者什么键盘。 在src-gui/driver/event.c里有下面的函数。 egui_event (unsigned int type,unsigned int code, int value) 当系统有任何输入就会调用这个函数,我将这个事件通过光标所在窗口和焦点所在窗口方式将事件打包存放到事件对列里,当前列表里存放了30个循环覆盖事件。(当前版本代码有点bug,新版本已经修改。) Libegui部分: 这部分代码是给应用程序使用的,当应用程序所有的初始化工作完成以后,就会进入一个loop程序里。 代码如下: while (1) { /* loop get event */ get_egui_event (); Egui_timer_handler(); } 这里有2个函数,分别是获取事件和timer轮训事件。 get_egui_event (); 在event.c里实现,通过ioctl获取kernel事件,如果超过10毫秒没有得到事件就返回。 Egui_timer_handler(); 如果有timer事件就调用timer事件,如果没有timer事件直接返回。 具体关于timer事件回调我们将专门的timer论题详述。 细说get_egui_event(); 当它通过ioctl获取的事件应该是kernel已经计算好的为该进程的窗口事件,此文档对应的版本,还没有在一个进程实现多窗口,原因就是libegui里没有再将事件对应的窗口查找出来。因为应用没有作多窗口链表。这个以后肯定会实现。 目前实现的是将event给唯一的窗口,libegui里的event.c负责事件处理,将鼠标和window事件操作传给window.c,其他的传给widget。 Libwidget的事件处理,他同kernel机制一样,要分发事件给具体的widget. 3.3.2. 事件处理类型 Libegui部分: CLICK_LEFT:这个部分是判断一个单击左键产生后是否在window的标题栏内,如果是则可以移动窗口。 W_MOVE:(后来版本已经改变) 移动窗口 HIDE_CURSOR: 这是Egui的一个特殊实现,当光标从A窗口,移动到B窗口时,先是进程(A窗口所在的进程)A1移动鼠标,后是进程B1移动鼠标,但是进程A1并不知道光标已经在进程B1了。所以这时同时出现2个鼠标。这时kernel发送一个通知进程A1关闭鼠标的事件。这个实现以后会改掉。 Libwidget事件: PRESS_KEY:键盘输入事件,目前传给edit控件。 其他事件,单击,释放,移动鼠标等,都对应不同的控件的不同操作,这部分在widget章节详细描述。 4. Widget的实现 Egui本身内置了widget实现,实现widget更多的是为了验证窗口管理,拖动,事件管理的可行性。 主要是widget框架的搭建和实例的应用。 4.1. widget框架 widget_main.c 主要将申请的新控件放入链表,采用树状结构 图中A为window下连接的第一个widget, A1,A2,A3为他的子widget,A1,A2,A3之间的关系为兄弟关系。 A11,A12位A1的子widget。A11,A12位兄弟关系。 这个框架应该不同多解释。 那么父与子是什么关系呢?子widget要在父widget的区域内。 目前EGui的实现兄弟之间不交接。 除了链表外,widget_main.c还要查找event所属与的widget。 Widget.c的作用:抽象层,大部分widget的操作,例如: Draw,show,事件处理等都通过抽象层完成。 Widget目前支持click_left, press_key, rel_left, draw_in, draw_out等事件和事件的回调。 控件的事件回调,应用最多的是button,一般我们都要处理鼠标点击button后的动作。 这时候会调用用户自己写的事件处理。 4.2. 控件实例 目前实现的控件不多,button,edit,pixmap,window,form. 这里面其他的我不再细说,主要讲window。为了不混淆概念,我用“w_win”,代表window的widget。“window”代表EGui_Window,就是真正的带有ID的window。 为什么再制作一个和window一样的名字的widget呢? 主要是为了在全部的window大小范围内实现widget可以控制的区域。 大家知道,EGui的widget里子widget只能在父widget范围内,所以关闭按钮,window图标就不好用widget去实现,所以我就做了一个w_win这个控件,他就是widget链表图中的A控件。 5. Timer和其他实现 5.1. timer 这部分是我同事帮我做的,这部分实现和gtk非常像,因为我们不想用pthread去实现,所以要和event一起去通过loop来实现。因为event需要等待至少10毫秒才返回,并且,如果出现需要用很多时间来处理的event,就会造成timer被延时。所以一般来说这个timer的实现不要用来做精确定时。 虽然他的参数种,最小的单位是1毫秒,在实际应用中可能会是10毫秒,所以我建议大家使用这个timer时,用10毫秒的整数倍数去实现。 5.2. 其他实现 这是给这个文档留的一个后门吧,原本的意思是写一些我认为EGui里实现的比较好的闪光点拿出来给大家讲讲。 这部分文档以后再陆续的去补充吧。 6. 总结 在5.1期间我将这个文档赶制了出来,没有时间去检查,没有时间去细化。加上我的文笔不好大家先看着,有什么问题大家提出来,我再修改。 谢谢大家收看,休息时间马上回来,祝大家节日快乐。
|