X导航(一些笔记)
这些笔记来自O'Reilly出版的《The Definitive Guides to the X Window Systems, Vol 1 - Xlib Programming Manual》, 1992年第三版 (By Laxxuss)
Xserver的职责
-
允许多个客户端访问显示资源(display = screen + mouse + keyboard)
-
解释来自客户端的网络消息
-
把用户的输入通过网络消息传给客户端
-
二维绘制
-
维护一些数据结构 (windows, cursors, fonts, 图形相关上下文Graphics Context——客户端通过资源ID来共享前述资源)
一般客户端的职责
响应相应的事件(输入事件、与其他程序的交互事件)
特殊客户端——窗口管理器的职责
管理有限的UI资源(显示(display)、屏幕空间、颜色表)
X协议分类
-
请求:xlib->server (e.g.绘制线、查询窗口大小、改变颜色表中的某个单元的颜色)。大多数Xlib例程产生请求(除了如Regions和资源管理)。
-
响应:server->xlib,响应相关请求。需要获得响应的xlib例程发出的请求被称为round−trip(往返) request(对性能造成影响)。
-
事件:server->xlib,在xlib中事件保存为队列。客户指定服务器送来事件的范围。
-
错误:server->xlib,通知客户端前一请求是无效,类似事件,但是由Xlib指定的句柄处理(默认是打印错误消息)。
Xlib的覆盖面
-
颜色管理:绘制用的颜色模式(解释)之间的切换
-
指针管理(Cursors):鼠标图样的改变
-
数据管理:数据和窗口或者数字联系的机制
-
显示(Display)的连接:(通过网络)程序连接/断开一个显示(Display)
-
获取显示(Display)和服务端相关信息:提供服务端的实现和其相连的显示(Display) 的信息
-
绘图:绘制/填充点、线、矩形、多边形和弧的函数
-
错误处理只有keyboard和mouse事件传播。事件掩码是每个客户端独有:一些挂接错误处理句柄的函数
-
事件处理:获取来自用户、其他程序、服务端的事件
-
扩展:获知服务端的扩展以及这些扩展的用法
-
字体:列出可用的字体、载入字体以及找出其相应的字符
-
几何:操作和转换几何相关的
-
图形上下文:设置对绘制请求的解释(绘制的方式,如填充还是勾画、线的形状...)
-
对主机访问的控制:控制来自网络其他机器对服务端的访问
-
图像:获取/显示/操纵屏幕图像
-
客户端之间的通信:
-
国际化:独立于语言的输入和文本渲染处理函数
-
键盘:改变键盘输入处理方式(包括键盘映射)
-
指针设备(鼠标):改变指针输入处理方式
-
区域(Regions)操作:对多边形区域(Region)作数学操作
-
资源管理:方便管理用户设置和命令行参数
-
屏幕保护:设置屏幕保护期间显示的内容
-
文本:渲染文本和相关文本渲染的几何信息(大小...)
-
用户设置:设置/获取键盘连击相关设置
-
窗口属性(Attribute):获取/设置窗口的当前特质(属性)
-
窗口生命周期:窗口创建和消亡的函数
-
窗口管理:在屏幕上操作窗口——改变大小、可见性、窗口栈
Xlib清空请求队列(到server端)的时机
-
调用需要立即响应的函数(名中含Query、Fetch、Get的例程)
-
调用读取某个事件的例程,而此时队列中没有匹配事件
-
调用XFlush、XSync ()
属性(property)
窗口的特性
窗口的层次
-
每个屏幕(screen)对应一个root window(撑满整个屏幕)
-
root window的直接子window成为顶级(toplevel)窗口,由窗口管理器所管理
-
toplevel窗口的子窗口可用作实现比如按钮,滚动条之类
-
子窗口可以部分或全部置于父窗口之外,但是子窗口只能向其和父窗口交集区域输出/获取输入
-
子窗口总是在父窗口的上面
窗口的映射
窗口必须被映射——通过 XmapWindow()(或其他相关例程:XMapSubwindows()),窗口符合以下几点才可见:只有keyboard和mouse事件传播。事件掩码是每个客户端独有
-
该窗口的所有祖先窗口被映射
-
不能被其他可见的兄弟窗口或者祖先的兄弟窗口覆盖(通过XCirculateSubwindows(), XConfigureWindow(), 和 XrestackWindows()调整栈中的顺序)
-
相应的请求缓冲被清空(到server)
-
顶级窗口的初次映射是个特例——因为需要窗口管理器“领养"之。更复杂的情形:客户必须等待一个“曝光”事件窗口才可见
图形上下文的三个方面
-
位掩码(plane mask)
-
剪切掩码(clip mask)
-
逻辑函数
可绘对象(drawable: window & pixmap)
三类错误事件(Error Event)
-
检查返回值来侦测错误和进行相应处理(修改请求的参数再试),比如检测XOpenDisplay()成功否
-
协议错误——编程失误引起,由XErrorHandler处理(例外:对于返回状态代码过程的则通过检测状态代码来处理),通过XSetErrorHandler ()添加一个自定义的句柄
-
致命系统错误——(X服务器崩溃、网络失败...),由XIOErrorHandler处理,通过XSetIOErrorHandler ()添加一个自定义的句柄
错误事件结构体 (错误发生时刻的相关上下文环境)
错误事件只发给XErrorHandler处理
int type;
Display *display; /* 发生错误的Display */
XID resourceid; /* 资源ID */
unsigned long serial; /* 失败请求的序列号 */
unsigned char error_code; /* 错误代码 */
unsigned char request_code; /* 失败请求的主操作码 */
unsigned char minor_code; /* 指示扩展,若此请求不使用扩展为0 */
其中error_code可由XGetErrorText()或XGetErrorDatabaseText()来解读
GUI设计中的三要素
-
窗口的层次关系
-
选择需要处理的事件
-
事件处理(主要是简单的键盘和鼠标事件)
编写一个X程序的四个步骤
-
连接到X服务端(即选择显示(display),因为一个X服务端对应一个显示(display))
-
客户端之间的通信(和窗口管理器协作)
-
处理曝光事件
-
处理改变窗口大小事件(改变给定窗口中子窗口及其他图形内容的尺寸和位置)
创建X程序的一般过程
-
向X server创建一个连接,失败退出
-
获取物理屏幕的信息,从而计算窗口大小
-
创建窗口
-
设置合适的属性(properties),从而和窗口管理器通信
-
选择关注的事件
-
载入字体,用来打印文字
-
创建一个图形上下文(GC),来控制绘图请求的行为
-
通过映射窗口来显示之
-
循环处理事件
-
响应曝光(expose)事件,调用相应例程来绘制图形和文字
-
获取配置更改通知(ConfigureNotify)事件,其新的大小信息在事件结构体中
-
响应其他事件
-
退出——释放相关资源,关闭显示并退出
-
其他需要的步骤:
-
从配置文件/命令行参数获取用户自定义信息
-
处理颜色
一个简单的X程序(模板)
//Xlib include files
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/Xatom.h>
#include <stdio.h>
//Bitmap data for icon
#include "bitmaps/icon_bitmap"
#define BITMAPDEPTH 1
//布尔值,window足够大么->否则显示一条合适的信息
#define TOO_SMALL 0
#define BIG_ENOUGH 1
//这两个参数是几乎所有Xlib例程都需要的,故申明为全局比较简单;如果有其他源文件,这两个变量需要申明为'extern'
Display *display; /* 关于服务端及其屏幕的信息的结构体,仅由XOpenDisplay ()填充 */
int screen_num;
static char *progname;
void load_font (XFontStruct **);
/* ... */
void main (int argc, char **argv)
{
Window win; /* Window ID,其值由XCreateWindow ()或者XCreateSimpleWindow ()例程返回 */
unsigned int width, height; /* Window size */
int x = 0, y = 0; /* Window position */
unsigned int border_width = 4; /* Border four pixels wide */
unsigned int display_width, display_height;
Pixmap icon_pixmap; /* 位图资源ID */
char *display_name = NULL; /* Server to connect to,为NULL则由环境变量DISPLAY指示,格式:host:server.screen */
Screen *screen_ptr;
progname = argv[0];
//连接到服务器
if ((display = XOpenDisplay(display_name)) == NULL) {
(void) fprintf(stderr, "%s: cannot connect to X server %sn",progname, XDisplayName (display_name));
exit( −1 );
}
screen_num = DefaultScreen (display); /* screen_num允许的值0 ~ ScreenCount (display) - 1 */
screen_ptr = DefaultScreenOfDisplay (display);
//获取窗口信息
unsigned int display_width = DisplayWidth (display, screen_num);
unsigned int display_height = DisplayHeight(display, screen_num);
//创建窗口
width = display_width/3, height = display_height/4;
win = XCreateSimpleWindow (display, RootWindow (display, screen_num),
x, y, width, height, border_width,
BlackPixel (display, screen_num) /* border pixel value */,
WhitePixel (display, screen_num) /* background pixel value */);
//窗口栏上的图标
#define icon_bitmap_width 20
#define icon_bitmap_height 20
static char icon_bitmap_bits[] = {
0x60, 0x00, 0x01, 0xb0, 0x00, 0x07, 0x0c, 0x03, 0x00, 0x04, 0x04, 0x00,
0xc2, 0x18, 0x00, 0x03, 0x30, 0x00, 0x01, 0x60, 0x00, 0xf1, 0xdf, 0x00,
0xc1, 0xf0, 0x01, 0x82, 0x01, 0x00, 0x02, 0x03, 0x00, 0x02, 0x0c, 0x00,
0x02, 0x38, 0x00, 0x04, 0x60, 0x00, 0x04, 0xe0, 0x00, 0x04, 0x38, 0x00,
0x84, 0x06, 0x00, 0x14, 0x14, 0x00, 0x0c, 0x34, 0x00, 0x00, 0x00, 0x00};
icon_pixmap = XCreateBitmapFromData (display, win, icon_bitmap_bits,
icon_bitmap_width, icon_bitmap_height);
//和窗口管理器通信
char *window_name = "Basic Window Program";
char *icon_name = "basicwin";
XWMHints *wm_hints;
XClassHint *class_hints;
XTextProperty windowName, iconName;
XSizeHints *size_hints; /* 结构体:告诉window管理器顶级窗口最佳大小及大小增量 */
if (!(size_hints = XAllocSizeHints ())) {
fprintf (stderr, "%s: failure allocating memory", progname);
exit (0);
}
if (!(wm_hints = XAllocWMHints ())) {
fprintf(stderr, "%s: failure allocating memory", progname);
exit(0);
}
if (!(class_hints = XAllocClassHint ())) {
fprintf(stderr, "%s: failure allocating memory", progname);
exit (0);
}
//优先级:user(USPosition | USSize) > 窗口管理器 > 应用程序
size_hints−>flags = PPosition | PSize | PMinSize;
size_hints−>min_width = 300;
size_hints−>min_height = 200;
//设置XTextProperty结构体
if (XStringListToTextProperty (&window_name, 1, &windowName) == 0) {
fprintf (stderr, "%s: structure allocation for windowName failed.n", progname);
exit (-1);
}
if (XStringListToTextProperty (&icon_name, 1, &iconName) == 0) {
fprintf (stderr, "%s: structure allocation for iconName failed.n", progname);
exit (-1);
}
//程序是否在窗口第一次映射时显示一个图标/或者是正常的显示
wm_hints−>initial_state = NormalState;
//程序需要键盘输入么
wm_hints−>input = True;
wm_hints−>icon_pixmap = icon_pixmap;
wm_hints−>flags = StateHint | IconPixmapHint | InputHint;
//以下提供给窗口管理器关于本程序的信息
class_hints−>res_name = progname;
class_hints−>res_class = "Basicwin";
XSetWMProperties(display, win, &windowName, &iconName,
argv, argc, size_hints, wm_hints, class_hints);
//选择需要接受的事件, StructureNotifyMask相当于选择了CirculateNotify、ConfigureNotify、DestroyNotify、GravityNotify、MapNotify、ReparentNotify和UnmapNotify
XSelectInput (display, win,
ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask);
//创建服务器资源(至此,已经创建了一个window和icon pixmap两个资源
GC gc; /* ID of graphics context */
XFontStruct *font_info; /* Structure containing font information */
/* load_font:载入一个字体资源 */
load_font (&font_info);
/* get_GC:创建一个图形上下完资源(用来绘制字体和图形) */
//映射窗口
XMapWindow(display, win);
//清空请求缓冲->如果需要周期性绘制则需要调用XFlush ()
//建立起事件处理循环
XEvent report; /* Structure for event information */
int window_size = BIG_ENOUGH;
while (1) {
XNextEvent (display, &report);
switch (report.type) {
case Expose:
//绘制窗口优化:如非连续的Expose事件中最后一个,则不处理之/是否确定最小绘制区
if (report.xexpose.count != 0) break;
if (window_size == TOO_SMALL) TooSmall(win, gc, font_info);
else {
place_text(win, gc, font_info, width, height);
place_graphics(win, gc, width, height);
}
break;
case ConfigureNotify:
//处理窗口大小改变事件
width = report.xconfigure.width;
height = report.xconfigure.height;
if ((width < size_hints−>min_width) || (height < size_hints−>min_height))
window_size = TOO_SMALL;
else
window_size = BIG_ENOUGH;
break;
case ButtonPress:
case KeyPress:
//离开程序
XUnloadFont(display, font_info−>fid);
XFreeGC(display, gc);
XCloseDisplay(display);
exit(1);
default:
break;
}
}
}
void load_font (XFontStruct **font_info)
{
char *fontname = "9x15";
if ((*font_info = XLoadQueryFont(display,fontname)) == NULL) {
(void) fprintf( stderr, "Basic: Cannot open 9x15 fontn");
exit( −1 );
}
}
/* ... */
Some Tips
Q & A
-
为什么GUI的基元是窗口?
为了提高屏幕的利用虑,把屏幕划分为窗口来使用,故而
-
窗口属于谁?
窗口是全局的,通过窗口的ID可以引用窗口,故窗口不是特定地属于某个客户端
-
事件如何被分发处理?
服务端:X发送相关事件给在窗口的事件掩码中有标记的客户端,对键盘和鼠标相关事件可能还要向其父窗口传递
客户端:通过某ID窗口的事件掩码选择关心的事件,从事件队列中获取事件分发到合适的代码中来处理
事件的结构体中的两个最主要成员:1)事件发生的窗口 2)事件类型
一些详细的规范(PDF下载)
原书 ( The Definitive Guides to the X Window Systems, Vol 1 - Xlib Programming Manual)第三版
Xlib手册 (X Version 11, Release 6.9/7.0)
ICCCM (Inter-Client Communication Conventions Manual) v2.1
X协议 (X Version 11, Release 6.7 DRAFT)
X会话管理库 (X Session Management Library) v1.0
X各个部分简介(PDF下载)
X的未来
XGL
The State of Linux Graphics (Jon Smirl)(PDF)
XGL (PDF)
EGL(位于窗口系统和OpenGL ES/OpenVG等的一层)
-
提供创建可供客户APIs绘制和共享的渲染表面(windows,pbuffers,pixmaps)的机制
-
为客户端API提供创建和管理图形上下文(Graphics contexts)的方法
-
提供同步客户端API和平台原生渲染API的方式
OpenGL ES
OpenVG
硬件加速矢量绘图接口(Flash、svg...)
新闻:OpenVG加速webkit渲染
开源实现:shivaVG
OpenVG vs cairo:
-
OpenVG不支持字体或者文本渲染
-
OpenVG可以成为cairo的一个后端
-
OpenVG更像硬件的抽象状态机(更底层)
OpenMAX
-
硬件加速的全面的流媒体编码器
-
程序的可移植性
开源实现:Bellagio(整合层)
OpenMAX vs gstreamer:
-
通过OpenMAX 的IL(整合)层可以被gstreamer整合
-
OpenMAX的AL(应用)层和gstreamer竞争。
glFX
运行时刻的特效框架。(特效由COLLADA FX格式所描述)
COLLADA
基于XML,3D内容交换规范
新的驱动
当前DRM的内存管理 (PDF)
新的内存管理模型
Gallium(镓)3D驱动模型 (odp演示文件)
新闻:mesa的采用Gallium驱动模型的cell平台驱动(The Mesa Cell driver is part of the Gallium3D architecture. Tungsten Graphics is leading the project. Two phases are planned. First, to implement the framework for parallel rasterization using the Cell SPEs, including texture mapping. Second, to implement a full-featured OpenGL driver with support for GLSL, etc. )
讨论:X真的优秀么?
X作太多工作(人机外设的管理(DISPLAY)、绘制及相应数据管理、事件分发)?
主要是现代的绘制工作已经太复杂了,X继续支持绘制工作和已有的OpenGL之流冗余,而且也做不好。
X并没有很好划分Client-Server?
将获取的事件发给客户端处理,有以下几个缺点(看上去有点像AJAX于传统动态网页技术的PK):
YY(无责任灌水)
X不受欢迎?
当前Linux下的GUI除了编写上的困难(包括跨环境),其他还有一些缺点:
思路:
交互系统由硬件——软件——用户构成
-
硬件(CPU等) | 用户 |
执行线(CPU的执行入口,i.e. CPU的核) | 鼠标指针数 |
进程 | 窗口(工作流程) |
当前进程 | 获得焦点的窗口 |
进程间的通信 | 窗口间操作 |
进程调度 | 任务栏(或者火狐之类的标签栏) |
进程抢占 | 焦点抢占 |
缓存机制 | 历史记录(最近文档) |
... | ... |